mirror of
https://github.com/quantum5/totp.git
synced 2025-04-24 05:31:59 -04:00
Decode secret as base32
This commit is contained in:
parent
6181e0c0bc
commit
5243c3aa8b
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -11,6 +11,7 @@
|
|||
"@otplib/core": "^12.0.1",
|
||||
"@otplib/plugin-crypto-js": "^12.0.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"base32-decode": "^1.0.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"classnames": "^2.5.1",
|
||||
"react": "^18.2.0",
|
||||
|
@ -1930,6 +1931,12 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/base32-decode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base32-decode/-/base32-decode-1.0.0.tgz",
|
||||
"integrity": "sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"@otplib/core": "^12.0.1",
|
||||
"@otplib/plugin-crypto-js": "^12.0.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"base32-decode": "^1.0.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"classnames": "^2.5.1",
|
||||
"react": "^18.2.0",
|
||||
|
|
20
src/App.tsx
20
src/App.tsx
|
@ -1,10 +1,12 @@
|
|||
import React, {useState} from 'react';
|
||||
import base32Decode from 'base32-decode';
|
||||
import {NumberInput, TextInput} from './Input';
|
||||
import OTPOutput, {HashAlgorithm} from './OTPOutput';
|
||||
import OTPOutput from './OTPOutput';
|
||||
import Select from './Select';
|
||||
import Collapsible from './Collapsible';
|
||||
import ActionLink from './ActionLink';
|
||||
import {type State, defaults, serializeState, deserializeState} from './state';
|
||||
import {HashAlgorithm} from './algorithms.tsx';
|
||||
|
||||
function parseState() {
|
||||
if (window.location.hash.startsWith('#!')) {
|
||||
|
@ -20,9 +22,17 @@ function App() {
|
|||
const {secret, step, digits, algorithm} = state;
|
||||
const [offset, setOffset] = React.useState(0);
|
||||
|
||||
const [validSecret, decoded] = React.useMemo(() => {
|
||||
try {
|
||||
return [true, base32Decode(secret.toUpperCase(), 'RFC4648')];
|
||||
} catch (e) {
|
||||
return [false, undefined];
|
||||
}
|
||||
}, [secret]);
|
||||
|
||||
const validStep = step > 0;
|
||||
const validDigits = digits > 0 && digits <= 10;
|
||||
const valid = validStep && validDigits && !!secret;
|
||||
const valid = validSecret && validStep && validDigits && !!secret;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!validStep) return;
|
||||
|
@ -74,7 +84,6 @@ function App() {
|
|||
|
||||
React.useEffect(() => {
|
||||
const value = serializeState(state);
|
||||
console.log(value);
|
||||
history.replaceState(null, '', window.location.pathname + window.location.search + (value && `#!${value}`));
|
||||
}, [state]);
|
||||
|
||||
|
@ -91,7 +100,8 @@ function App() {
|
|||
return (
|
||||
<div className="totp-app">
|
||||
<div className="totp-settings">
|
||||
<TextInput label="Secret key" value={secret} onChange={setSecret}/>
|
||||
<TextInput label="Secret key" value={secret} onChange={setSecret}
|
||||
error={!validSecret && 'Secret must be a valid base32-encoded string'}/>
|
||||
{advanced ?
|
||||
<ActionLink onClick={hideAdvanced}>Hide advanced options</ActionLink> :
|
||||
<ActionLink onClick={showAdvanced}>Show advanced options</ActionLink>}
|
||||
|
@ -108,7 +118,7 @@ function App() {
|
|||
<button type="button" className="btn btn-secondary" onClick={onReset}>Reset</button>
|
||||
</Collapsible>
|
||||
</div>
|
||||
{valid && <OTPOutput secret={secret} offset={offset} algorithm={algorithm} digits={digits}/>}
|
||||
{valid && <OTPOutput secret={decoded} offset={offset} algorithm={algorithm} digits={digits}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
import React from 'react';
|
||||
import {HashAlgorithms, HOTP, HOTPOptions} from '@otplib/core';
|
||||
import {HOTP, HOTPOptions} from '@otplib/core';
|
||||
import {createDigest} from '@otplib/plugin-crypto-js';
|
||||
import classNames from 'classnames';
|
||||
import CopyButton from './CopyButton.tsx';
|
||||
|
||||
export const ALGORITHMS = {
|
||||
sha1: HashAlgorithms.SHA1,
|
||||
sha256: HashAlgorithms.SHA256,
|
||||
sha512: HashAlgorithms.SHA512,
|
||||
};
|
||||
|
||||
export type HashAlgorithm = keyof typeof ALGORITHMS;
|
||||
import {ALGORITHMS, HashAlgorithm} from './algorithms.tsx';
|
||||
|
||||
function OTPCode({code, delta}: { code: string; delta: number }) {
|
||||
return <div className={classNames('totp-code', {
|
||||
|
|
9
src/algorithms.tsx
Normal file
9
src/algorithms.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import {HashAlgorithms} from '@otplib/core';
|
||||
|
||||
export const ALGORITHMS = {
|
||||
sha1: HashAlgorithms.SHA1,
|
||||
sha256: HashAlgorithms.SHA256,
|
||||
sha512: HashAlgorithms.SHA512,
|
||||
};
|
||||
|
||||
export type HashAlgorithm = keyof typeof ALGORITHMS;
|
|
@ -1,4 +1,5 @@
|
|||
import {ALGORITHMS, HashAlgorithm} from './OTPOutput';
|
||||
|
||||
import {ALGORITHMS, HashAlgorithm} from './algorithms.tsx';
|
||||
|
||||
export type State = {
|
||||
secret: string;
|
||||
|
|
Loading…
Reference in a new issue