Merge branch 'main' into migrate_files_db_to_sqlite_async
This commit is contained in:
commit
ed830dc387
2 changed files with 36 additions and 15 deletions
|
@ -233,7 +233,7 @@ const OTPDisplay: React.FC<OTPDisplayProps> = ({ code, otp, nextOTP }) => {
|
|||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<TimerProgress period={code.period} />
|
||||
<CodeValidityBar code={code} />
|
||||
<div
|
||||
style={{
|
||||
padding: "12px 20px 0px 20px",
|
||||
|
@ -329,24 +329,25 @@ const OTPDisplay: React.FC<OTPDisplayProps> = ({ code, otp, nextOTP }) => {
|
|||
);
|
||||
};
|
||||
|
||||
interface TimerProgressProps {
|
||||
period: number;
|
||||
interface CodeValidityBarProps {
|
||||
code: Code;
|
||||
}
|
||||
|
||||
const TimerProgress: React.FC<TimerProgressProps> = ({ period }) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const us = period * 1e6;
|
||||
const CodeValidityBar: React.FC<CodeValidityBarProps> = ({ code }) => {
|
||||
const [progress, setProgress] = useState(code.type == "hotp" ? 1 : 0);
|
||||
|
||||
useEffect(() => {
|
||||
const advance = () => {
|
||||
const us = code.period * 1e6;
|
||||
const timeRemaining = us - ((Date.now() * 1000) % us);
|
||||
setProgress(timeRemaining / us);
|
||||
};
|
||||
|
||||
const ticker = setInterval(advance, 10);
|
||||
const ticker =
|
||||
code.type == "hotp" ? undefined : setInterval(advance, 10);
|
||||
|
||||
return () => clearInterval(ticker);
|
||||
}, []);
|
||||
return () => ticker && clearInterval(ticker);
|
||||
}, [code]);
|
||||
|
||||
const color = progress > 0.4 ? "green" : "orange";
|
||||
|
||||
|
|
|
@ -29,6 +29,15 @@ export interface Code {
|
|||
* code remains valid.
|
||||
*/
|
||||
period: number;
|
||||
/** The (HMAC) algorithm used by the OTP generator. */
|
||||
algorithm: "sha1" | "sha256" | "sha512";
|
||||
/**
|
||||
* HOTP counter.
|
||||
*
|
||||
* Only valid for HOTP codes. It might be even missing for HOTP codes, in
|
||||
* which case we should start from 0.
|
||||
*/
|
||||
counter?: number;
|
||||
/**
|
||||
* The secret that is used to drive the OTP generator.
|
||||
*
|
||||
|
@ -36,8 +45,6 @@ export interface Code {
|
|||
* {@link type}-specific manner).
|
||||
*/
|
||||
secret: string;
|
||||
/** The (HMAC) algorithm used by the OTP generator. */
|
||||
algorithm: "sha1" | "sha256" | "sha512";
|
||||
/** The original string from which this code was generated. */
|
||||
uriString: string;
|
||||
}
|
||||
|
@ -53,6 +60,12 @@ export interface Code {
|
|||
* - (TOTP)
|
||||
* otpauth://totp/ACME:user@example.org?algorithm=SHA1&digits=6&issuer=acme&period=30&secret=ALPHANUM
|
||||
*
|
||||
* - (HOTP)
|
||||
* otpauth://hotp/Test?secret=AAABBBCCCDDDEEEFFF&issuer=Test&counter=0
|
||||
*
|
||||
* - (Steam)
|
||||
* otpauth://steam/Steam:SteamAccount?algorithm=SHA1&digits=5&issuer=Steam&period=30&secret=AAABBBCCCDDDEEEFFF
|
||||
*
|
||||
* See also `auth/test/models/code_test.dart`.
|
||||
*/
|
||||
export const codeFromURIString = (id: string, uriString: string): Code => {
|
||||
|
@ -94,8 +107,9 @@ const _codeFromURIString = (id: string, uriString: string): Code => {
|
|||
issuer: parseIssuer(url, path),
|
||||
length: parseLength(url, type),
|
||||
period: parsePeriod(url),
|
||||
secret: parseSecret(url),
|
||||
algorithm: parseAlgorithm(url),
|
||||
counter: parseCounter(url),
|
||||
secret: parseSecret(url),
|
||||
uriString,
|
||||
};
|
||||
};
|
||||
|
@ -164,6 +178,11 @@ const parseAlgorithm = (url: URL): Code["algorithm"] => {
|
|||
}
|
||||
};
|
||||
|
||||
const parseCounter = (url: URL): number | undefined => {
|
||||
const c = url.searchParams.get("counter");
|
||||
return c ? parseInt(c, 10) : undefined;
|
||||
};
|
||||
|
||||
const parseSecret = (url: URL): string =>
|
||||
ensure(url.searchParams.get("secret")).replaceAll(" ", "").toUpperCase();
|
||||
|
||||
|
@ -194,13 +213,14 @@ export const generateOTPs = (code: Code): [otp: string, nextOTP: string] => {
|
|||
}
|
||||
|
||||
case "hotp": {
|
||||
const counter = code.counter || 0;
|
||||
const hotp = new HOTP({
|
||||
secret: code.secret,
|
||||
counter: 0,
|
||||
counter: counter,
|
||||
algorithm: code.algorithm,
|
||||
});
|
||||
otp = hotp.generate();
|
||||
nextOTP = hotp.generate({ counter: 1 });
|
||||
otp = hotp.generate({ counter });
|
||||
nextOTP = hotp.generate({ counter: counter + 1 });
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue