You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-06 12:02:40 +03:00
registration: add function to re-request email token (#2357)
This commit is contained in:
committed by
GitHub
parent
49dd76b91e
commit
923ff4b282
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
import { logger } from "../../src/logger";
|
||||
import { InteractiveAuth } from "../../src/interactive-auth";
|
||||
import { MatrixError } from "../../src/http-api";
|
||||
import { sleep } from "../../src/utils";
|
||||
import { randomString } from "../../src/randomstring";
|
||||
|
||||
// Trivial client object to test interactive auth
|
||||
// (we do not need TestClient here)
|
||||
@@ -172,4 +174,107 @@ describe("InteractiveAuth", function() {
|
||||
expect(error.message).toBe('No appropriate authentication flow found');
|
||||
});
|
||||
});
|
||||
|
||||
describe("requestEmailToken", () => {
|
||||
it("increases auth attempts", async () => {
|
||||
const doRequest = jest.fn();
|
||||
const stateUpdated = jest.fn();
|
||||
const requestEmailToken = jest.fn();
|
||||
requestEmailToken.mockImplementation(async () => ({ sid: "" }));
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
matrixClient: new FakeClient(),
|
||||
doRequest, stateUpdated, requestEmailToken,
|
||||
});
|
||||
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 1, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 2, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 3, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 4, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 5, undefined);
|
||||
});
|
||||
|
||||
it("increases auth attempts", async () => {
|
||||
const doRequest = jest.fn();
|
||||
const stateUpdated = jest.fn();
|
||||
const requestEmailToken = jest.fn();
|
||||
requestEmailToken.mockImplementation(async () => ({ sid: "" }));
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
matrixClient: new FakeClient(),
|
||||
doRequest, stateUpdated, requestEmailToken,
|
||||
});
|
||||
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 1, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 2, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 3, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 4, undefined);
|
||||
requestEmailToken.mockClear();
|
||||
await ia.requestEmailToken();
|
||||
expect(requestEmailToken).toHaveBeenLastCalledWith(undefined, ia.getClientSecret(), 5, undefined);
|
||||
});
|
||||
|
||||
it("passes errors through", async () => {
|
||||
const doRequest = jest.fn();
|
||||
const stateUpdated = jest.fn();
|
||||
const requestEmailToken = jest.fn();
|
||||
requestEmailToken.mockImplementation(async () => {
|
||||
throw new Error("unspecific network error");
|
||||
});
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
matrixClient: new FakeClient(),
|
||||
doRequest, stateUpdated, requestEmailToken,
|
||||
});
|
||||
|
||||
expect(async () => await ia.requestEmailToken()).rejects.toThrowError("unspecific network error");
|
||||
});
|
||||
|
||||
it("only starts one request at a time", async () => {
|
||||
const doRequest = jest.fn();
|
||||
const stateUpdated = jest.fn();
|
||||
const requestEmailToken = jest.fn();
|
||||
requestEmailToken.mockImplementation(() => sleep(500, { sid: "" }));
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
matrixClient: new FakeClient(),
|
||||
doRequest, stateUpdated, requestEmailToken,
|
||||
});
|
||||
|
||||
await Promise.all([ia.requestEmailToken(), ia.requestEmailToken(), ia.requestEmailToken()]);
|
||||
expect(requestEmailToken).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("stores result in email sid", async () => {
|
||||
const doRequest = jest.fn();
|
||||
const stateUpdated = jest.fn();
|
||||
const requestEmailToken = jest.fn();
|
||||
const sid = randomString(24);
|
||||
requestEmailToken.mockImplementation(() => sleep(500, { sid }));
|
||||
|
||||
const ia = new InteractiveAuth({
|
||||
matrixClient: new FakeClient(),
|
||||
doRequest, stateUpdated, requestEmailToken,
|
||||
});
|
||||
|
||||
await ia.requestEmailToken();
|
||||
expect(ia.getEmailSid()).toEqual(sid);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -203,6 +203,8 @@ export class InteractiveAuth {
|
||||
private chosenFlow: IFlow = null;
|
||||
private currentStage: string = null;
|
||||
|
||||
private emailAttempt = 1;
|
||||
|
||||
// if we are currently trying to submit an auth dict (which includes polling)
|
||||
// the promise the will resolve/reject when it completes
|
||||
private submitPromise: Promise<void> = null;
|
||||
@@ -408,6 +410,34 @@ export class InteractiveAuth {
|
||||
this.emailSid = sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a new email token and sets the email sid for the validation session
|
||||
*/
|
||||
public requestEmailToken = async () => {
|
||||
if (!this.requestingEmailToken) {
|
||||
logger.trace("Requesting email token. Attempt: " + this.emailAttempt);
|
||||
// If we've picked a flow with email auth, we send the email
|
||||
// now because we want the request to fail as soon as possible
|
||||
// if the email address is not valid (ie. already taken or not
|
||||
// registered, depending on what the operation is).
|
||||
this.requestingEmailToken = true;
|
||||
try {
|
||||
const requestTokenResult = await this.requestEmailTokenCallback(
|
||||
this.inputs.emailAddress,
|
||||
this.clientSecret,
|
||||
this.emailAttempt++,
|
||||
this.data.session,
|
||||
);
|
||||
this.emailSid = requestTokenResult.sid;
|
||||
logger.trace("Email token request succeeded");
|
||||
} finally {
|
||||
this.requestingEmailToken = false;
|
||||
}
|
||||
} else {
|
||||
logger.warn("Could not request email token: Already requesting");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire off a request, and either resolve the promise, or call
|
||||
* startAuthStage.
|
||||
@@ -458,24 +488,9 @@ export class InteractiveAuth {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.emailSid &&
|
||||
!this.requestingEmailToken &&
|
||||
this.chosenFlow.stages.includes(AuthType.Email)
|
||||
) {
|
||||
// If we've picked a flow with email auth, we send the email
|
||||
// now because we want the request to fail as soon as possible
|
||||
// if the email address is not valid (ie. already taken or not
|
||||
// registered, depending on what the operation is).
|
||||
this.requestingEmailToken = true;
|
||||
if (!this.emailSid && this.chosenFlow.stages.includes(AuthType.Email)) {
|
||||
try {
|
||||
const requestTokenResult = await this.requestEmailTokenCallback(
|
||||
this.inputs.emailAddress,
|
||||
this.clientSecret,
|
||||
1, // TODO: Multiple send attempts?
|
||||
this.data.session,
|
||||
);
|
||||
this.emailSid = requestTokenResult.sid;
|
||||
await this.requestEmailToken();
|
||||
// NB. promise is not resolved here - at some point, doRequest
|
||||
// will be called again and if the user has jumped through all
|
||||
// the hoops correctly, auth will be complete and the request
|
||||
@@ -491,8 +506,6 @@ export class InteractiveAuth {
|
||||
// send the email, for whatever reason.
|
||||
this.attemptAuthDeferred.reject(e);
|
||||
this.attemptAuthDeferred = null;
|
||||
} finally {
|
||||
this.requestingEmailToken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user