totp/src/OTPOutput.tsx

47 lines
1.3 KiB
TypeScript
Raw Normal View History

2025-02-10 18:05:02 -05:00
import {useMemo} from 'react';
2025-02-10 13:09:14 -05:00
import {HOTP, HOTPOptions} from '@otplib/core';
2024-04-07 00:13:19 -04:00
import {createDigest} from '@otplib/plugin-crypto-js';
2024-04-07 01:04:16 -04:00
import classNames from 'classnames';
2024-04-07 03:03:48 -04:00
import CopyButton from './CopyButton.tsx';
2025-02-10 13:09:14 -05:00
import {ALGORITHMS, HashAlgorithm} from './algorithms.tsx';
2024-04-07 00:13:19 -04:00
2024-04-07 01:04:16 -04:00
function OTPCode({code, delta}: { code: string; delta: number }) {
return <div className={classNames('totp-code', {
'totp-older': delta < 0,
'totp-newer': delta > 0,
'totp-current': delta === 0,
'totp-far': Math.abs(delta) > 5,
2024-04-07 02:04:25 -04:00
'totp-near-first': delta === -5,
'totp-near-last': delta === 5,
2024-04-07 01:04:16 -04:00
})}>
2024-04-07 00:13:19 -04:00
{code}
2024-04-07 03:03:48 -04:00
<CopyButton text={code}/>
2024-04-07 00:13:19 -04:00
</div>;
}
export default function OTPOutput({secret, offset, algorithm, digits}: {
2025-02-10 18:05:02 -05:00
secret: ArrayBuffer;
2024-04-07 00:13:19 -04:00
offset: number;
algorithm: HashAlgorithm;
digits: number;
}) {
2025-02-10 13:34:32 -05:00
const otp = useMemo(() => new HOTP<HOTPOptions>({
2024-04-07 00:13:19 -04:00
createDigest,
digits,
algorithm: ALGORITHMS[algorithm],
}), [digits, algorithm]);
return <div className="totp-output">
{[...Array(21).keys()].map((i) => {
2024-04-07 01:04:16 -04:00
const delta = i - 10;
const current = offset + delta;
2025-02-10 18:05:02 -05:00
return <OTPCode key={current} code={otp.generate(
// they really wanted an ArrayBuffer or some similar binary type
// but misdeclared the type
secret as unknown as string,
current,
)} delta={delta}/>;
2024-04-07 00:13:19 -04:00
})}
</div>;
}