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 { logger } from "../../src/logger";
|
||||||
import { InteractiveAuth } from "../../src/interactive-auth";
|
import { InteractiveAuth } from "../../src/interactive-auth";
|
||||||
import { MatrixError } from "../../src/http-api";
|
import { MatrixError } from "../../src/http-api";
|
||||||
|
import { sleep } from "../../src/utils";
|
||||||
|
import { randomString } from "../../src/randomstring";
|
||||||
|
|
||||||
// Trivial client object to test interactive auth
|
// Trivial client object to test interactive auth
|
||||||
// (we do not need TestClient here)
|
// (we do not need TestClient here)
|
||||||
@@ -172,4 +174,107 @@ describe("InteractiveAuth", function() {
|
|||||||
expect(error.message).toBe('No appropriate authentication flow found');
|
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 chosenFlow: IFlow = null;
|
||||||
private currentStage: string = null;
|
private currentStage: string = null;
|
||||||
|
|
||||||
|
private emailAttempt = 1;
|
||||||
|
|
||||||
// if we are currently trying to submit an auth dict (which includes polling)
|
// if we are currently trying to submit an auth dict (which includes polling)
|
||||||
// the promise the will resolve/reject when it completes
|
// the promise the will resolve/reject when it completes
|
||||||
private submitPromise: Promise<void> = null;
|
private submitPromise: Promise<void> = null;
|
||||||
@@ -408,6 +410,34 @@ export class InteractiveAuth {
|
|||||||
this.emailSid = sid;
|
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
|
* Fire off a request, and either resolve the promise, or call
|
||||||
* startAuthStage.
|
* startAuthStage.
|
||||||
@@ -458,24 +488,9 @@ export class InteractiveAuth {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!this.emailSid && this.chosenFlow.stages.includes(AuthType.Email)) {
|
||||||
!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;
|
|
||||||
try {
|
try {
|
||||||
const requestTokenResult = await this.requestEmailTokenCallback(
|
await this.requestEmailToken();
|
||||||
this.inputs.emailAddress,
|
|
||||||
this.clientSecret,
|
|
||||||
1, // TODO: Multiple send attempts?
|
|
||||||
this.data.session,
|
|
||||||
);
|
|
||||||
this.emailSid = requestTokenResult.sid;
|
|
||||||
// NB. promise is not resolved here - at some point, doRequest
|
// NB. promise is not resolved here - at some point, doRequest
|
||||||
// will be called again and if the user has jumped through all
|
// will be called again and if the user has jumped through all
|
||||||
// the hoops correctly, auth will be complete and the request
|
// the hoops correctly, auth will be complete and the request
|
||||||
@@ -491,8 +506,6 @@ export class InteractiveAuth {
|
|||||||
// send the email, for whatever reason.
|
// send the email, for whatever reason.
|
||||||
this.attemptAuthDeferred.reject(e);
|
this.attemptAuthDeferred.reject(e);
|
||||||
this.attemptAuthDeferred = null;
|
this.attemptAuthDeferred = null;
|
||||||
} finally {
|
|
||||||
this.requestingEmailToken = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user