admin MFAuth refactoring & cosmetics

This commit is contained in:
Aaron 2019-02-22 10:14:07 -06:00
parent e3fd6aa398
commit 9071f07443
No known key found for this signature in database
GPG Key ID: 3B5B7597106F0A0E
2 changed files with 66 additions and 46 deletions

View File

@ -14,8 +14,7 @@
margin-bottom: 0.8rem;
}
&-codes,
&-qrcode {
&-codes {
margin: 1.5rem 0;
font-size: 1rem;
font-family: 'Courier New', Courier, monospace;
@ -48,13 +47,21 @@
& > div {
white-space: nowrap;
line-height: 2rem;
margin-right: 0.6rem;
margin-right: 0.5rem;
font-weight: bold;
}
}
&-controls {
display: flex;
margin-top: 1rem;
text-align: right;
justify-content: flex-end;
&-label {
white-space: nowrap;
line-height: 2rem;
font-weight: bold;
}
& > * + * {
margin-left: 0.5rem;
@ -63,6 +70,7 @@
ol {
margin-left: 1rem;
margin-bottom: 2rem;
& > li {
margin: 1rem 0;

View File

@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { Link, Redirect } from 'react-router-dom';
import { Link, Redirect, withRouter, RouteComponentProps } from 'react-router-dom';
import { view } from 'react-easy-state';
import CopyToClipboard from 'react-copy-to-clipboard';
import QRCode from 'qrcode.react';
@ -15,10 +15,12 @@ import store, {
import { downloadString } from 'src/util/file';
import './index.less';
interface Props {
interface OwnProps {
isReset?: boolean;
}
type Props = OwnProps & RouteComponentProps<any>;
const STATE = {
// remote
isLoginFresh: false,
@ -31,10 +33,10 @@ const STATE = {
isEmailVerified: false,
// local
loaded: false,
hasReadSetup: false,
stepOutlineComplete: false,
initializing: false,
hasSavedCodes: false,
hasVerified: false,
stepRecoveryCodesComplete: false,
stepTotpComplete: false,
password: '',
verifyCode: '',
isVerifying: false,
@ -50,10 +52,10 @@ class MFAuth extends React.Component<Props, State> {
render() {
const {
loaded,
hasReadSetup,
stepOutlineComplete,
password,
hasSavedCodes,
hasVerified,
stepRecoveryCodesComplete,
stepTotpComplete,
verifyCode,
isLoginFresh,
has2fa,
@ -86,7 +88,7 @@ class MFAuth extends React.Component<Props, State> {
has2fa &&
backupCodeCount < 5 && (
<Alert
type="warning"
type="error"
message={
<>
You only have <b>{backupCodeCount}</b> recovery codes remaining! Generate
@ -103,7 +105,6 @@ class MFAuth extends React.Component<Props, State> {
<h1>
{isReset ? 'Reset two-factor authentication' : 'Two-factor authentication'}
</h1>
{lowBackupCodesWarning}
{children}
</>
)}
@ -126,12 +127,14 @@ class MFAuth extends React.Component<Props, State> {
Too much time has elapsed since you last affirmed your credentials, please
enter your password below.
</p>
<Input.Password
onPressEnter={this.handleSubmitPassword}
onChange={e => this.setState({ password: e.target.value })}
value={password}
/>
<div className="MFAuth-controls">
<Input.Password
onPressEnter={this.handleSubmitPassword}
onChange={e => this.setState({ password: e.target.value })}
value={password}
autoFocus={true}
/>
<Button
type="primary"
onClick={this.handleSubmitPassword}
@ -145,10 +148,15 @@ class MFAuth extends React.Component<Props, State> {
}
// STEP 1 (outline)
if ((!has2fa || isReset) && !hasReadSetup) {
if ((!has2fa || isReset) && !stepOutlineComplete) {
return wrap(
<div>
{!has2fa && <Alert type="info" message="Administration requires 2fa setup." />}
{!has2fa && (
<Alert
type="info"
message={<>Administration requires two-factor authentication setup.</>}
/>
)}
{isReset && (
<Alert
type="warning"
@ -170,11 +178,7 @@ class MFAuth extends React.Component<Props, State> {
</li>
</ol>
<div className="MFAuth-controls">
{isReset && (
<Link to="/settings">
<Button>Cancel</Button>
</Link>
)}
{isReset && <Button onClick={this.handleCancel}>Cancel</Button>}
<Button onClick={this.handleReadSetup} type="primary">
I'm ready
</Button>
@ -184,7 +188,7 @@ class MFAuth extends React.Component<Props, State> {
}
// STEP 2 (recovery codes)
if ((!has2fa || isReset) && !hasSavedCodes) {
if ((!has2fa || isReset) && !stepRecoveryCodesComplete) {
return wrap(
((initializing || !backupCodes.length) && (
<Spin tip="Loading 2fa setup..." />
@ -228,7 +232,7 @@ class MFAuth extends React.Component<Props, State> {
<Button onClick={this.handleCancel}>Cancel</Button>
<Button
type="primary"
onClick={() => this.setState({ hasSavedCodes: true })}
onClick={() => this.setState({ stepRecoveryCodesComplete: true })}
>
Next
</Button>
@ -239,7 +243,7 @@ class MFAuth extends React.Component<Props, State> {
}
// STEP 4 (totp setup/verify)
if ((!has2fa || isReset) && !hasVerified) {
if ((!has2fa || isReset) && !stepTotpComplete) {
return wrap(
<div>
<h2>3. Set up Authenticator</h2>
@ -273,11 +277,13 @@ class MFAuth extends React.Component<Props, State> {
</div>
</Card>
<div className="MFAuth-verify">
<div>Enter code from application:</div>
<div>Enter code from application</div>
<Input
placeholder="123456"
value={verifyCode}
onChange={e => this.setState({ verifyCode: e.target.value })}
onPressEnter={this.handleEnable}
autoFocus={true}
/>
</div>
<div className="MFAuth-controls">
@ -288,7 +294,7 @@ class MFAuth extends React.Component<Props, State> {
disabled={verifyCode.length === 0}
loading={isVerifying}
>
{isReset ? 'Reset' : 'Enable'}
{isReset ? 'Save' : 'Enable'}
</Button>
</div>
</div>,
@ -299,21 +305,22 @@ class MFAuth extends React.Component<Props, State> {
if (has2fa && !is2faAuthed) {
return wrap(
<>
<h2>2FAuthentication required</h2>
{lowBackupCodesWarning}
<h2>Two-Factor authentication required</h2>
<p>
Enter the current code from your authenticator application. Enter a backup
Enter the current code from your authenticator application. Enter a recovery
code if you do not have access to your authenticator application.
</p>
<div className="MFAuth-verify">
<div>Enter code from application:</div>
<div className="MFAuth-verify" />
<div className="MFAuth-controls">
<div className="MFAuth-controls-label">Enter code from application</div>
<Input
placeholder="123456"
value={verifyCode}
onChange={e => this.setState({ verifyCode: e.target.value })}
onPressEnter={this.handleVerify}
autoFocus={true}
/>
</div>
<div className="MFAuth-controls">
<Button
type="primary"
onClick={this.handleVerify}
@ -342,15 +349,19 @@ class MFAuth extends React.Component<Props, State> {
};
private handleCancel = async () => {
this.setState({ ...STATE });
this.update2faStateFromServer();
const { isReset } = this.props;
if (isReset) {
message.info('Canceled two-factor reset');
this.props.history.replace('/settings');
} else {
this.setState({ ...STATE });
this.update2faStateFromServer();
}
};
private handleReadSetup = async () => {
if (this.state.isLoginFresh) {
this.setState({ hasReadSetup: true });
this.loadSetup();
}
this.setState({ stepOutlineComplete: true });
this.loadSetup();
};
private handleSubmitPassword = async () => {
@ -382,13 +393,13 @@ class MFAuth extends React.Component<Props, State> {
private handleEnable = async () => {
const { backupCodes, totpSecret, verifyCode } = this.state;
if (verifyCode.length === 0) return; // for pressEnter
this.setState({ isVerifying: true });
try {
await post2faEnable({ backupCodes, totpSecret, verifyCode });
message.success('Two-factor setup complete!');
store.checkLogin(); // should return authenticated status
this.setState({ hasVerified: true });
// await this.update2faStateFromServer();
this.setState({ stepTotpComplete: true });
} catch (e) {
handleApiError(e);
}
@ -397,6 +408,7 @@ class MFAuth extends React.Component<Props, State> {
private handleVerify = async () => {
const { verifyCode } = this.state;
if (verifyCode.length === 0) return; // for pressEnter
this.setState({ isVerifying: true });
try {
await post2faVerify({ verifyCode });
@ -409,4 +421,4 @@ class MFAuth extends React.Component<Props, State> {
};
}
export default view(MFAuth);
export default withRouter(view(MFAuth));