一応未来のトークンも表示できるようにしたので30秒縛りがなくなります 😀
2要素認証のコードはこんな感じ。secret はサービス側で QR コードを表示された時に、表示するオプションがあるのでそれを押すと base32 テキストが出てきます。
```
// https://www.rfc-editor.org/rfc/rfc4226#page-6
const hotp = (secret: BinaryLike, count: number, digits: number = 6) => {
const c = Buffer.alloc(8);
c.writeBigInt64BE(BigInt(count));
const hs = createHmac('sha1', secret).update(c).digest();
const s = (hs.readUInt32BE(hs[19] & 0x0f) & 0x7fffffff);
return (s % (10 ** digits)).toString().padStart(digits, '0');
};
// https://www.rfc-editor.org/rfc/rfc6238#page-4
const totp = (secret: BinaryLike, time: number, step: number = 30, digits: number = 6, t0: number = 0) => {
const count = Math.floor((time - t0) / step);
return hotp(secret, count, digits);
};
// https://github.com/google/google-authenticator/wiki/Key-Uri-Format#secret
const twoFA = (secret: string, length: number = 1) => {
const sec = base32.decode(secret);
const now = Math.floor(Date.now() / 1000);
return { totp: Array.from({ length }, (_, i) => totp(sec, now + 30 * i, 30, 6)), time: 30 - (now % 30) };
}
```
Discussion
とにかく secret を安全な場所に保存しておけばアプリが消えても大丈夫です。