2024-04-07 00:13:19 -04:00
|
|
|
import React from 'react';
|
|
|
|
import {NumberInput, TextInput} from './Input';
|
|
|
|
import OTPOutput, {HashAlgorithm} from './OTPOutput';
|
|
|
|
import Select from './Select';
|
2024-04-06 22:54:44 -04:00
|
|
|
|
|
|
|
function App() {
|
2024-04-07 00:13:19 -04:00
|
|
|
const [secret, setSecret] = React.useState('');
|
|
|
|
const [step, setStep] = React.useState(30);
|
|
|
|
const [offset, setOffset] = React.useState(0);
|
|
|
|
const [digits, setDigits] = React.useState(6);
|
|
|
|
const [algorithm, setAlgorithm] = React.useState<HashAlgorithm>('sha1');
|
|
|
|
|
|
|
|
const validStep = step > 0;
|
|
|
|
const validDigits = digits > 0 && digits < 10;
|
|
|
|
const valid = validStep && validDigits;
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (!validStep) return;
|
|
|
|
const now = Date.now();
|
|
|
|
setOffset(Math.floor(now / (1000 * step)));
|
|
|
|
}, [validStep, step]);
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
if (!validStep) return;
|
|
|
|
const now = Date.now();
|
|
|
|
const nextOffset = Math.floor(now / (1000 * step)) + 1;
|
|
|
|
const nextUpdate = nextOffset * step * 1000;
|
|
|
|
const timer = setTimeout(() => setOffset(nextOffset), nextUpdate - now);
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
}, [validStep, offset, step]);
|
|
|
|
|
|
|
|
const onReset = React.useCallback(() => {
|
|
|
|
setStep(30);
|
|
|
|
setDigits(6);
|
|
|
|
setAlgorithm('sha1');
|
|
|
|
}, []);
|
2024-04-06 22:54:44 -04:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="totp-app">
|
|
|
|
<div className="totp-settings">
|
2024-04-07 00:13:19 -04:00
|
|
|
<TextInput label="Secret key" value={secret} onChange={setSecret}/>
|
|
|
|
<NumberInput label="Time step" value={step} onChange={setStep} min={1}
|
|
|
|
error={!validStep && 'You must enter an integer time step ≥ 1 second'}/>
|
|
|
|
<NumberInput label="Code digits" value={digits} onChange={setDigits} min={1} max={10}
|
|
|
|
error={!validDigits && 'You must enter an integer digit count between 1 and 10'}/>
|
|
|
|
<Select label="Algorithm" value={algorithm} onChange={setAlgorithm} options={{
|
|
|
|
sha1: 'SHA-1',
|
|
|
|
sha256: 'SHA-256',
|
|
|
|
sha512: 'SHA-512',
|
|
|
|
}}/>
|
|
|
|
<button type="button" className="btn btn-secondary" onClick={onReset}>Reset</button>
|
2024-04-06 22:54:44 -04:00
|
|
|
</div>
|
2024-04-07 00:13:19 -04:00
|
|
|
{valid && <OTPOutput secret={secret} offset={offset} algorithm={algorithm} digits={digits}/>}
|
2024-04-06 22:54:44 -04:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default App;
|