{props.title}
-
{props.children}
-
{props.explanation}
+
{props.onRegisterClick
?
({
- icon: {
- paddingTop: theme.spacing(2),
- paddingBottom: theme.spacing(2),
+ container: {
+ height: "200px",
},
-}));
\ No newline at end of file
+ containerFlex: {
+ display: "flex",
+ flexWrap: "wrap",
+ height: "100%",
+ width: "100%",
+ alignItems: "center",
+ alignContent: "center",
+ justifyContent: "center",
+ }
+}));
+
+function AlreadyAuthenticatedContainer() {
+ const theme = useTheme();
+ return (
+
+
+ Authenticated!
+
+ )
+}
+
+function NotRegisteredContainer() {
+ const theme = useTheme();
+ return (
+
+
+ Register your first device by clicking on the link below
+
+ )
+}
+
+interface MethodContainerProps {
+ explanation: string;
+ children: ReactNode;
+}
+
+function MethodContainer(props: MethodContainerProps) {
+ const theme = useTheme();
+ return (
+
+ {props.children}
+ {props.explanation}
+
+ )
+}
\ No newline at end of file
diff --git a/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx b/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx
index 257439a3..43c021fc 100644
--- a/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/OneTimePasswordMethod.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useCallback } from "react";
-import MethodContainer from "./MethodContainer";
+import MethodContainer, { State as MethodContainerState } from "./MethodContainer";
import OTPDial from "./OTPDial";
import { completeTOTPSignIn } from "../../../services/OneTimePassword";
import { useRedirectionURL } from "../../../hooks/RedirectionURL";
@@ -15,6 +15,7 @@ export enum State {
export interface Props {
id: string;
authenticationLevel: AuthenticationLevel;
+ registered: boolean;
onRegisterClick: () => void;
onSignInError: (err: Error) => void;
@@ -65,11 +66,19 @@ export default function (props: Props) {
useEffect(() => { signInFunc() }, [signInFunc]);
+ let methodState = MethodContainerState.METHOD;
+ if (props.authenticationLevel === AuthenticationLevel.TwoFactor) {
+ methodState = MethodContainerState.ALREADY_AUTHENTICATED;
+ } else if (!props.registered) {
+ methodState = MethodContainerState.NOT_REGISTERED;
+ }
+
return (
;
}
+ let methodState = MethodContainerState.METHOD;
+ if (props.authenticationLevel === AuthenticationLevel.TwoFactor) {
+ methodState = MethodContainerState.ALREADY_AUTHENTICATED;
+ }
+
return (
+ explanation="A notification has been sent to your smartphone"
+ state={methodState}>
{icon}
diff --git a/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx b/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
index c989b0df..3497a243 100644
--- a/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
@@ -17,7 +17,7 @@ import {
SecondFactorPushRoute, SecondFactorU2FRoute, SecondFactorRoute
} from "../../../Routes";
import { setPrefered2FAMethod } from "../../../services/UserPreferences";
-import { UserPreferences } from "../../../models/UserPreferences";
+import { UserInfo } from "../../../models/UserInfo";
import { Configuration } from "../../../models/Configuration";
import u2fApi from "u2f-api";
import { AuthenticationLevel } from "../../../services/State";
@@ -28,7 +28,7 @@ export interface Props {
username: string;
authenticationLevel: AuthenticationLevel;
- userPreferences: UserPreferences;
+ userInfo: UserInfo;
configuration: Configuration;
onMethodChanged: (method: SecondFactorMethod) => void;
@@ -109,6 +109,8 @@ export default function (props: Props) {
createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess} />
@@ -117,6 +119,8 @@ export default function (props: Props) {
createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess} />
diff --git a/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx b/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx
index 7f3b4e23..23da9d5e 100644
--- a/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/SecurityKeyMethod.tsx
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useState, Fragment } from "react";
-import MethodContainer from "./MethodContainer";
+import MethodContainer, { State as MethodContainerState } from "./MethodContainer";
import { makeStyles, Button, useTheme } from "@material-ui/core";
import { initiateU2FSignin, completeU2FSignin } from "../../../services/SecurityKey";
import u2fApi from "u2f-api";
@@ -8,7 +8,6 @@ import { useIsMountedRef } from "../../../hooks/Mounted";
import { useTimer } from "../../../hooks/Timer";
import LinearProgressBar from "../../../components/LinearProgressBar";
import FingerTouchIcon from "../../../components/FingerTouchIcon";
-import SuccessIcon from "../../../components/SuccessIcon";
import FailureIcon from "../../../components/FailureIcon";
import IconWithContext from "./IconWithContext";
import { CSSProperties } from "@material-ui/styles";
@@ -17,13 +16,13 @@ import { AuthenticationLevel } from "../../../services/State";
export enum State {
WaitTouch = 1,
SigninInProgress = 2,
- Success = 3,
- Failure = 4,
+ Failure = 3,
}
export interface Props {
id: string;
authenticationLevel: AuthenticationLevel;
+ registered: boolean;
onRegisterClick: () => void;
onSignInError: (err: Error) => void;
@@ -44,7 +43,7 @@ export default function (props: Props) {
const doInitiateSignIn = useCallback(async () => {
// If user is already authenticated, we don't initiate sign in process.
- if (props.authenticationLevel >= AuthenticationLevel.TwoFactor) {
+ if (!props.registered || props.authenticationLevel >= AuthenticationLevel.TwoFactor) {
return;
}
@@ -69,8 +68,7 @@ export default function (props: Props) {
setState(State.SigninInProgress);
const res = await completeU2FSignin(signResponse, redirectionURL);
- setState(State.Success);
- setTimeout(() => { onSignInSuccessCallback(res ? res.redirect : undefined) }, 1500);
+ onSignInSuccessCallback(res ? res.redirect : undefined);
} catch (err) {
// If the request was initiated and the user changed 2FA method in the meantime,
// the process is interrupted to avoid updating state of unmounted component.
@@ -79,22 +77,23 @@ export default function (props: Props) {
onSignInErrorCallback(new Error("Failed to initiate security key sign in process"));
setState(State.Failure);
}
- }, [onSignInSuccessCallback, onSignInErrorCallback, redirectionURL, mounted, triggerTimer, props.authenticationLevel]);
-
- // Set successful state if user is already authenticated.
- useEffect(() => {
- if (props.authenticationLevel >= AuthenticationLevel.TwoFactor) {
- setState(State.Success);
- }
- }, [props.authenticationLevel, setState]);
+ }, [onSignInSuccessCallback, onSignInErrorCallback, redirectionURL, mounted, triggerTimer, props.authenticationLevel, props.registered]);
useEffect(() => { doInitiateSignIn() }, [doInitiateSignIn]);
+ let methodState = MethodContainerState.METHOD;
+ if (props.authenticationLevel === AuthenticationLevel.TwoFactor) {
+ methodState = MethodContainerState.ALREADY_AUTHENTICATED;
+ } else if (!props.registered) {
+ methodState = MethodContainerState.NOT_REGISTERED;
+ }
+
return (
@@ -134,15 +133,9 @@ function Icon(props: IconProps) {
context={
}
className={state === State.Failure ? undefined : "hidden"} />
- const success =
}
- context={
Success!
}
- className={state === State.Success || state === State.SigninInProgress ? undefined : "hidden"} />
-
return (
{touch}
- {success}
{failure}
)