mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
refactor(core): add time window validation tolerance for otp authenticator (#4684)
This commit is contained in:
parent
248448d13a
commit
8c0b55ab06
2 changed files with 20 additions and 23 deletions
|
@ -1,6 +1,17 @@
|
||||||
import { authenticator } from 'otplib';
|
import { authenticator } from 'otplib';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note:
|
||||||
|
* Considering T1 and T2 two consecutive time steps,
|
||||||
|
* any token generated within T1 but checked with T2 could be considered valid according to [RFC 6238 5.2](https://datatracker.ietf.org/doc/html/rfc6238#section-5.2).
|
||||||
|
*
|
||||||
|
* FYI: https://github.com/yeojz/otplib/issues/697#issuecomment-1655749578
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||||
|
authenticator.options = { window: 1 };
|
||||||
|
|
||||||
export const generateTotpSecret = () => authenticator.generateSecret();
|
export const generateTotpSecret = () => authenticator.generateSecret();
|
||||||
|
|
||||||
export const validateTotpToken = (secret: string, token: string) =>
|
export const validateTotpToken = (secret: string, token: string) => {
|
||||||
authenticator.check(token, secret);
|
return authenticator.check(token, secret);
|
||||||
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { authenticator } from 'otplib';
|
import { authenticator } from 'otplib';
|
||||||
|
|
||||||
import { demoAppUrl } from '#src/constants.js';
|
|
||||||
import { waitFor, dcls } from '#src/utils.js';
|
import { waitFor, dcls } from '#src/utils.js';
|
||||||
|
|
||||||
import ExpectExperience from './expect-experience.js';
|
import ExpectExperience from './expect-experience.js';
|
||||||
|
@ -35,10 +34,7 @@ export default class ExpectTotpExperience extends ExpectExperience {
|
||||||
|
|
||||||
const code = authenticator.generate(secret);
|
const code = authenticator.generate(secret);
|
||||||
|
|
||||||
for (const [index, char] of code.split('').entries()) {
|
await this.fillTotpCode(code);
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await this.toFillInput(`totpCode_${index}`, char);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the form to commit automatically
|
// Wait for the form to commit automatically
|
||||||
await waitFor(500);
|
await waitFor(500);
|
||||||
|
@ -64,10 +60,7 @@ export default class ExpectTotpExperience extends ExpectExperience {
|
||||||
|
|
||||||
const code = authenticator.generate(secret);
|
const code = authenticator.generate(secret);
|
||||||
|
|
||||||
for (const [index, char] of code.split('').entries()) {
|
await this.fillTotpCode(code);
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await this.toFillInput(`totpCode_${index}`, char);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the form to commit automatically
|
// Wait for the form to commit automatically
|
||||||
await waitFor(500);
|
await waitFor(500);
|
||||||
|
@ -76,17 +69,10 @@ export default class ExpectTotpExperience extends ExpectExperience {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async fillTotpCode(code: string) {
|
||||||
* Assert the page is at the demo app page and get the user ID from the page.
|
for (const [index, char] of code.split('').entries()) {
|
||||||
* @returns The user ID.
|
// eslint-disable-next-line no-await-in-loop
|
||||||
*/
|
await this.toFillInput(`totpCode_${index}`, char);
|
||||||
async getUserIdFromDemoAppPage() {
|
}
|
||||||
this.toMatchUrl(demoAppUrl);
|
|
||||||
const userIdDiv = await expect(this.page).toMatchElement([dcls('infoCard'), 'div'].join(' '), {
|
|
||||||
text: 'User ID: ',
|
|
||||||
});
|
|
||||||
const userIdSpan = await expect(userIdDiv).toMatchElement('span');
|
|
||||||
|
|
||||||
return (await userIdSpan.evaluate((element) => element.textContent)) ?? '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue