-
+
+ Register your device by scanning the barcode or adding the key.
-
- {this.state.secret ?
: null}
+
+
+
+
+
{secret.base32_secret}
+
+
+
+
+
+
Need Google Authenticator?
+
+
)
}
+ private renderError() {
+ const {classes} = this.props;
+ return (
+
+
+ {this.props.error}
+
+
+
+
+
+
+ );
+ }
+
+ private renderSecret() {
+ return this.props.secret
+ ? this.renderWithSecret(this.props.secret)
+ : this.renderError();
+ }
+
+ private renderLoading() {
+ const { classes } = this.props;
+ return (
+
+
One-Time password secret is being generated...
+
+
+ )
+ }
+
render() {
- return this.state.secret ? this.renderWithSecret(this.state.secret) : null;
+ return !this.props.secret && !this.props.error
+ ? this.renderLoading()
+ : this.renderSecret();
}
}
diff --git a/client-react/src/views/OneTimePasswordRegistrationView/Secret.ts b/client-react/src/views/OneTimePasswordRegistrationView/Secret.ts
new file mode 100644
index 00000000..61e93943
--- /dev/null
+++ b/client-react/src/views/OneTimePasswordRegistrationView/Secret.ts
@@ -0,0 +1,5 @@
+
+export interface Secret {
+ otpauth_url: string;
+ base32_secret: string;
+}
\ No newline at end of file
diff --git a/client-react/src/views/ResetPasswordView/ResetPasswordView.tsx b/client-react/src/views/ResetPasswordView/ResetPasswordView.tsx
index b7770a36..b1c08d36 100644
--- a/client-react/src/views/ResetPasswordView/ResetPasswordView.tsx
+++ b/client-react/src/views/ResetPasswordView/ResetPasswordView.tsx
@@ -16,12 +16,14 @@ class ResetPasswordView extends Component
{
diff --git a/client-react/src/views/SecondFactorView/SecondFactorView.tsx b/client-react/src/views/SecondFactorView/SecondFactorView.tsx
index 6e9e78ec..dcc021da 100644
--- a/client-react/src/views/SecondFactorView/SecondFactorView.tsx
+++ b/client-react/src/views/SecondFactorView/SecondFactorView.tsx
@@ -3,16 +3,18 @@ import React, { Component } from 'react';
import { WithStyles, withStyles, Button, TextField } from '@material-ui/core';
import styles from '../../assets/jss/views/SecondFactorView/SecondFactorView';
-import securityKeyImage from '../../assets/images/security-key-hand.png';
import StateSynchronizer from '../../containers/components/StateSynchronizer/StateSynchronizer';
import { RouterProps, Redirect } from 'react-router';
import RemoteState from '../../reducers/Portal/RemoteState';
import AuthenticationLevel from '../../types/AuthenticationLevel';
import { WithState } from '../../components/StateSynchronizer/WithState';
-
-type Mode = 'u2f' | 'totp';
+import CircleLoader, { Status } from '../../components/CircleLoader/CircleLoader';
export interface Props extends WithStyles, RouterProps, WithState {
+ securityKeySupported: boolean;
+ securityKeyVerified: boolean;
+ securityKeyError: string | null;
+
onLogoutClicked: () => void;
onRegisterSecurityKeyClicked: () => void;
onRegisterOneTimePasswordClicked: () => void;
@@ -20,7 +22,6 @@ export interface Props extends WithStyles, RouterProps, WithState {
};
interface State {
- mode: Mode;
remoteState: RemoteState | null;
}
@@ -28,43 +29,53 @@ class SecondFactorView extends Component {
constructor(props: Props) {
super(props);
this.state = {
- mode: 'u2f',
remoteState: null,
}
}
- private toggleMode = () => {
- if (this.state.mode === 'u2f') {
- this.setState({mode: 'totp'});
- } else if (this.state.mode === 'totp') {
- this.setState({mode: 'u2f'});
- }
- }
-
- private renderU2f() {
+ private renderU2f(n: number) {
const { classes } = this.props;
+ let u2fStatus = Status.LOADING;
+ if (this.props.securityKeyVerified) {
+ u2fStatus = Status.SUCCESSFUL;
+ } else if (this.props.securityKeyError) {
+ u2fStatus = Status.FAILURE;
+ }
return (
-
-
-
-
+
+
Option {n} - Security Key
Insert your security key into a USB port and touch the gold disk.
+
+
+
+
)
}
- private renderTotp() {
+ private renderTotp(n: number) {
const { classes } = this.props;
return (
-
-
Provide a one-time password.
+
+
Option {n} - One-Time Password
+ label="One-Time Password">
+
)
}
diff --git a/client-react/src/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView.tsx b/client-react/src/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView.tsx
index b5aaa5aa..a333ac65 100644
--- a/client-react/src/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView.tsx
+++ b/client-react/src/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView.tsx
@@ -1,18 +1,24 @@
import React, { Component } from "react";
-import securityKeyImage from '../../assets/images/security-key-hand.png';
-import { WithStyles, withStyles } from "@material-ui/core";
+import { WithStyles, withStyles, Button } from "@material-ui/core";
import styles from '../../assets/jss/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView';
-import { RouteProps } from "react-router";
+import { RouteProps, RouterProps } from "react-router";
import QueryString from 'query-string';
+import FormNotification from "../../components/FormNotification/FormNotification";
+import CircleLoader, { Status } from "../../components/CircleLoader/CircleLoader";
-interface Props extends WithStyles, RouteProps {
- componentDidMount: (token: string) => void;
+export interface Props extends WithStyles, RouteProps, RouterProps {
+ deviceRegistered: boolean | null;
+ error: string | null;
+ onInit: (token: string) => void;
+ onBackClicked: () => void;
}
class SecurityKeyRegistrationView extends Component
{
componentDidMount() {
+ if (this.props.deviceRegistered) return;
+
if (!this.props.location) {
console.error('There is no location to retrieve query params from...');
return;
@@ -22,20 +28,53 @@ class SecurityKeyRegistrationView extends Component {
console.error('Token parameter is expected and not provided');
return;
}
- this.props.componentDidMount(params['token'] as string);
+ this.props.onInit(params['token'] as string);
}
- render() {
- const {classes} = this.props;
+ private renderError() {
+ const { classes } = this.props;
return (
-
Press the gold disk to register your security key
-
-
+
+ {this.props.error}
+
+
+
)
}
+
+ private renderRegistering() {
+ const { classes } = this.props;
+ let status = Status.LOADING;
+ if (this.props.deviceRegistered === true) {
+ status = Status.SUCCESSFUL;
+ } else if (this.props.error) {
+ status = Status.FAILURE;
+ }
+ return (
+
+
Press the gold disk to register your security key
+
+
+
+
+ )
+ }
+
+ render() {
+ if (this.props.error) {
+ return this.renderError();
+ }
+
+ return this.renderRegistering();
+ }
}
export default withStyles(styles)(SecurityKeyRegistrationView);
\ No newline at end of file