From 8ef402511ca8047c68123f3e02b90a304cc48d36 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 15:15:49 +0100 Subject: [PATCH 1/7] Add Duo Push Notification option as 2FA. --- .travis.yml | 1 + bootstrap.sh | 3 +- .../SecondFactorDuoPush.module.scss | 15 + client/src/behaviors/TriggerDuoPushAuth.ts | 18 + .../SecondFactorDuoPush.tsx | 50 +++ .../SecondFactorForm/SecondFactorForm.tsx | 6 + .../SecondFactorDuoPush.ts | 55 +++ .../SecondFactorForm/SecondFactorForm.ts | 1 + .../reducers/Portal/SecondFactor/actions.ts | 10 +- .../reducers/Portal/SecondFactor/reducer.ts | 27 ++ client/src/reducers/constants.ts | 4 + client/src/services/AutheliaService.ts | 15 + client/src/types/Method2FA.ts | 2 +- config.template.yml | 9 + example/compose/duo-api/Dockerfile | 12 + example/compose/duo-api/docker-compose.yml | 6 + example/compose/duo-api/duo_api.js | 66 ++++ example/compose/duo-api/duo_client.js | 10 + example/compose/duo-api/package-lock.json | 358 ++++++++++++++++++ example/compose/duo-api/package.json | 14 + .../compose/nginx/portal/docker-compose.yml | 4 +- example/compose/nginx/portal/nginx.conf.ejs | 19 + package-lock.json | 62 +-- package.json | 1 + scripts/authelia-scripts-bootstrap | 3 + server/src/lib/Server.ts | 3 + .../lib/configuration/schema/Configuration.ts | 2 + .../schema/DuoPushConfiguration.ts | 6 + .../routes/secondfactor/duo-push/Post.spec.ts | 109 ++++++ .../lib/routes/secondfactor/duo-push/Post.ts | 51 +++ .../secondfactor/preferences/Get.spec.ts | 2 +- .../secondfactor/preferences/Post.spec.ts | 2 +- .../lib/routes/verify/CheckAuthorizations.ts | 4 +- server/src/lib/web_server/RestApi.ts | 7 + shared/api.ts | 15 + test/helpers/assertions/VerifyHasAppeared.ts | 5 + .../context/AutheliaServerWithHotReload.ts | 11 +- test/suites/basic/config.yml | 6 - test/suites/duo-push/README.md | 12 + test/suites/duo-push/config.yml | 116 ++++++ test/suites/duo-push/environment.ts | 36 ++ .../duo-push/scenarii/DuoPushNotification.ts | 64 ++++ test/suites/duo-push/test.ts | 16 + test/suites/duo-push/users_database.yml | 29 ++ 44 files changed, 1197 insertions(+), 70 deletions(-) create mode 100644 client/src/assets/scss/components/SecondFactorDuoPush/SecondFactorDuoPush.module.scss create mode 100644 client/src/behaviors/TriggerDuoPushAuth.ts create mode 100644 client/src/components/SecondFactorDuoPush/SecondFactorDuoPush.tsx create mode 100644 client/src/containers/components/SecondFactorDuoPush/SecondFactorDuoPush.ts create mode 100644 example/compose/duo-api/Dockerfile create mode 100644 example/compose/duo-api/docker-compose.yml create mode 100644 example/compose/duo-api/duo_api.js create mode 100644 example/compose/duo-api/duo_client.js create mode 100644 example/compose/duo-api/package-lock.json create mode 100644 example/compose/duo-api/package.json create mode 100644 server/src/lib/configuration/schema/DuoPushConfiguration.ts create mode 100644 server/src/lib/routes/secondfactor/duo-push/Post.spec.ts create mode 100644 server/src/lib/routes/secondfactor/duo-push/Post.ts create mode 100644 test/helpers/assertions/VerifyHasAppeared.ts create mode 100644 test/suites/duo-push/README.md create mode 100644 test/suites/duo-push/config.yml create mode 100644 test/suites/duo-push/environment.ts create mode 100644 test/suites/duo-push/scenarii/DuoPushNotification.ts create mode 100644 test/suites/duo-push/test.ts create mode 100644 test/suites/duo-push/users_database.yml diff --git a/.travis.yml b/.travis.yml index debdccbe..681936a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ addons: - authelia.example.com - admin.example.com - mail.example.com + - duo.example.com before_script: - export DISPLAY=:99.0 diff --git a/bootstrap.sh b/bootstrap.sh index 4c92e6a5..c3eee60e 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -38,7 +38,8 @@ then 127.0.0.1 mx1.mail.example.com 127.0.0.1 mx2.mail.example.com 127.0.0.1 singlefactor.example.com -127.0.0.1 login.example.com" +127.0.0.1 login.example.com +192.168.240.100 duo.example.com" return; fi diff --git a/client/src/assets/scss/components/SecondFactorDuoPush/SecondFactorDuoPush.module.scss b/client/src/assets/scss/components/SecondFactorDuoPush/SecondFactorDuoPush.module.scss new file mode 100644 index 00000000..22787100 --- /dev/null +++ b/client/src/assets/scss/components/SecondFactorDuoPush/SecondFactorDuoPush.module.scss @@ -0,0 +1,15 @@ +@import '../../variables.scss'; + +.image { + width: '120px'; +} + +.imageContainer { + text-align: center; + margin-top: ($theme-spacing) * 2; + margin-bottom: ($theme-spacing) * 2; +} + +.retryContainer { + text-align: center; +} \ No newline at end of file diff --git a/client/src/behaviors/TriggerDuoPushAuth.ts b/client/src/behaviors/TriggerDuoPushAuth.ts new file mode 100644 index 00000000..61cbe67f --- /dev/null +++ b/client/src/behaviors/TriggerDuoPushAuth.ts @@ -0,0 +1,18 @@ +import { Dispatch } from "redux"; +import AutheliaService from "../services/AutheliaService"; +import { triggerDuoPushAuth, triggerDuoPushAuthSuccess, triggerDuoPushAuthFailure } from "../reducers/Portal/SecondFactor/actions"; + +export default async function(dispatch: Dispatch, redirectionUrl: string | null) { + dispatch(triggerDuoPushAuth()); + try { + const res = await AutheliaService.triggerDuoPush(redirectionUrl); + const body = await res.json(); + if ('error' in body) { + throw new Error(body['error']); + } + dispatch(triggerDuoPushAuthSuccess()); + return body; + } catch (err) { + dispatch(triggerDuoPushAuthFailure(err.message)) + } +} \ No newline at end of file diff --git a/client/src/components/SecondFactorDuoPush/SecondFactorDuoPush.tsx b/client/src/components/SecondFactorDuoPush/SecondFactorDuoPush.tsx new file mode 100644 index 00000000..29d30973 --- /dev/null +++ b/client/src/components/SecondFactorDuoPush/SecondFactorDuoPush.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import classnames from 'classnames'; +import CircleLoader, { Status } from '../../components/CircleLoader/CircleLoader'; +import styles from '../../assets/scss/components/SecondFactorDuoPush/SecondFactorDuoPush.module.scss'; +import { Button } from '@material/react-button'; + +export interface OwnProps { + redirectionUrl: string | null; +} + +export interface StateProps { + duoPushVerified: boolean | null; + duoPushError: string | null; +} + +export interface DispatchProps { + onInit: () => void; + onRetryClicked: () => void; +} + +export type Props = OwnProps & StateProps & DispatchProps; + +export default class SecondFactorDuoPush extends React.Component { + componentWillMount() { + this.props.onInit(); + } + + render() { + let u2fStatus = Status.LOADING; + if (this.props.duoPushVerified === true) { + u2fStatus = Status.SUCCESSFUL; + } else if (this.props.duoPushError) { + u2fStatus = Status.FAILURE; + } + return ( +
+
You will soon receive a push notification on your phone.
+
+ +
+ {(u2fStatus == Status.FAILURE) + ?
+ +
+ : null} +
+ ) + } +} \ No newline at end of file diff --git a/client/src/components/SecondFactorForm/SecondFactorForm.tsx b/client/src/components/SecondFactorForm/SecondFactorForm.tsx index fef245ce..111d6021 100644 --- a/client/src/components/SecondFactorForm/SecondFactorForm.tsx +++ b/client/src/components/SecondFactorForm/SecondFactorForm.tsx @@ -5,6 +5,7 @@ import SecondFactorTOTP from '../../containers/components/SecondFactorTOTP/Secon import SecondFactorU2F from '../../containers/components/SecondFactorU2F/SecondFactorU2F'; import { Button } from '@material/react-button'; import classnames from 'classnames'; +import SecondFactorDuoPush from '../../containers/components/SecondFactorDuoPush/SecondFactorDuoPush'; export interface OwnProps { username: string; @@ -21,6 +22,7 @@ export interface DispatchProps { onLogoutClicked: () => void; onOneTimePasswordMethodClicked: () => void; onSecurityKeyMethodClicked: () => void; + onDuoPushMethodClicked: () => void; onUseAnotherMethodClicked: () => void; } @@ -37,6 +39,9 @@ class SecondFactorForm extends Component { if (method == 'u2f') { title = "Security Key"; methodComponent = (); + } else if (method == "duo_push") { + title = "Duo Push Notification"; + methodComponent = (); } else { title = "One-Time Password" methodComponent = (); @@ -57,6 +62,7 @@ class SecondFactorForm extends Component {
+
); diff --git a/client/src/containers/components/SecondFactorDuoPush/SecondFactorDuoPush.ts b/client/src/containers/components/SecondFactorDuoPush/SecondFactorDuoPush.ts new file mode 100644 index 00000000..2f23d23e --- /dev/null +++ b/client/src/containers/components/SecondFactorDuoPush/SecondFactorDuoPush.ts @@ -0,0 +1,55 @@ +import { connect } from 'react-redux'; +import { RootState } from '../../../reducers'; +import { Dispatch } from 'redux'; +import SecondFactorDuoPush, { StateProps, OwnProps, DispatchProps } from '../../../components/SecondFactorDuoPush/SecondFactorDuoPush'; +import FetchStateBehavior from '../../../behaviors/FetchStateBehavior'; +import TriggerDuoPushAuth from '../../../behaviors/TriggerDuoPushAuth'; + + +const mapStateToProps = (state: RootState): StateProps => ({ + duoPushVerified: state.secondFactor.duoPushVerificationSuccess, + duoPushError: state.secondFactor.duoPushVerificationError, +}); + +async function redirectIfPossible(body: any) { + if ('redirect' in body) { + window.location.href = body['redirect']; + return true; + } + return false; +} + +async function handleSuccess(dispatch: Dispatch, res: Response, duration?: number) { + async function handle() { + const redirected = await redirectIfPossible(res); + if (!redirected) { + await FetchStateBehavior(dispatch); + } + } + + if (duration) { + setTimeout(handle, duration); + } else { + await handle(); + } +} + +async function triggerDuoPushAuth(dispatch: Dispatch, redirectionUrl: string | null) { + const res = await TriggerDuoPushAuth(dispatch, redirectionUrl); + if (!res) return; + await handleSuccess(dispatch, res, 2000); +} + +const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps): DispatchProps => { + return { + onInit: async () => { + await triggerDuoPushAuth(dispatch, ownProps.redirectionUrl); + }, + onRetryClicked: async () => { + await triggerDuoPushAuth(dispatch, ownProps.redirectionUrl); + } + } +} + + +export default connect(mapStateToProps, mapDispatchToProps)(SecondFactorDuoPush); \ No newline at end of file diff --git a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts index 66b1e5ea..01dde789 100644 --- a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts +++ b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts @@ -31,6 +31,7 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { onLogoutClicked: () => LogoutBehavior(dispatch), onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), + onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), onUseAnotherMethodClicked: () => dispatch(setUseAnotherMethod(true)), } } diff --git a/client/src/reducers/Portal/SecondFactor/actions.ts b/client/src/reducers/Portal/SecondFactor/actions.ts index eaeae9cf..d7c23576 100644 --- a/client/src/reducers/Portal/SecondFactor/actions.ts +++ b/client/src/reducers/Portal/SecondFactor/actions.ts @@ -16,7 +16,10 @@ import { SET_PREFERED_METHOD, SET_PREFERED_METHOD_FAILURE, SET_PREFERED_METHOD_SUCCESS, - SET_USE_ANOTHER_METHOD + SET_USE_ANOTHER_METHOD, + TRIGGER_DUO_PUSH_AUTH, + TRIGGER_DUO_PUSH_AUTH_SUCCESS, + TRIGGER_DUO_PUSH_AUTH_FAILURE } from "../../constants"; import Method2FA from "../../../types/Method2FA"; @@ -54,6 +57,11 @@ export const oneTimePasswordVerificationFailure = createAction(ONE_TIME_PASSWORD return (err: string) => resolve(err); }); +export const triggerDuoPushAuth = createAction(TRIGGER_DUO_PUSH_AUTH); +export const triggerDuoPushAuthSuccess = createAction(TRIGGER_DUO_PUSH_AUTH_SUCCESS); +export const triggerDuoPushAuthFailure = createAction(TRIGGER_DUO_PUSH_AUTH_FAILURE, resolve => { + return (err: string) => resolve(err); +}); export const logout = createAction(LOGOUT_REQUEST); export const logoutSuccess = createAction(LOGOUT_SUCCESS); diff --git a/client/src/reducers/Portal/SecondFactor/reducer.ts b/client/src/reducers/Portal/SecondFactor/reducer.ts index e12608e4..cf6605e2 100644 --- a/client/src/reducers/Portal/SecondFactor/reducer.ts +++ b/client/src/reducers/Portal/SecondFactor/reducer.ts @@ -27,6 +27,10 @@ interface SecondFactorState { oneTimePasswordVerificationLoading: boolean, oneTimePasswordVerificationSuccess: boolean | null, oneTimePasswordVerificationError: string | null, + + duoPushVerificationLoading: boolean; + duoPushVerificationSuccess: boolean | null; + duoPushVerificationError: string | null; } const secondFactorInitialState: SecondFactorState = { @@ -51,6 +55,10 @@ const secondFactorInitialState: SecondFactorState = { oneTimePasswordVerificationLoading: false, oneTimePasswordVerificationError: null, oneTimePasswordVerificationSuccess: null, + + duoPushVerificationLoading: false, + duoPushVerificationSuccess: null, + duoPushVerificationError: null, } export type PortalState = StateType; @@ -163,6 +171,25 @@ export default (state = secondFactorInitialState, action: SecondFactorAction): S ...state, userAnotherMethod: action.payload, } + case getType(Actions.triggerDuoPushAuth): + return { + ...state, + duoPushVerificationLoading: true, + duoPushVerificationError: null, + duoPushVerificationSuccess: null, + } + case getType(Actions.triggerDuoPushAuthSuccess): + return { + ...state, + duoPushVerificationLoading: false, + duoPushVerificationSuccess: true, + } + case getType(Actions.triggerDuoPushAuthFailure): + return { + ...state, + duoPushVerificationLoading: false, + duoPushVerificationError: action.payload, + } } return state; } \ No newline at end of file diff --git a/client/src/reducers/constants.ts b/client/src/reducers/constants.ts index abbd5f05..9332d4ee 100644 --- a/client/src/reducers/constants.ts +++ b/client/src/reducers/constants.ts @@ -28,6 +28,10 @@ export const ONE_TIME_PASSWORD_VERIFICATION_REQUEST = '@portal/second_factor/one export const ONE_TIME_PASSWORD_VERIFICATION_SUCCESS = '@portal/second_factor/one_time_password_verification_success'; export const ONE_TIME_PASSWORD_VERIFICATION_FAILURE = '@portal/second_factor/one_time_password_verification_failure'; +export const TRIGGER_DUO_PUSH_AUTH = '@portal/second_factor/trigger_duo_push_auth_request'; +export const TRIGGER_DUO_PUSH_AUTH_SUCCESS = '@portal/second_factor/trigger_duo_push_auth_request_success'; +export const TRIGGER_DUO_PUSH_AUTH_FAILURE = '@portal/second_factor/trigger_duo_push_auth_request_failure'; + export const LOGOUT_REQUEST = '@portal/logout_request'; export const LOGOUT_SUCCESS = '@portal/logout_success'; export const LOGOUT_FAILURE = '@portal/logout_failure'; diff --git a/client/src/services/AutheliaService.ts b/client/src/services/AutheliaService.ts index a0c5d1f5..cf5b9b07 100644 --- a/client/src/services/AutheliaService.ts +++ b/client/src/services/AutheliaService.ts @@ -113,6 +113,21 @@ class AutheliaService { }) } + static async triggerDuoPush(redirectionUrl: string | null): Promise { + + const headers: Record = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + } + if (redirectionUrl) { + headers['X-Target-Url'] = redirectionUrl; + } + return this.fetchSafe('/api/duo-push', { + method: 'POST', + headers: headers, + }) + } + static async initiatePasswordResetIdentityValidation(username: string) { return this.fetchSafe('/api/password-reset/identity/start', { method: 'POST', diff --git a/client/src/types/Method2FA.ts b/client/src/types/Method2FA.ts index 09e0b3c4..c46b4b27 100644 --- a/client/src/types/Method2FA.ts +++ b/client/src/types/Method2FA.ts @@ -1,4 +1,4 @@ -type Method2FA = "u2f" | "totp"; +type Method2FA = "u2f" | "totp" | "duo_push"; export default Method2FA; \ No newline at end of file diff --git a/config.template.yml b/config.template.yml index a29682b8..0587fa53 100644 --- a/config.template.yml +++ b/config.template.yml @@ -29,6 +29,15 @@ default_redirection_url: https://home.example.com:8080/ totp: issuer: authelia.com +# Duo Push API +# +# Parameters used to contact the Duo API. Those are generated when you protect an application +# of type "Partner Auth API" in the management panel. +duo_api: + hostname: api-123456789.duosecurity.com + integration_key: ABCDEF + secret_key: 1234567890abcdefghifjkl + # The authentication backend to use for verifying user passwords # and retrieve information such as email address and groups # users belong to. diff --git a/example/compose/duo-api/Dockerfile b/example/compose/duo-api/Dockerfile new file mode 100644 index 00000000..fc70d55d --- /dev/null +++ b/example/compose/duo-api/Dockerfile @@ -0,0 +1,12 @@ +FROM node:8.7.0-alpine + +WORKDIR /usr/app/src + +ADD package.json package.json +RUN npm install --production --quiet + +ADD duo_api.js duo_api.js + +EXPOSE 3000 + +CMD ["node", "duo_api.js"] \ No newline at end of file diff --git a/example/compose/duo-api/docker-compose.yml b/example/compose/duo-api/docker-compose.yml new file mode 100644 index 00000000..0932084a --- /dev/null +++ b/example/compose/duo-api/docker-compose.yml @@ -0,0 +1,6 @@ +version: '2' +services: + duo-api: + image: authelia-duo-api + networks: + - authelianet diff --git a/example/compose/duo-api/duo_api.js b/example/compose/duo-api/duo_api.js new file mode 100644 index 00000000..5181f5c7 --- /dev/null +++ b/example/compose/duo-api/duo_api.js @@ -0,0 +1,66 @@ +/* + * This is a script to fake the Duo API for push notifications. + * + * Access is allowed by default but one can change the behavior at runtime + * by POSTing to /allow or /deny. Then the /auth/v2/auth endpoint will act + * accordingly. + */ + +const express = require("express"); +const app = express(); +const port = 3000; + +app.set('trust proxy', true); + +let permission = 'allow'; + +app.post('/allow', (req, res) => { + permission = 'allow'; + res.send('ALLOWED'); +}); + +app.post('/deny', (req, res) => { + permission = 'deny'; + res.send('DENIED'); +}); + +app.post('/auth/v2/auth', (req, res) => { + let response; + if (permission == 'allow') { + response = { + response: { + result: 'allow', + status: 'allow', + status_msg: 'The user allowed access.', + }, + stat: 'OK', + }; + } else { + response = { + response: { + result: 'deny', + status: 'deny', + status_msg: 'The user denied access.', + }, + stat: 'OK', + }; + } + setTimeout(() => res.json(response), 2000); +}); + +app.listen(port, () => console.log(`Duo API listening on port ${port}!`)); + +// The signals we want to handle +// NOTE: although it is tempting, the SIGKILL signal (9) cannot be intercepted and handled +var signals = { + 'SIGHUP': 1, + 'SIGINT': 2, + 'SIGTERM': 15 +}; +// Create a listener for each of the signals that we want to handle +Object.keys(signals).forEach((signal) => { + process.on(signal, () => { + console.log(`process received a ${signal} signal`); + process.exit(128 + signals[signal]); + }); +}); \ No newline at end of file diff --git a/example/compose/duo-api/duo_client.js b/example/compose/duo-api/duo_client.js new file mode 100644 index 00000000..ee6b2a11 --- /dev/null +++ b/example/compose/duo-api/duo_client.js @@ -0,0 +1,10 @@ +/* + * This is just client script to test the fake API. + */ + +const DuoApi = require("@duosecurity/duo_api"); + +process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0; + +const client = new DuoApi.Client("ABCDEFG", "SECRET", "duo.example.com"); +client.jsonApiCall("POST", "/auth/v2/auth", { username: 'john', factor: "push", device: "auto" }, console.log); \ No newline at end of file diff --git a/example/compose/duo-api/package-lock.json b/example/compose/duo-api/package-lock.json new file mode 100644 index 00000000..06cb0628 --- /dev/null +++ b/example/compose/duo-api/package-lock.json @@ -0,0 +1,358 @@ +{ + "name": "duo-api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "2.1.22", + "negotiator": "0.6.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "1.6.16" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.4", + "qs": "6.5.2", + "range-parser": "1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "1.4.0", + "type-is": "1.6.16", + "utils-merge": "1.0.1", + "vary": "1.1.2" + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.4.0", + "unpipe": "1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": "1.4.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": "2.1.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "requires": { + "mime-db": "1.38.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.3", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.22" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/example/compose/duo-api/package.json b/example/compose/duo-api/package.json new file mode 100644 index 00000000..4d8267ba --- /dev/null +++ b/example/compose/duo-api/package.json @@ -0,0 +1,14 @@ +{ + "name": "duo-api", + "version": "1.0.0", + "description": "", + "main": "duo_api.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.16.4" + } +} diff --git a/example/compose/nginx/portal/docker-compose.yml b/example/compose/nginx/portal/docker-compose.yml index d5fa8b3e..44622085 100644 --- a/example/compose/nginx/portal/docker-compose.yml +++ b/example/compose/nginx/portal/docker-compose.yml @@ -8,4 +8,6 @@ services: ports: - "8080:443" networks: - - authelianet + authelianet: + # Set the IP to be able to query on port 443 + ipv4_address: 192.168.240.100 diff --git a/example/compose/nginx/portal/nginx.conf.ejs b/example/compose/nginx/portal/nginx.conf.ejs index 3306e45a..2f7c14c9 100644 --- a/example/compose/nginx/portal/nginx.conf.ejs +++ b/example/compose/nginx/portal/nginx.conf.ejs @@ -431,5 +431,24 @@ http { proxy_pass $upstream_endpoint; } } + + server { + listen 443 ssl; + server_name duo.example.com; + + resolver 127.0.0.11 ipv6=off; + set $upstream_endpoint http://duo-api:3000; + + ssl_certificate /etc/ssl/server.crt; + ssl_certificate_key /etc/ssl/server.key; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "SAMEORIGIN"; + + location / { + proxy_set_header Host $http_host; + proxy_pass $upstream_endpoint; + } + } } diff --git a/package-lock.json b/package-lock.json index ba64b8be..e29d9f0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -171,6 +171,14 @@ "to-fast-properties": "2.0.0" } }, + "@duosecurity/duo_api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@duosecurity/duo_api/-/duo_api-1.2.0.tgz", + "integrity": "sha512-Jxmeo5VZtaut9hELnBNZyvA7kojwRBAHl0uOk0dZSfBbphjr3QJ+92dnm/I++GPUEhEKjALLeQ9fCABwo5HsPQ==", + "requires": { + "nopt": "3.0.6" + } + }, "@sinonjs/commons": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz", @@ -482,12 +490,6 @@ "integrity": "sha512-txsii9cwD2OUOPukfPu3Jpoi3CnznBAwRX3JF26EC4p5T6IA8AaL6PBilACyY2fJkk+ydDNo4BJrJOo/OmNaZw==", "dev": true }, - "@types/proxyquire": { - "version": "1.3.28", - "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", - "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", - "dev": true - }, "@types/query-string": { "version": "5.1.0", "resolved": "http://registry.npmjs.org/@types/query-string/-/query-string-5.1.0.tgz", @@ -595,8 +597,7 @@ "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.5", @@ -2530,16 +2531,6 @@ "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=", "dev": true }, - "fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", - "dev": true, - "requires": { - "is-object": "1.0.1", - "merge-descriptors": "1.0.1" - } - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4201,12 +4192,6 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -5163,12 +5148,6 @@ "integrity": "sha1-WuDA6vj+I+AJzQH5iJtCxPY0rxI=", "dev": true }, - "module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", - "dev": true - }, "moment": { "version": "2.22.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", @@ -5812,7 +5791,6 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, "requires": { "abbrev": "1.1.1" } @@ -7288,28 +7266,6 @@ "ipaddr.js": "1.6.0" } }, - "proxyquire": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.0.tgz", - "integrity": "sha512-kptdFArCfGRtQFv3Qwjr10lwbEV0TBJYvfqzhwucyfEXqVgmnAkyEw/S3FYzR5HI9i5QOq4rcqQjZ6AlknlCDQ==", - "dev": true, - "requires": { - "fill-keys": "1.0.2", - "module-not-found-error": "1.0.1", - "resolve": "1.8.1" - }, - "dependencies": { - "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - } - } - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", diff --git a/package.json b/package.json index 589edd3d..727ede86 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "title": "Authelia API documentation" }, "dependencies": { + "@duosecurity/duo_api": "^1.2.0", "ajv": "^6.3.0", "bluebird": "^3.5.0", "body-parser": "^1.15.2", diff --git a/scripts/authelia-scripts-bootstrap b/scripts/authelia-scripts-bootstrap index d2832c21..b4ec915f 100755 --- a/scripts/authelia-scripts-bootstrap +++ b/scripts/authelia-scripts-bootstrap @@ -7,6 +7,9 @@ async function main() { console.log('Build authelia-example-backend docker image.') await exec('docker build -t authelia-example-backend example/compose/nginx/backend'); + console.log('Build authelia-duo-api docker image.') + await exec('docker build -t authelia-duo-api example/compose/duo-api'); + if (!fs.existsSync('/tmp/kind')) { console.log('Install Kind for spawning a Kubernetes cluster.'); await exec('wget https://github.com/clems4ever/kind/releases/download/0.1.0-cmic1/kind-linux-amd64 -O /tmp/kind && chmod +x /tmp/kind'); diff --git a/server/src/lib/Server.ts b/server/src/lib/Server.ts index c36854d1..346965c7 100644 --- a/server/src/lib/Server.ts +++ b/server/src/lib/Server.ts @@ -40,6 +40,9 @@ export default class Server { displayableConfiguration.notifier.email.password = STARS; if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp) displayableConfiguration.notifier.smtp.password = STARS; + if (displayableConfiguration.duo_api) { + displayableConfiguration.duo_api.secret_key = STARS; + } this.globalLogger.debug("User configuration is %s", JSON.stringify(displayableConfiguration, undefined, 2)); diff --git a/server/src/lib/configuration/schema/Configuration.ts b/server/src/lib/configuration/schema/Configuration.ts index 8d16a5fb..d0584732 100644 --- a/server/src/lib/configuration/schema/Configuration.ts +++ b/server/src/lib/configuration/schema/Configuration.ts @@ -5,6 +5,7 @@ import { RegulationConfiguration, complete as RegulationConfigurationComplete } import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration"; import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration"; import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration"; +import { DuoPushConfiguration } from "./DuoPushConfiguration"; export interface Configuration { access_control?: ACLConfiguration; @@ -17,6 +18,7 @@ export interface Configuration { session?: SessionConfiguration; storage?: StorageConfiguration; totp?: TotpConfiguration; + duo_api?: DuoPushConfiguration; } export function complete( diff --git a/server/src/lib/configuration/schema/DuoPushConfiguration.ts b/server/src/lib/configuration/schema/DuoPushConfiguration.ts new file mode 100644 index 00000000..bede3767 --- /dev/null +++ b/server/src/lib/configuration/schema/DuoPushConfiguration.ts @@ -0,0 +1,6 @@ + +export interface DuoPushConfiguration { + hostname: string; + integration_key: string; + secret_key: string; +} \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/duo-push/Post.spec.ts b/server/src/lib/routes/secondfactor/duo-push/Post.spec.ts new file mode 100644 index 00000000..cd3fa1e4 --- /dev/null +++ b/server/src/lib/routes/secondfactor/duo-push/Post.spec.ts @@ -0,0 +1,109 @@ +import * as Express from "express"; +import { ServerVariables } from "../../../ServerVariables"; +import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../ServerVariablesMockBuilder.spec"; +import * as ExpressMock from "../../../stubs/express.spec"; +import Post from "./Post"; +import * as Sinon from "sinon"; +import * as Assert from "assert"; +import { Level } from "../../../authentication/Level"; +const DuoApi = require("@duosecurity/duo_api"); + + +describe("routes/secondfactor/duo-push/Post", function() { + let vars: ServerVariables; + let mocks: ServerVariablesMock; + let req: Express.Request; + let res: ExpressMock.ResponseMock; + + beforeEach(function() { + const sv = ServerVariablesMockBuilder.build(); + vars = sv.variables; + mocks = sv.mocks; + + vars.config.duo_api = { + hostname: 'abc', + integration_key: 'xyz', + secret_key: 'secret', + } + + req = ExpressMock.RequestMock(); + res = ExpressMock.ResponseMock(); + }) + + it("should raise authentication level of user", async function() { + const mock = Sinon.stub(DuoApi, "Client"); + mock.returns({ + jsonApiCall: Sinon.stub().yields({response: {result: 'allow'}}) + }); + req.session.auth = { + userid: 'john' + } + + Assert.equal(req.session.auth.authentication_level, undefined); + await Post(vars)(req, res as any); + Assert(res.status.calledWith(204)); + Assert(res.send.calledWith()); + Assert.equal(req.session.auth.authentication_level, Level.TWO_FACTOR); + mock.restore(); + }); + + it("should block if no duo API is configured", async function() { + const mock = Sinon.stub(DuoApi, "Client"); + mock.returns({ + jsonApiCall: Sinon.stub().yields({response: {result: 'allow'}}) + }); + req.session.auth = { + userid: 'john' + } + vars.config.duo_api = undefined; + + Assert.equal(req.session.auth.authentication_level, undefined); + await Post(vars)(req, res as any); + Assert(res.status.calledWith(200)); + Assert(res.send.calledWith({error: 'Operation failed.'})); + Assert.equal(req.session.auth.authentication_level, undefined); + mock.restore(); + }); + + it("should block if user denied notification", async function() { + const mock = Sinon.stub(DuoApi, "Client"); + mock.returns({ + jsonApiCall: Sinon.stub().yields({response: {result: 'deny'}}) + }); + req.session.auth = { + userid: 'john' + } + + Assert.equal(req.session.auth.authentication_level, undefined); + await Post(vars)(req, res as any); + Assert(res.status.calledWith(200)); + Assert(res.send.calledWith({error: 'Operation failed.'})); + Assert.equal(req.session.auth.authentication_level, undefined); + mock.restore(); + }); + + it("should block if duo push service is down", function() { + const mock = Sinon.stub(DuoApi, "Client"); + const timerMock = Sinon.useFakeTimers(); + mock.returns({ + jsonApiCall: Sinon.stub() + }); + req.session.auth = { + userid: 'john' + } + + Assert.equal(req.session.auth.authentication_level, undefined); + const promise = Post(vars)(req, res as any) + .then(() => { + Assert(res.status.calledWith(200)); + Assert(res.send.calledWith({error: 'Operation failed.'})); + Assert.equal(req.session.auth.authentication_level, undefined); + + mock.restore(); + timerMock.restore(); + }); + // Move forward in time to timeout. + timerMock.tick(62000); + return promise; + }); +}); \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/duo-push/Post.ts b/server/src/lib/routes/secondfactor/duo-push/Post.ts new file mode 100644 index 00000000..f3891a8e --- /dev/null +++ b/server/src/lib/routes/secondfactor/duo-push/Post.ts @@ -0,0 +1,51 @@ +import * as Express from "express"; +import { ServerVariables } from "../../../ServerVariables"; +import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler"; +import * as ErrorReplies from "../../../ErrorReplies"; +import * as UserMessage from "../../../../../../shared/UserMessages"; +import redirect from "../redirect"; +import { Level } from "../../../authentication/Level"; +import { DuoPushConfiguration } from "../../../configuration/schema/DuoPushConfiguration"; +const DuoApi = require("@duosecurity/duo_api"); + +interface DuoResponse { + response: { + result: "allow" | "deny"; + status: "allow" | "deny" | "fraud"; + status_msg: string; + }; + stat: "OK" | "FAIL"; +} + +function triggerAuth(username: string, config: DuoPushConfiguration): Promise { + return new Promise((resolve, reject) => { + const client = new DuoApi.Client(config.integration_key, config.secret_key, config.hostname); + const timer = setTimeout(() => reject(new Error("Call to duo push API timed out.")), 60000); + client.jsonApiCall("POST", "/auth/v2/auth", { username, factor: "push", device: "auto" }, (data: DuoResponse) => { + clearTimeout(timer); + resolve(data); + }); + }); +} + + +export default function(vars: ServerVariables) { + return async function(req: Express.Request, res: Express.Response) { + try { + if (!vars.config.duo_api) { + throw new Error("Duo Push Notification is not configured."); + } + + const authSession = AuthenticationSessionHandler.get(req, vars.logger); + const authRes = await triggerAuth(authSession.userid, vars.config.duo_api); + if (authRes.response.result !== "allow") { + throw new Error("User denied access."); + } + vars.logger.debug(req, "Access allowed by user via Duo Push."); + authSession.authentication_level = Level.TWO_FACTOR; + await redirect(vars)(req, res); + } catch (err) { + ErrorReplies.replyWithError200(req, res, vars.logger, UserMessage.OPERATION_FAILED)(err); + } + }; +} \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/preferences/Get.spec.ts b/server/src/lib/routes/secondfactor/preferences/Get.spec.ts index 08900347..7767672c 100644 --- a/server/src/lib/routes/secondfactor/preferences/Get.spec.ts +++ b/server/src/lib/routes/secondfactor/preferences/Get.spec.ts @@ -6,7 +6,7 @@ import * as ExpressMock from "../../../stubs/express.spec"; import Get from "./Get"; import * as Assert from "assert"; -describe("routes/secondfactor/Get", function() { +describe("routes/secondfactor/preferences/Get", function() { let vars: ServerVariables; let mocks: ServerVariablesMock; let req: Express.Request; diff --git a/server/src/lib/routes/secondfactor/preferences/Post.spec.ts b/server/src/lib/routes/secondfactor/preferences/Post.spec.ts index 2d55e5d3..da2b71e8 100644 --- a/server/src/lib/routes/secondfactor/preferences/Post.spec.ts +++ b/server/src/lib/routes/secondfactor/preferences/Post.spec.ts @@ -6,7 +6,7 @@ import * as ExpressMock from "../../../stubs/express.spec"; import Post from "./Post"; import * as Assert from "assert"; -describe("routes/secondfactor/Post", function() { +describe("routes/secondfactor/preferences/Post", function() { let vars: ServerVariables; let mocks: ServerVariablesMock; let req: Express.Request; diff --git a/server/src/lib/routes/verify/CheckAuthorizations.ts b/server/src/lib/routes/verify/CheckAuthorizations.ts index 0374a1f4..95984225 100644 --- a/server/src/lib/routes/verify/CheckAuthorizations.ts +++ b/server/src/lib/routes/verify/CheckAuthorizations.ts @@ -36,11 +36,11 @@ export default function ( } else if (user && authorizationLevel == AuthorizationLevel.DENY) { throw new Exceptions.NotAuthorizedError( - Util.format("User %s is not authorized to access %s%s", user, domain, resource)); + Util.format("User %s is not authorized to access %s%s", (user) ? user : "unknown", domain, resource)); } else if (!isAuthorized(authorizationLevel, authenticationLevel)) { throw new Exceptions.NotAuthenticatedError(Util.format( - "User '%s' is not sufficiently authorized to access %s%s.", user, domain, resource)); + "User '%s' is not sufficiently authorized to access %s%s.", (user) ? user : "unknown", domain, resource)); } return authorizationLevel; } \ No newline at end of file diff --git a/server/src/lib/web_server/RestApi.ts b/server/src/lib/web_server/RestApi.ts index 9d3d9f75..04a15b5b 100644 --- a/server/src/lib/web_server/RestApi.ts +++ b/server/src/lib/web_server/RestApi.ts @@ -1,6 +1,7 @@ import * as Express from "express"; import SecondFactorPreferencesGet from "../routes/secondfactor/preferences/Get"; import SecondFactorPreferencesPost from "../routes/secondfactor/preferences/Post"; +import SecondFactorDuoPushPost from "../routes/secondfactor/duo-push/Post"; import FirstFactorPost = require("../routes/firstfactor/post"); import LogoutPost from "../routes/logout/post"; @@ -102,6 +103,12 @@ export class RestApi { RequireValidatedFirstFactor.middleware(vars.logger), SecondFactorPreferencesPost(vars)); + if (vars.config.duo_api) { + app.post(Endpoints.SECOND_FACTOR_DUO_PUSH_POST, + RequireValidatedFirstFactor.middleware(vars.logger), + SecondFactorDuoPushPost(vars)); + } + setupTotp(app, vars); setupU2f(app, vars); setupResetPassword(app, vars); diff --git a/shared/api.ts b/shared/api.ts index 055def7a..89023277 100644 --- a/shared/api.ts +++ b/shared/api.ts @@ -107,6 +107,21 @@ export const SECOND_FACTOR_U2F_SIGN_REQUEST_GET = "/api/u2f/sign_request"; */ export const SECOND_FACTOR_TOTP_POST = "/api/totp"; +/** + * @api {post} /api/duo-push Complete Duo Push Factor + * @apiName ValidateDuoPushSecondFactor + * @apiGroup DuoPush + * @apiVersion 1.0.0 + * @apiUse UserSession + * @apiUse InternalError + * + * @apiSuccess (Success 302) Redirect to the URL that has been stored during last call to /api/verify. + * @apiError (Error 401) {none} error TOTP token is invalid. + * + * @apiDescription Verify TOTP token. The user is authenticated upon success. + */ +export const SECOND_FACTOR_DUO_PUSH_POST = "/api/duo-push"; + /** * @api {get} /api/secondfactor/u2f/identity/start Start U2F registration identity validation diff --git a/test/helpers/assertions/VerifyHasAppeared.ts b/test/helpers/assertions/VerifyHasAppeared.ts new file mode 100644 index 00000000..fea58cc8 --- /dev/null +++ b/test/helpers/assertions/VerifyHasAppeared.ts @@ -0,0 +1,5 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; + +export default async function(driver: WebDriver, locator: SeleniumWebDriver.Locator, timeout: number = 5000) { + await driver.wait(SeleniumWebDriver.until.elementLocated(locator), timeout); +} \ No newline at end of file diff --git a/test/helpers/context/AutheliaServerWithHotReload.ts b/test/helpers/context/AutheliaServerWithHotReload.ts index edcb5b1c..2a8ad315 100644 --- a/test/helpers/context/AutheliaServerWithHotReload.ts +++ b/test/helpers/context/AutheliaServerWithHotReload.ts @@ -17,8 +17,10 @@ class AutheliaServerWithHotReload implements AutheliaServerInterface { constructor(configPath: string, watchedPaths: string[]) { this.configPath = configPath; - this.watcher = Chokidar.watch(['server', 'shared/**/*.ts', 'node_modules', - this.AUTHELIA_INTERRUPT_FILENAME, configPath].concat(watchedPaths), { + const pathsToReload = ['server', 'shared/**/*.ts', 'node_modules', + this.AUTHELIA_INTERRUPT_FILENAME, configPath].concat(watchedPaths); + console.log("Authelia will reload on changes of files or directories in " + pathsToReload.join(', ')); + this.watcher = Chokidar.watch(pathsToReload, { persistent: true, ignoreInitial: true, }); @@ -29,7 +31,10 @@ class AutheliaServerWithHotReload implements AutheliaServerInterface { await exec('./node_modules/.bin/tslint -c server/tslint.json -p server/tsconfig.json') this.serverProcess = ChildProcess.spawn('./node_modules/.bin/ts-node', ['-P', './server/tsconfig.json', './server/src/index.ts', this.configPath], { - env: {...process.env}, + env: { + ...process.env, + NODE_TLS_REJECT_UNAUTHORIZED: 0, + }, }); this.serverProcess.stdout.pipe(process.stdout); this.serverProcess.stderr.pipe(process.stderr); diff --git a/test/suites/basic/config.yml b/test/suites/basic/config.yml index 172aa3ef..534f2568 100644 --- a/test/suites/basic/config.yml +++ b/test/suites/basic/config.yml @@ -86,12 +86,6 @@ regulation: # The length of time before a banned user can login again. ban_time: 900 -# Default redirection URL -# -# Note: this parameter is optional. If not provided, user won't -# be redirected upon successful authentication. -#default_redirection_url: https://authelia.example.domain - notifier: # For testing purpose, notifications can be sent in a file # filesystem: diff --git a/test/suites/duo-push/README.md b/test/suites/duo-push/README.md new file mode 100644 index 00000000..d8d83dd3 --- /dev/null +++ b/test/suites/duo-push/README.md @@ -0,0 +1,12 @@ +# Duo Push Notification suite + +This suite has been created to test Authelia against the Duo API for push notifications. +It allows a user to validate second factor with a mobile phone. + +## Components + +Authelia, nginx, Duo fake API + +## Tests + +Test allowed and denied access via push notifications. \ No newline at end of file diff --git a/test/suites/duo-push/config.yml b/test/suites/duo-push/config.yml new file mode 100644 index 00000000..c3320d65 --- /dev/null +++ b/test/suites/duo-push/config.yml @@ -0,0 +1,116 @@ +############################################################### +# Authelia minimal configuration # +############################################################### + +port: 9091 + +logs_level: debug + +default_redirection_url: https://home.example.com:8080/ + +authentication_backend: + file: + path: ./test/suites/basic/users_database.test.yml + +session: + secret: unsecure_session_secret + domain: example.com + expiration: 3600000 # 1 hour + inactivity: 300000 # 5 minutes + +# Configuration of the storage backend used to store data and secrets. i.e. totp data +storage: + local: + path: /tmp/authelia/db + +# TOTP Issuer Name +# +# This will be the issuer name displayed in Google Authenticator +# See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format for more info on issuer names +totp: + issuer: example.com + +# The Duo Push Notification API configuration +duo_api: + hostname: duo.example.com + integration_key: ABCDEFGHIJKL + secret_key: abcdefghijklmnopqrstuvwxyz123456789 + +# Access Control +# +# Access control is a set of rules you can use to restrict user access to certain +# resources. +access_control: + # Default policy can either be `bypass`, `one_factor`, `two_factor` or `deny`. + default_policy: deny + + rules: + - domain: singlefactor.example.com + policy: one_factor + + - domain: public.example.com + policy: bypass + + - domain: secure.example.com + policy: two_factor + + - domain: '*.example.com' + subject: "group:admins" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/john/.*$' + subject: "user:john" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/harry/.*$' + subject: "user:harry" + policy: two_factor + + - domain: '*.mail.example.com' + subject: "user:bob" + policy: two_factor + + - domain: dev.example.com + resources: + - '^/users/bob/.*$' + subject: "user:bob" + policy: two_factor + + +# Configuration of the authentication regulation mechanism. +regulation: + # Set it to 0 to disable max_retries. + max_retries: 3 + + # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. + find_time: 300 + + # The length of time before a banned user can login again. + ban_time: 900 + +notifier: + # For testing purpose, notifications can be sent in a file + # filesystem: + # filename: /tmp/authelia/notification.txt + + # Use your email account to send the notifications. You can use an app password. + # List of valid services can be found here: https://nodemailer.com/smtp/well-known/ + ## email: + ## username: user@example.com + ## password: yourpassword + ## sender: admin@example.com + ## service: gmail + + # Use a SMTP server for sending notifications + smtp: + username: test + password: password + secure: false + host: 127.0.0.1 + port: 1025 + sender: admin@example.com + diff --git a/test/suites/duo-push/environment.ts b/test/suites/duo-push/environment.ts new file mode 100644 index 00000000..e52b040a --- /dev/null +++ b/test/suites/duo-push/environment.ts @@ -0,0 +1,36 @@ +import fs from 'fs'; +import { exec } from "../../helpers/utils/exec"; +import AutheliaServer from "../../helpers/context/AutheliaServer"; +import DockerEnvironment from "../../helpers/context/DockerEnvironment"; + +const autheliaServer = new AutheliaServer(__dirname + '/config.yml', [__dirname + '/users_database.yml']); +const dockerEnv = new DockerEnvironment([ + 'docker-compose.yml', + 'example/compose/nginx/backend/docker-compose.yml', + 'example/compose/nginx/portal/docker-compose.yml', + 'example/compose/duo-api/docker-compose.yml', +]) + +async function setup() { + await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); + await exec('mkdir -p /tmp/authelia/db'); + await exec('./example/compose/nginx/portal/render.js ' + (fs.existsSync('.suite') ? '': '--production')); + await dockerEnv.start(); + await autheliaServer.start(); +} + +async function teardown() { + await autheliaServer.stop(); + await dockerEnv.stop(); + await exec('rm -rf /tmp/authelia/db'); +} + +const setup_timeout = 30000; +const teardown_timeout = 30000; + +export { + setup, + setup_timeout, + teardown, + teardown_timeout +}; \ No newline at end of file diff --git a/test/suites/duo-push/scenarii/DuoPushNotification.ts b/test/suites/duo-push/scenarii/DuoPushNotification.ts new file mode 100644 index 00000000..ab8875d2 --- /dev/null +++ b/test/suites/duo-push/scenarii/DuoPushNotification.ts @@ -0,0 +1,64 @@ +import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import LoginAs from "../../../helpers/LoginAs"; +import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; +import ClickOnLink from "../../../helpers/ClickOnLink"; +import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUseAnotherMethodView"; +import ClickOnButton from "../../../helpers/behaviors/ClickOnButton"; +import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved"; +import Request from 'request-promise'; +import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; +import VerifyHasAppeared from "../../../helpers/assertions/VerifyHasAppeared"; +import SeleniumWebDriver from "selenium-webdriver"; +import VisitPage from "../../../helpers/VisitPage"; + + +export default function() { + before(async function() { + this.driver = await StartDriver(); + }); + + after(async function () { + await StopDriver(this.driver); + }); + + describe('Allow access', function() { + before(async function() { + // Configure the fake API to return allowing response. + await Request('https://duo.example.com/allow', {method: 'POST'}); + }); + + it('should grant access with Duo API', async function() { + await LoginAs(this.driver, "john", "password", "https://secure.example.com:8080/secret.html"); + await VerifyIsSecondFactorStage(this.driver); + + await ClickOnLink(this.driver, 'Use another method'); + await VerifyIsUseAnotherMethodView(this.driver); + await ClickOnButton(this.driver, 'Duo Push Notification'); + + await VerifyUrlIs(this.driver, "https://secure.example.com:8080/secret.html"); + await VerifySecretObserved(this.driver); + + await VisitPage(this.driver, "https://login.example.com:8080/#/"); + await ClickOnButton(this.driver, "Logout"); + }); + }); + + describe('Deny access', function() { + before(async function() { + // Configure the fake API to return denying response. + await Request('https://duo.example.com/deny', {method: 'POST'}); + }); + + it('should grant access with Duo API', async function() { + await LoginAs(this.driver, "john", "password", "https://secure.example.com:8080/secret.html"); + await VerifyIsSecondFactorStage(this.driver); + + await ClickOnLink(this.driver, 'Use another method'); + await VerifyIsUseAnotherMethodView(this.driver); + await ClickOnButton(this.driver, 'Duo Push Notification'); + + // The retry button appeared. + await VerifyHasAppeared(this.driver, SeleniumWebDriver.By.tagName("button")); + }); + }); +} \ No newline at end of file diff --git a/test/suites/duo-push/test.ts b/test/suites/duo-push/test.ts new file mode 100644 index 00000000..1dab3d4d --- /dev/null +++ b/test/suites/duo-push/test.ts @@ -0,0 +1,16 @@ +import AutheliaSuite from "../../helpers/context/AutheliaSuite"; +import { exec } from '../../helpers/utils/exec'; +import DuoPushNotification from "./scenarii/DuoPushNotification"; + +// required to query duo-api over https +process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; + +AutheliaSuite(__dirname, function() { + this.timeout(10000); + + beforeEach(async function() { + await exec(`cp ${__dirname}/users_database.yml ${__dirname}/users_database.test.yml`); + }); + + describe("Duo Push Notication", DuoPushNotification); +}); \ No newline at end of file diff --git a/test/suites/duo-push/users_database.yml b/test/suites/duo-push/users_database.yml new file mode 100644 index 00000000..6fe7a384 --- /dev/null +++ b/test/suites/duo-push/users_database.yml @@ -0,0 +1,29 @@ +############################################################### +# Users Database # +############################################################### + +# This file can be used if you do not have an LDAP set up. + +# List of users +users: + john: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: john.doe@authelia.com + groups: + - admins + - dev + + harry: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: harry.potter@authelia.com + groups: [] + + bob: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: bob.dylan@authelia.com + groups: + - dev + + james: + password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/" + email: james.dean@authelia.com \ No newline at end of file From ff88ad354f334e255236a87f45473d93baaf97ee Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 16:37:31 +0100 Subject: [PATCH 2/7] Install /etc/hosts entries from bootstrap script. This allows to add an entry which is not pointing to localhost but to a docker container in the Travis virtual env. --- .travis.yml | 15 +------- bootstrap.sh | 19 ----------- scripts/authelia-scripts-bootstrap | 55 +++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/.travis.yml b/.travis.yml index 681936a5..4d922109 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js +required: sudo node_js: - '9' services: @@ -12,20 +13,6 @@ addons: packages: - libgif-dev - google-chrome-stable - hosts: - - admin.example.com - - login.example.com - - singlefactor.example.com - - dev.example.com - - home.example.com - - mx1.mail.example.com - - mx2.mail.example.com - - public.example.com - - secure.example.com - - authelia.example.com - - admin.example.com - - mail.example.com - - duo.example.com before_script: - export DISPLAY=:99.0 diff --git a/bootstrap.sh b/bootstrap.sh index c3eee60e..8d5fc04f 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -24,25 +24,6 @@ then return; fi -echo "[BOOTSTRAP] Checking if example.com domain is forwarded to your machine..." -cat /etc/hosts | grep "login.example.com" > /dev/null -if [ $? -ne 0 ]; -then - echo "[ERROR] Please add those lines to /etc/hosts: - -127.0.0.1 home.example.com -127.0.0.1 public.example.com -127.0.0.1 secure.example.com -127.0.0.1 dev.example.com -127.0.0.1 admin.example.com -127.0.0.1 mx1.mail.example.com -127.0.0.1 mx2.mail.example.com -127.0.0.1 singlefactor.example.com -127.0.0.1 login.example.com -192.168.240.100 duo.example.com" - return; -fi - echo "[BOOTSTRAP] Running additional bootstrap steps..." authelia-scripts bootstrap diff --git a/scripts/authelia-scripts-bootstrap b/scripts/authelia-scripts-bootstrap index b4ec915f..2e58fb58 100755 --- a/scripts/authelia-scripts-bootstrap +++ b/scripts/authelia-scripts-bootstrap @@ -3,12 +3,59 @@ var { exec } = require('./utils/exec'); var fs = require('fs'); -async function main() { +async function buildDockerImages() { + console.log("[BOOTSTRAP] Building required Docker images..."); + console.log('Build authelia-example-backend docker image.') await exec('docker build -t authelia-example-backend example/compose/nginx/backend'); console.log('Build authelia-duo-api docker image.') await exec('docker build -t authelia-duo-api example/compose/duo-api'); +} + +async function checkHostsFile() { + async function checkAndFixEntry(entries, domain, ip) { + const foundEntry = entries.filter(l => l[1] == domain); + if (foundEntry.length > 0) { + if (foundEntry[0][0] == ip) { + // The entry exists and is correct. + return; + } + else { + // We need to remove the entry and replace it. + console.log(`Update entry for ${domain}.`); + await exec(`cat /etc/hosts | grep -v "${domain}" | /usr/bin/sudo tee /etc/hosts > /dev/null`); + await exec(`echo "${ip} ${domain}" | /usr/bin/sudo tee -a /etc/hosts > /dev/null`); + } + } + else { + // We need to add the new entry. + console.log(`Add entry for ${domain}.`); + await exec(`echo "${ip} ${domain}" | /usr/bin/sudo tee -a /etc/hosts > /dev/null`); + } + } + + console.log("[BOOTSTRAP] Checking if example.com domain is forwarded to your machine..."); + const actualEntries = fs.readFileSync("/etc/hosts").toString("utf-8") + .split("\n").filter(l => l !== '').map(l => l.split(" ").filter(w => w !== '')); + + await checkAndFixEntry(actualEntries, 'login.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'admin.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'singlefactor.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'dev.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'home.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'mx1.mail.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'mx2.mail.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'public.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'secure.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'authelia.example.com', '127.0.0.1'); + await checkAndFixEntry(actualEntries, 'mail.example.com', '127.0.0.1'); + + await checkAndFixEntry(actualEntries, 'duo.example.com', '192.168.240.100'); +} + +async function checkKubernetesDependencies() { + console.log("[BOOTSTRAP] Checking Kubernetes tools in /tmp to allow testing a Kube cluster... (no junk installed on host)"); if (!fs.existsSync('/tmp/kind')) { console.log('Install Kind for spawning a Kubernetes cluster.'); @@ -21,6 +68,12 @@ async function main() { } } +async function main() { + await checkHostsFile(); + await buildDockerImages(); + await checkKubernetesDependencies(); +} + main().catch((err) => { console.error(err); process.exit(1); From 4eaafb711561e8774ff8b8f0f7d6e89d42afbfe0 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 18:45:32 +0100 Subject: [PATCH 3/7] Update the documentation to include information on Duo. --- README.md | 18 ++++--- config.template.yml | 2 +- docs/2factor/duo-push-notifications.md | 47 +++++++++++++++++++ docs/2factor/security-key.md | 40 ++++++++++++++++ docs/2factor/time-based-one-time-password.md | 29 ++++++++++++ docs/features.md | 37 +++------------ images/2factor_duo.png | Bin 0 -> 19030 bytes images/2factor_totp.png | Bin 0 -> 18316 bytes images/2factor_u2f.png | Bin 0 -> 20848 bytes images/duo-push-1.jpg | Bin 0 -> 51976 bytes images/duo-push-2.png | Bin 0 -> 53608 bytes images/first_factor.png | Bin 19471 -> 16479 bytes images/use-another-method.png | Bin 0 -> 26999 bytes test/suites/.gitignore | 4 +- 14 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 docs/2factor/duo-push-notifications.md create mode 100644 docs/2factor/security-key.md create mode 100644 docs/2factor/time-based-one-time-password.md create mode 100644 images/2factor_duo.png create mode 100644 images/2factor_totp.png create mode 100644 images/2factor_u2f.png create mode 100644 images/duo-push-1.jpg create mode 100644 images/duo-push-2.png create mode 100644 images/use-another-method.png diff --git a/README.md b/README.md index d98936e3..ad4e1861 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,9 @@ [![Gitter](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/authelia/general?utm_source=share-link&utm_medium=link&utm_campaign=share-link) [![Donate](https://img.shields.io/badge/Donate-PayPal-orange.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=clement%2emichaud34%40gmail%2ecom&lc=FR&item_name=Authelia¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted) -**Authelia** is an open-source authentication and authorization providing - 2-factor authentication and single sign-on (SSO) for your applications. +**Authelia** is an open-source authentication and authorization server +providing 2-factor authentication and single sign-on (SSO) for your +applications. It acts as a companion of reverse proxies by handling authentication and authorization requests. @@ -20,15 +21,17 @@ for specific services in only few seconds.

- +

## Features summary Here is the list of the main available features: -* **[U2F] - Universal 2-Factor -** support with [Yubikey]. -* **[TOTP] - Time-Base One Time password -** support with [Google Authenticator]. +* Several kind of second factor: + * **[Security Key (U2F)](./docs/2factor/security-key.md)** support with [Yubikey]. + * **[Time-based One-Time password](./docs/2factor/time-based-one-time-password.md)** support with [Google Authenticator]. + * **[Mobile Push Notifications](./docs/2factor/duo-push-notifications.md)** with [Duo](https://duo.com/). * Password reset with identity verification using email. * Single-factor only authentication method available. * Access restriction after too many authentication attempts. @@ -43,6 +46,7 @@ For more details about the features, follow [Features](./docs/features.md). You can start off with + git clone https://github.com/clems4ever/authelia.git source bootstrap.sh If you want to go further, please read [Getting Started](./docs/getting-started.md). @@ -113,8 +117,8 @@ Wanna see more features? Then fuel us with a few beers! [MIT License]: https://opensource.org/licenses/MIT [TOTP]: https://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm -[U2F]: https://www.yubico.com/about/background/fido/ +[Security Key]: https://www.yubico.com/about/background/fido/ [Yubikey]: https://www.yubico.com/products/yubikey-hardware/yubikey4/ [auth_request]: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html [Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en -[config.template.yml]: https://github.com/clems4ever/authelia/blob/master/config.template.yml +[config.template.yml]: https://github.com/clems4ever/authelia/blob/master/config.template.yml \ No newline at end of file diff --git a/config.template.yml b/config.template.yml index 0587fa53..727858bf 100644 --- a/config.template.yml +++ b/config.template.yml @@ -34,7 +34,7 @@ totp: # Parameters used to contact the Duo API. Those are generated when you protect an application # of type "Partner Auth API" in the management panel. duo_api: - hostname: api-123456789.duosecurity.com + hostname: api-123456789.example.com integration_key: ABCDEF secret_key: 1234567890abcdefghifjkl diff --git a/docs/2factor/duo-push-notifications.md b/docs/2factor/duo-push-notifications.md new file mode 100644 index 00000000..7b2eaf59 --- /dev/null +++ b/docs/2factor/duo-push-notifications.md @@ -0,0 +1,47 @@ +# Duo Push Notification + +Using mobile push notifications is becoming the new trendy way to validate +the second factor of a 2FA authentication process. [Duo](https://duo.com/) is offering an API +to integrate this kind validation and **Authelia** leverages this mechanism +so that you can simply push a button on your smartphone to be securely granted +access to your services. + +

+ +

+ +In order to use this feature, you should first create a free account on Duo +(up to 10 users), create a user account and attach it a mobile device. The name +of the user must match the name of the user in your internal database. +Then, click on *Applications* and *Protect an Application*. Then select the option +called *Partner Auth API*. This will generate an integration key, a secret key and +a hostname. You can set the name of the application to **Authelia** and then you +must add the generated information to your configuration as: + + duo_api: + hostname: api-123456789.example.com + integration_key: ABCDEF + secret_key: 1234567890abcdefghifjkl + +This can be seen in [config.template.yml](../../config.template.yml) file. + +When selecting *Duo Push Notification* at the second factor stage, you will +automatically receive a push notification on your phone to grant or deny access. + +

+ + +

+ +## Limitations + +Users must be enrolled via the Duo Admin panel, they cannot enroll a device from +**Authelia** yet. + + +## FAQ + +### Why don't I have access to the *Duo Push Notification* option? + +It's likely that you have not configured **Authelia** correctly. Please read this +documentation again and be sure you had a look at [config.template.yml](../../config.template.yml). \ No newline at end of file diff --git a/docs/2factor/security-key.md b/docs/2factor/security-key.md new file mode 100644 index 00000000..c1f748cf --- /dev/null +++ b/docs/2factor/security-key.md @@ -0,0 +1,40 @@ +# Security Keys (U2F) + +**Authelia** also offers authentication using Security Keys supporting U2F +like [Yubikey](Yubikey) USB devices. U2F is one of the most secure +authentication protocol and is already available for Google, Facebook, Github +accounts and more. + +The protocol requires your security key being enrolled before authenticating. + +

+ +

+ +To do so, select the *Security Key* method in the second factor page and click +on the *register new device* link. This will send a link to the +user email address. This e-mail will likely be sent to https://mail.example.com:8080/ +if you're testing Authelia and you've not configured anything. + +Confirm your identity by clicking on **Continue** and you'll be asked to +touch the token of your security key to enroll. + +

+ +

+ +Upon successful registration, you can authenticate using your security key by simply +touching the token again. + +Easy, right?! + +## FAQ + +### Why don't I have access to the *Security Key* option? + +U2F protocol is a new protocol that is only supported by recent browser +and must even be enabled on some of them like Firefox. Please be sure +your browser supports U2F and that the feature is enabled to make the +option available in **Authelia**. + +[Yubikey]: https://www.yubico.com/products/yubikey-hardware/yubikey4/ diff --git a/docs/2factor/time-based-one-time-password.md b/docs/2factor/time-based-one-time-password.md new file mode 100644 index 00000000..2ef9bbf7 --- /dev/null +++ b/docs/2factor/time-based-one-time-password.md @@ -0,0 +1,29 @@ +# One-Time Passwords + +In **Authelia**, your users can use [Google Authenticator] for generating unique +tokens that they can use to pass the second factor. + +

+ +

+ +Select the *One-Time Password method* and click on the *register new device* link. +Then, check the email sent by **Authelia** to your email address +to validate your identity. If you're testing **Authelia**, it's likely +that this e-mail has been sent to https://mail.example.com:8080/ + +Confirm your identity by clicking on **Continue** and you'll get redirected +on a page where your secret will be displayed as QRCode and in Base32 formats. + +

+ +

+ +You can use [Google Authenticator] to store it. + +From now on, you'll get generated +tokens from your phone that you can use to validate the second factor in **Authelia**. + + + +[Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en \ No newline at end of file diff --git a/docs/features.md b/docs/features.md index b601cda9..f811973e 100644 --- a/docs/features.md +++ b/docs/features.md @@ -15,39 +15,16 @@ You can find an example of the configuration of the LDAP backend in

-## Second factor with TOTP +## Second factor -In **Authelia**, you can register a per user TOTP (Time-Based One Time -Password) secret before being being able to authenticate. Click on the -register button and check the email **Authelia** sent to your email address -to validate your identity. +**Authelia** comes with three kind of second factor. -Confirm your identity by clicking on **Continue** and you'll get redirected -on a page where your secret will be displayed in QRCode and Base32 formats. -You can use [Google Authenticator] to store it and get the generated tokens. +* Security keys like [Yubikey]. More info [here](./2factor/security-key.md). +* One-Time Passwords generated by [Google Authenticator]. More info [here](./2factor/time-based-one-time-password.md). +* Duo Push Notifications to use with [Duo mobile application](https://play.google.com/store/apps/details?id=com.duosecurity.duomobile&hl=en) available on Android, iOS and Windows. More info [here](./2factor/duo-push-notifications.md).

- -

- -## Second factor with U2F security keys - -**Authelia** also offers authentication using U2F (Universal 2-Factor) devices -like [Yubikey](Yubikey) USB security keys. U2F is one of the most secure -authentication protocol and is already available for Google, Facebook, Github -accounts and more. - -Like TOTP, U2F requires you register your security key before authenticating. -To do so, click on the register button. This will send a link to the -user email address. -Confirm your identity by clicking on **Continue** and you'll be asked to -touch the token of your device to register. Upon successful registration, -you can authenticate using your U2F device by simply touching the token. - -Easy, right?! - -

- +

## Password reset @@ -96,5 +73,5 @@ Redis key/value store. You can specify your own Redis instance in [basic authentication]: https://en.wikipedia.org/wiki/Basic_access_authentication [config.template.yml]: https://github.com/clems4ever/authelia/blob/master/config.template.yml -[Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en [Yubikey]: https://www.yubico.com/products/yubikey-hardware/yubikey4/ +[Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en \ No newline at end of file diff --git a/images/2factor_duo.png b/images/2factor_duo.png new file mode 100644 index 0000000000000000000000000000000000000000..f68d1b48d127d198d50128fe53e2922550934eac GIT binary patch literal 19030 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

91yeYsxN)yrlZ+ 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

R014!a0XJcZSD+6w0n)oui%o(=k+F0{#Kaa& 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 |oWiOI=_p`CpQ2vqm$Q8TZqYI07sB6qG| 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 t;2P8(BnjaKQNc+-k&+nyr`hK+ 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 literal 0 HcmV?d00001 diff --git a/images/duo-push-2.png b/images/duo-push-2.png new file mode 100644 index 0000000000000000000000000000000000000000..23b5d73e3566756688041340f22d5b864e332904 GIT binary patch literal 53608 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 E2Cs^L(EtDd literal 19471 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 BSk3?d diff --git a/images/use-another-method.png b/images/use-another-method.png new file mode 100644 index 0000000000000000000000000000000000000000..560679b63c04b2e0e79136e037568385df234125 GIT binary patch literal 26999 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 HyTE?~w-7c* literal 0 HcmV?d00001 diff --git a/test/suites/.gitignore b/test/suites/.gitignore index 4df2d1dc..83d1ce3c 100644 --- a/test/suites/.gitignore +++ b/test/suites/.gitignore @@ -1 +1,3 @@ -users_database.test.yml \ No newline at end of file +users_database.test.yml + +private-*/ \ No newline at end of file From d09a307ff8f734a0c7c63c58ace7ac20d35f0624 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 19:28:32 +0100 Subject: [PATCH 4/7] Fix redirection after 2FA method change. Authelia was using links with href="#" that changed the URL when clicked on. Therefore, this commit removes the href property and apply link style to tags without href property. --- client/src/components/SecondFactorForm/SecondFactorForm.tsx | 4 ++-- client/src/components/SecondFactorTOTP/SecondFactorTOTP.tsx | 2 +- client/src/components/SecondFactorU2F/SecondFactorU2F.tsx | 2 +- client/src/index.css | 5 +++++ example/compose/nginx/backend/html/home/index.html | 4 ++-- test/helpers/context/DockerCompose.ts | 4 ++++ test/helpers/context/DockerEnvironment.ts | 4 ++++ test/suites/duo-push/environment.ts | 2 ++ 8 files changed, 21 insertions(+), 6 deletions(-) diff --git a/client/src/components/SecondFactorForm/SecondFactorForm.tsx b/client/src/components/SecondFactorForm/SecondFactorForm.tsx index 111d6021..9e3ea884 100644 --- a/client/src/components/SecondFactorForm/SecondFactorForm.tsx +++ b/client/src/components/SecondFactorForm/SecondFactorForm.tsx @@ -71,7 +71,7 @@ class SecondFactorForm extends Component { private renderUseAnotherMethodLink() { return (

@@ -84,7 +84,7 @@ class SecondFactorForm extends Component {
Hello {this.props.username}
diff --git a/client/src/components/SecondFactorTOTP/SecondFactorTOTP.tsx b/client/src/components/SecondFactorTOTP/SecondFactorTOTP.tsx index a64d3473..ba79ef5d 100644 --- a/client/src/components/SecondFactorTOTP/SecondFactorTOTP.tsx +++ b/client/src/components/SecondFactorTOTP/SecondFactorTOTP.tsx @@ -68,7 +68,7 @@ export default class SecondFactorTOTP extends React.Component { value={this.state.oneTimePassword} />
- Register new device diff --git a/client/src/components/SecondFactorU2F/SecondFactorU2F.tsx b/client/src/components/SecondFactorU2F/SecondFactorU2F.tsx index d58899d0..9ce86cf9 100644 --- a/client/src/components/SecondFactorU2F/SecondFactorU2F.tsx +++ b/client/src/components/SecondFactorU2F/SecondFactorU2F.tsx @@ -41,7 +41,7 @@ export default class SecondFactorU2F extends React.Component {
- Register new device diff --git a/client/src/index.css b/client/src/index.css index 4821f2cc..b3757ca5 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -9,3 +9,8 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } + +a { + text-decoration: underline; + cursor: pointer; +} \ No newline at end of file diff --git a/example/compose/nginx/backend/html/home/index.html b/example/compose/nginx/backend/html/home/index.html index 58b86b9f..c17a53d6 100644 --- a/example/compose/nginx/backend/html/home/index.html +++ b/example/compose/nginx/backend/html/home/index.html @@ -12,10 +12,10 @@ one of the following links to test access control powered by Authelia.
  • - public.example.com / index.html + public.example.com /
  • - secure.example.com / secret.html + secure.example.com / secret.html
  • singlefactor.example.com / secret.html diff --git a/test/helpers/context/DockerCompose.ts b/test/helpers/context/DockerCompose.ts index ddf6c963..0c9bb90f 100644 --- a/test/helpers/context/DockerCompose.ts +++ b/test/helpers/context/DockerCompose.ts @@ -23,6 +23,10 @@ class DockerCompose { async ps() { return Promise.resolve(execSync(this.commandPrefix + ' ps').toString('utf-8')); } + + async logs(service: string) { + await exec(this.commandPrefix + ' logs ' + service) + } } export default DockerCompose; \ No newline at end of file diff --git a/test/helpers/context/DockerEnvironment.ts b/test/helpers/context/DockerEnvironment.ts index 5d3f1e76..fe955ce2 100644 --- a/test/helpers/context/DockerEnvironment.ts +++ b/test/helpers/context/DockerEnvironment.ts @@ -11,6 +11,10 @@ class DockerEnvironment { await this.dockerCompose.up(); } + async logs(service: string) { + await this.dockerCompose.logs(service); + } + async stop() { await this.dockerCompose.down(); } diff --git a/test/suites/duo-push/environment.ts b/test/suites/duo-push/environment.ts index e52b040a..c612370b 100644 --- a/test/suites/duo-push/environment.ts +++ b/test/suites/duo-push/environment.ts @@ -3,6 +3,8 @@ import { exec } from "../../helpers/utils/exec"; import AutheliaServer from "../../helpers/context/AutheliaServer"; import DockerEnvironment from "../../helpers/context/DockerEnvironment"; +process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; + const autheliaServer = new AutheliaServer(__dirname + '/config.yml', [__dirname + '/users_database.yml']); const dockerEnv = new DockerEnvironment([ 'docker-compose.yml', From a717b965c167c405599c31744b6af6519153534f Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 22:19:43 +0100 Subject: [PATCH 5/7] Display only available 2FA methods. For instance Duo Push Notification method is not displayed if the API is not configured. --- .../src/behaviors/GetAvailable2faMethods.ts | 13 ++++ .../SecondFactorForm/SecondFactorForm.tsx | 20 +----- .../UseAnotherMethod/UseAnotherMethod.tsx | 66 +++++++++++++++++++ .../SecondFactorForm/SecondFactorForm.ts | 3 - .../UseAnotherMethod/UseAnotherMethod.tsx | 32 +++++++++ .../reducers/Portal/SecondFactor/actions.ts | 20 +++++- .../reducers/Portal/SecondFactor/reducer.ts | 28 ++++++++ client/src/reducers/constants.ts | 4 ++ client/src/services/AutheliaService.ts | 4 ++ .../routes/secondfactor/available/Get.spec.ts | 36 ++++++++++ .../lib/routes/secondfactor/available/Get.ts | 14 ++++ server/src/lib/web_server/RestApi.ts | 5 ++ shared/api.ts | 10 +++ .../assertions/VerifyButtonDoesNotExist.ts | 12 ++++ test/helpers/assertions/VerifyButtonExists.ts | 11 ++++ .../assertions/VerifyElementDoesNotExist.ts | 13 ++++ .../helpers/assertions/VerifyElementExists.ts | 13 ++++ test/suites/basic/scenarii/NoDuoPushOption.ts | 33 ++++++++++ test/suites/basic/test.ts | 2 + 19 files changed, 317 insertions(+), 22 deletions(-) create mode 100644 client/src/behaviors/GetAvailable2faMethods.ts create mode 100644 client/src/components/UseAnotherMethod/UseAnotherMethod.tsx create mode 100644 client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx create mode 100644 server/src/lib/routes/secondfactor/available/Get.spec.ts create mode 100644 server/src/lib/routes/secondfactor/available/Get.ts create mode 100644 test/helpers/assertions/VerifyButtonDoesNotExist.ts create mode 100644 test/helpers/assertions/VerifyButtonExists.ts create mode 100644 test/helpers/assertions/VerifyElementDoesNotExist.ts create mode 100644 test/helpers/assertions/VerifyElementExists.ts create mode 100644 test/suites/basic/scenarii/NoDuoPushOption.ts diff --git a/client/src/behaviors/GetAvailable2faMethods.ts b/client/src/behaviors/GetAvailable2faMethods.ts new file mode 100644 index 00000000..431dfde1 --- /dev/null +++ b/client/src/behaviors/GetAvailable2faMethods.ts @@ -0,0 +1,13 @@ +import { Dispatch } from "redux"; +import AutheliaService from "../services/AutheliaService"; +import { getAvailbleMethods, getAvailbleMethodsSuccess, getAvailbleMethodsFailure } from "../reducers/Portal/SecondFactor/actions"; + +export default async function(dispatch: Dispatch) { + dispatch(getAvailbleMethods()); + try { + const methods = await AutheliaService.getAvailable2faMethods(); + dispatch(getAvailbleMethodsSuccess(methods)); + } catch (err) { + dispatch(getAvailbleMethodsFailure(err.message)) + } +} \ No newline at end of file diff --git a/client/src/components/SecondFactorForm/SecondFactorForm.tsx b/client/src/components/SecondFactorForm/SecondFactorForm.tsx index 9e3ea884..ce2b0b05 100644 --- a/client/src/components/SecondFactorForm/SecondFactorForm.tsx +++ b/client/src/components/SecondFactorForm/SecondFactorForm.tsx @@ -3,9 +3,9 @@ import styles from '../../assets/scss/components/SecondFactorForm/SecondFactorFo import Method2FA from '../../types/Method2FA'; import SecondFactorTOTP from '../../containers/components/SecondFactorTOTP/SecondFactorTOTP'; import SecondFactorU2F from '../../containers/components/SecondFactorU2F/SecondFactorU2F'; -import { Button } from '@material/react-button'; import classnames from 'classnames'; import SecondFactorDuoPush from '../../containers/components/SecondFactorDuoPush/SecondFactorDuoPush'; +import UseAnotherMethod from '../../containers/components/UseAnotherMethod/UseAnotherMethod'; export interface OwnProps { username: string; @@ -20,9 +20,6 @@ export interface StateProps { export interface DispatchProps { onInit: () => void; onLogoutClicked: () => void; - onOneTimePasswordMethodClicked: () => void; - onSecurityKeyMethodClicked: () => void; - onDuoPushMethodClicked: () => void; onUseAnotherMethodClicked: () => void; } @@ -55,19 +52,6 @@ class SecondFactorForm extends Component { ); } - private renderUseAnotherMethod() { - return ( -
    -
    Choose a method
    -
    - - - -
    -
    - ); - } - private renderUseAnotherMethodLink() { return (
    @@ -88,7 +72,7 @@ class SecondFactorForm extends Component {
- {(this.props.useAnotherMethod) ? this.renderUseAnotherMethod() : this.renderMethod()} + {(this.props.useAnotherMethod) ? : this.renderMethod()}
{(this.props.useAnotherMethod) ? null : this.renderUseAnotherMethodLink()}
diff --git a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx new file mode 100644 index 00000000..f9a4199b --- /dev/null +++ b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -0,0 +1,66 @@ +import React, { Component } from 'react'; +import styles from '../../assets/scss/components/SecondFactorForm/SecondFactorForm.module.scss'; +import Method2FA from '../../types/Method2FA'; +import { Button } from '@material/react-button'; +import classnames from 'classnames'; + +export interface OwnProps {} + +export interface StateProps { + availableMethods: Method2FA[] | null; +} + +export interface DispatchProps { + onInit: () => void; + onOneTimePasswordMethodClicked: () => void; + onSecurityKeyMethodClicked: () => void; + onDuoPushMethodClicked: () => void; +} + +export type Props = OwnProps & StateProps & DispatchProps; + +interface MethodDescription { + name: string; + onClicked: () => void; + key: Method2FA; +} + +class UseAnotherMethod extends Component { + componentDidMount() { + this.props.onInit(); + } + + render() { + const methods: MethodDescription[] = [ + { + name: "One-Time Password", + onClicked: this.props.onOneTimePasswordMethodClicked, + key: "totp" + }, + { + name: "Security Key (U2F)", + onClicked: this.props.onSecurityKeyMethodClicked, + key: "u2f" + }, + { + name: "Duo Push Notification", + onClicked: this.props.onDuoPushMethodClicked, + key: "duo_push" + } + ]; + const methodsComponents = methods + .filter(m => this.props.availableMethods && this.props.availableMethods.includes(m.key)) + .map(m => ); + + return ( +
+
Choose a method
+
+ {methodsComponents} +
+
+ ) + } +} + +export default UseAnotherMethod; \ No newline at end of file diff --git a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts index 01dde789..78c608bc 100644 --- a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts +++ b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts @@ -29,9 +29,6 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { onInit: () => FetchPrefered2faMethod(dispatch), onLogoutClicked: () => LogoutBehavior(dispatch), - onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), - onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), - onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), onUseAnotherMethodClicked: () => dispatch(setUseAnotherMethod(true)), } } diff --git a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx new file mode 100644 index 00000000..9e502a1f --- /dev/null +++ b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -0,0 +1,32 @@ +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { RootState } from '../../../reducers'; +import SetPrefered2faMethod from '../../../behaviors/SetPrefered2faMethod'; +import { getPreferedMethodSuccess, setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; +import Method2FA from '../../../types/Method2FA'; +import UseAnotherMethod, {StateProps, DispatchProps} from '../../../components/UseAnotherMethod/UseAnotherMethod'; +import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; + +const mapStateToProps = (state: RootState): StateProps => ({ + availableMethods: state.secondFactor.getAvailableMethodResponse, +}) + +async function storeMethod(dispatch: Dispatch, method: Method2FA) { + // display the new option + dispatch(getPreferedMethodSuccess(method)); + dispatch(setUseAnotherMethod(false)); + + // And save the method for next time. + await SetPrefered2faMethod(dispatch, method); +} + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { + return { + onInit: () => GetAvailable2faMethods(dispatch), + onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), + onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), + onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(UseAnotherMethod); \ No newline at end of file diff --git a/client/src/reducers/Portal/SecondFactor/actions.ts b/client/src/reducers/Portal/SecondFactor/actions.ts index d7c23576..327fafbc 100644 --- a/client/src/reducers/Portal/SecondFactor/actions.ts +++ b/client/src/reducers/Portal/SecondFactor/actions.ts @@ -19,7 +19,10 @@ import { SET_USE_ANOTHER_METHOD, TRIGGER_DUO_PUSH_AUTH, TRIGGER_DUO_PUSH_AUTH_SUCCESS, - TRIGGER_DUO_PUSH_AUTH_FAILURE + TRIGGER_DUO_PUSH_AUTH_FAILURE, + GET_AVAILABLE_METHODS, + GET_AVAILABLE_METHODS_SUCCESS, + GET_AVAILABLE_METHODS_FAILURE } from "../../constants"; import Method2FA from "../../../types/Method2FA"; @@ -31,6 +34,16 @@ export const setUseAnotherMethod = createAction(SET_USE_ANOTHER_METHOD, resolve return (useAnotherMethod: boolean) => resolve(useAnotherMethod); }); + +export const getAvailbleMethods = createAction(GET_AVAILABLE_METHODS); +export const getAvailbleMethodsSuccess = createAction(GET_AVAILABLE_METHODS_SUCCESS, resolve => { + return (methods: Method2FA[]) => resolve(methods); +}); +export const getAvailbleMethodsFailure = createAction(GET_AVAILABLE_METHODS_FAILURE, resolve => { + return (err: string) => resolve(err); +}); + + export const getPreferedMethod = createAction(GET_PREFERED_METHOD); export const getPreferedMethodSuccess = createAction(GET_PREFERED_METHOD_SUCCESS, resolve => { return (method: Method2FA) => resolve(method); @@ -39,30 +52,35 @@ export const getPreferedMethodFailure = createAction(GET_PREFERED_METHOD_FAILURE return (err: string) => resolve(err); }); + export const setPreferedMethod = createAction(SET_PREFERED_METHOD); export const setPreferedMethodSuccess = createAction(SET_PREFERED_METHOD_SUCCESS); export const setPreferedMethodFailure = createAction(SET_PREFERED_METHOD_FAILURE, resolve => { return (err: string) => resolve(err); }) + export const securityKeySign = createAction(SECURITY_KEY_SIGN); export const securityKeySignSuccess = createAction(SECURITY_KEY_SIGN_SUCCESS); export const securityKeySignFailure = createAction(SECURITY_KEY_SIGN_FAILURE, resolve => { return (error: string) => resolve(error); }); + export const oneTimePasswordVerification = createAction(ONE_TIME_PASSWORD_VERIFICATION_REQUEST); export const oneTimePasswordVerificationSuccess = createAction(ONE_TIME_PASSWORD_VERIFICATION_SUCCESS); export const oneTimePasswordVerificationFailure = createAction(ONE_TIME_PASSWORD_VERIFICATION_FAILURE, resolve => { return (err: string) => resolve(err); }); + export const triggerDuoPushAuth = createAction(TRIGGER_DUO_PUSH_AUTH); export const triggerDuoPushAuthSuccess = createAction(TRIGGER_DUO_PUSH_AUTH_SUCCESS); export const triggerDuoPushAuthFailure = createAction(TRIGGER_DUO_PUSH_AUTH_FAILURE, resolve => { return (err: string) => resolve(err); }); + export const logout = createAction(LOGOUT_REQUEST); export const logoutSuccess = createAction(LOGOUT_SUCCESS); export const logoutFailure = createAction(LOGOUT_FAILURE, resolve => { diff --git a/client/src/reducers/Portal/SecondFactor/reducer.ts b/client/src/reducers/Portal/SecondFactor/reducer.ts index cf6605e2..15bd3f49 100644 --- a/client/src/reducers/Portal/SecondFactor/reducer.ts +++ b/client/src/reducers/Portal/SecondFactor/reducer.ts @@ -12,6 +12,10 @@ interface SecondFactorState { userAnotherMethod: boolean; + getAvailableMethodsLoading: boolean; + getAvailableMethodResponse: Method2FA[] | null; + getAvailableMethodError: string | null; + preferedMethodLoading: boolean; preferedMethodError: string | null; preferedMethod: Method2FA | null; @@ -40,6 +44,10 @@ const secondFactorInitialState: SecondFactorState = { userAnotherMethod: false, + getAvailableMethodsLoading: false, + getAvailableMethodResponse: null, + getAvailableMethodError: null, + preferedMethod: null, preferedMethodError: null, preferedMethodLoading: false, @@ -190,6 +198,26 @@ export default (state = secondFactorInitialState, action: SecondFactorAction): S duoPushVerificationLoading: false, duoPushVerificationError: action.payload, } + + case getType(Actions.getPreferedMethod): + return { + ...state, + getAvailableMethodsLoading: true, + getAvailableMethodResponse: null, + getAvailableMethodError: null, + } + case getType(Actions.getAvailbleMethodsSuccess): + return { + ...state, + getAvailableMethodsLoading: false, + getAvailableMethodResponse: action.payload, + } + case getType(Actions.getAvailbleMethodsFailure): + return { + ...state, + getAvailableMethodsLoading: false, + getAvailableMethodError: action.payload, + } } return state; } \ No newline at end of file diff --git a/client/src/reducers/constants.ts b/client/src/reducers/constants.ts index 9332d4ee..7e37d3e3 100644 --- a/client/src/reducers/constants.ts +++ b/client/src/reducers/constants.ts @@ -12,6 +12,10 @@ export const AUTHENTICATE_FAILURE = '@portal/authenticate_failure'; export const SET_SECURITY_KEY_SUPPORTED = '@portal/second_factor/set_security_key_supported'; export const SET_USE_ANOTHER_METHOD = '@portal/second_factor/set_use_another_method'; +export const GET_AVAILABLE_METHODS = '@portal/second_factor/get_available_methods'; +export const GET_AVAILABLE_METHODS_SUCCESS = '@portal/second_factor/get_available_methods_success'; +export const GET_AVAILABLE_METHODS_FAILURE = '@portal/second_factor/get_available_methods_failure'; + export const GET_PREFERED_METHOD = '@portal/second_factor/get_prefered_method'; export const GET_PREFERED_METHOD_SUCCESS = '@portal/second_factor/get_prefered_method_success'; export const GET_PREFERED_METHOD_FAILURE = '@portal/second_factor/get_prefered_method_failure'; diff --git a/client/src/services/AutheliaService.ts b/client/src/services/AutheliaService.ts index cf5b9b07..2e4ee0fd 100644 --- a/client/src/services/AutheliaService.ts +++ b/client/src/services/AutheliaService.ts @@ -171,6 +171,10 @@ class AutheliaService { body: JSON.stringify({method}) }); } + + static async getAvailable2faMethods(): Promise { + return await this.fetchSafeJson('/api/secondfactor/available'); + } } export default AutheliaService; \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/available/Get.spec.ts b/server/src/lib/routes/secondfactor/available/Get.spec.ts new file mode 100644 index 00000000..ff700805 --- /dev/null +++ b/server/src/lib/routes/secondfactor/available/Get.spec.ts @@ -0,0 +1,36 @@ +import * as Express from "express"; +import { ServerVariables } from "../../../ServerVariables"; +import { ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec"; +import * as ExpressMock from "../../../stubs/express.spec"; +import Get from "./Get"; +import * as Assert from "assert"; + + +describe("routes/secondfactor/duo-push/Post", function() { + let vars: ServerVariables; + let req: Express.Request; + let res: ExpressMock.ResponseMock; + + beforeEach(function() { + const sv = ServerVariablesMockBuilder.build(); + vars = sv.variables; + + req = ExpressMock.RequestMock(); + res = ExpressMock.ResponseMock(); + }) + + it("should return default available methods", async function() { + await Get(vars)(req, res as any); + Assert(res.json.calledWith(["u2f", "totp"])); + }); + + it("should return duo as an available method", async function() { + vars.config.duo_api = { + hostname: "example.com", + integration_key: "ABCDEFG", + secret_key: "ekjfzelfjz", + } + await Get(vars)(req, res as any); + Assert(res.json.calledWith(["u2f", "totp", "duo_push"])); + }); +}); \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/available/Get.ts b/server/src/lib/routes/secondfactor/available/Get.ts new file mode 100644 index 00000000..74175ae3 --- /dev/null +++ b/server/src/lib/routes/secondfactor/available/Get.ts @@ -0,0 +1,14 @@ +import * as Express from "express"; +import { ServerVariables } from "../../../ServerVariables"; +import Method2FA from "../../../../../../shared/Method2FA"; + + +export default function(vars: ServerVariables) { + return async function(_: Express.Request, res: Express.Response) { + const availableMethods: Method2FA[] = ["u2f", "totp"]; + if (vars.config.duo_api) { + availableMethods.push("duo_push"); + } + res.json(availableMethods); + }; +} \ No newline at end of file diff --git a/server/src/lib/web_server/RestApi.ts b/server/src/lib/web_server/RestApi.ts index 04a15b5b..efb792ec 100644 --- a/server/src/lib/web_server/RestApi.ts +++ b/server/src/lib/web_server/RestApi.ts @@ -2,6 +2,7 @@ import * as Express from "express"; import SecondFactorPreferencesGet from "../routes/secondfactor/preferences/Get"; import SecondFactorPreferencesPost from "../routes/secondfactor/preferences/Post"; import SecondFactorDuoPushPost from "../routes/secondfactor/duo-push/Post"; +import SecondFactorAvailableGet from "../routes/secondfactor/available/Get"; import FirstFactorPost = require("../routes/firstfactor/post"); import LogoutPost from "../routes/logout/post"; @@ -109,6 +110,10 @@ export class RestApi { SecondFactorDuoPushPost(vars)); } + app.get(Endpoints.SECOND_FACTOR_AVAILABLE_GET, + RequireValidatedFirstFactor.middleware(vars.logger), + SecondFactorAvailableGet(vars)); + setupTotp(app, vars); setupU2f(app, vars); setupResetPassword(app, vars); diff --git a/shared/api.ts b/shared/api.ts index 89023277..73af9af0 100644 --- a/shared/api.ts +++ b/shared/api.ts @@ -197,6 +197,16 @@ export const SECOND_FACTOR_PREFERENCES_GET = "/api/secondfactor/preferences"; */ export const SECOND_FACTOR_PREFERENCES_POST = "/api/secondfactor/preferences"; +/** + * @api {post} /api/secondfactor/available List the available methods. + * @apiName GetAvailableMethods + * @apiGroup 2FA + * @apiVersion 1.0.0 + * + * @apiDescription Get the available 2FA methods. + */ +export const SECOND_FACTOR_AVAILABLE_GET = "/api/secondfactor/available"; + /** * @api {post} /api/password-reset Set new password diff --git a/test/helpers/assertions/VerifyButtonDoesNotExist.ts b/test/helpers/assertions/VerifyButtonDoesNotExist.ts new file mode 100644 index 00000000..e0a63d71 --- /dev/null +++ b/test/helpers/assertions/VerifyButtonDoesNotExist.ts @@ -0,0 +1,12 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; +import VerifyElementDoesNotExist from "./VerifyElementDoesNotExist"; + +/** + * Verify that an element does not exist. + * + * @param driver The selenium driver + * @param content The content of the button to select. + */ +export default async function(driver: WebDriver, content: string) { + await VerifyElementDoesNotExist(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")) +} \ No newline at end of file diff --git a/test/helpers/assertions/VerifyButtonExists.ts b/test/helpers/assertions/VerifyButtonExists.ts new file mode 100644 index 00000000..a9f45085 --- /dev/null +++ b/test/helpers/assertions/VerifyButtonExists.ts @@ -0,0 +1,11 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; +import VerifyElementExists from "./VerifyElementExists"; + +/** + * Verify if a button with given content exists in the DOM. + * @param driver The selenium web driver. + * @param content The content of the button to find in the DOM. + */ +export default async function(driver: WebDriver, content: string) { + await VerifyElementExists(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); +} \ No newline at end of file diff --git a/test/helpers/assertions/VerifyElementDoesNotExist.ts b/test/helpers/assertions/VerifyElementDoesNotExist.ts new file mode 100644 index 00000000..25c6fa97 --- /dev/null +++ b/test/helpers/assertions/VerifyElementDoesNotExist.ts @@ -0,0 +1,13 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; + +/** + * + * @param driver The selenium web driver + * @param locator The locator of the element to check it does not exist. + */ +export default async function(driver: WebDriver, locator: SeleniumWebDriver.Locator) { + const els = await driver.findElements(locator); + if (els.length > 0) { + throw new Error("Element exists."); + } +} \ No newline at end of file diff --git a/test/helpers/assertions/VerifyElementExists.ts b/test/helpers/assertions/VerifyElementExists.ts new file mode 100644 index 00000000..aaa69d46 --- /dev/null +++ b/test/helpers/assertions/VerifyElementExists.ts @@ -0,0 +1,13 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; + +/** + * + * @param driver The selenium web driver. + * @param locator The locator of the element to find in the DOM. + */ +export default async function(driver: WebDriver, locator: SeleniumWebDriver.Locator) { + const els = await driver.findElements(locator); + if (els.length == 0) { + throw new Error("Element does not exist."); + } +} \ No newline at end of file diff --git a/test/suites/basic/scenarii/NoDuoPushOption.ts b/test/suites/basic/scenarii/NoDuoPushOption.ts new file mode 100644 index 00000000..d5929d5a --- /dev/null +++ b/test/suites/basic/scenarii/NoDuoPushOption.ts @@ -0,0 +1,33 @@ +import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import LoginAs from "../../../helpers/LoginAs"; +import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; +import ClickOnLink from "../../../helpers/ClickOnLink"; +import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUseAnotherMethodView"; +import VerifyElementDoesNotExist from "../../../helpers/assertions/VerifyElementDoesNotExist"; +import SeleniumWebDriver from "selenium-webdriver"; +import VerifyButtonDoesNotExist from "../../../helpers/assertions/VerifyButtonDoesNotExist"; +import VerifyButtonExists from "../../../helpers/assertions/VerifyButtonExists"; + + + +export default function() { + before(async function() { + this.driver = await StartDriver(); + }); + + after(async function() { + await StopDriver(this.driver); + }); + + // The Duo API is not configured so we should not see the method. + it("should not display duo push notification method", async function() { + await LoginAs(this.driver, "john", "password", "https://secure.example.com:8080/"); + await VerifyIsSecondFactorStage(this.driver); + + await ClickOnLink(this.driver, 'Use another method'); + await VerifyIsUseAnotherMethodView(this.driver); + await VerifyButtonExists(this.driver, "Security Key (U2F)"); + await VerifyButtonExists(this.driver, "One-Time Password"); + await VerifyButtonDoesNotExist(this.driver, "Duo Push Notification"); + }); +} \ No newline at end of file diff --git a/test/suites/basic/test.ts b/test/suites/basic/test.ts index 006dbf4a..12ecce4b 100644 --- a/test/suites/basic/test.ts +++ b/test/suites/basic/test.ts @@ -11,6 +11,7 @@ import { exec } from '../../helpers/utils/exec'; import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; import BypassPolicy from "./scenarii/BypassPolicy"; import Prefered2faMethod from "./scenarii/Prefered2faMethod"; +import NoDuoPushOption from "./scenarii/NoDuoPushOption"; AutheliaSuite(__dirname, function() { this.timeout(10000); @@ -30,4 +31,5 @@ AutheliaSuite(__dirname, function() { describe('Required two factor', RequiredTwoFactor); describe('Logout endpoint redirect to already logged in page', LogoutRedirectToAlreadyLoggedIn); describe('Prefered 2FA method', Prefered2faMethod); + describe('No Duo Push method available', NoDuoPushOption); }); \ No newline at end of file From a4b129a6764d0540bc33499dd4355edffedd07b7 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 22:36:49 +0100 Subject: [PATCH 6/7] Security Key method is not displayed if browser does not support it. --- .../UseAnotherMethod/UseAnotherMethod.tsx | 6 ++++++ .../components/SecondFactorForm/SecondFactorForm.ts | 13 +------------ .../components/SecondFactorU2F/SecondFactorU2F.ts | 7 +------ .../UseAnotherMethod/UseAnotherMethod.tsx | 10 ++++++++-- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx index f9a4199b..b255ed73 100644 --- a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -8,6 +8,7 @@ export interface OwnProps {} export interface StateProps { availableMethods: Method2FA[] | null; + isSecurityKeySupported: boolean; } export interface DispatchProps { @@ -48,8 +49,13 @@ class UseAnotherMethod extends Component { key: "duo_push" } ]; + const methodsComponents = methods + // Filter out security key if not supported by browser. + .filter(m => m.key !== "u2f" || (m.key === "u2f" && this.props.isSecurityKeySupported)) + // Filter out the methods that are not supported by the server. .filter(m => this.props.availableMethods && this.props.availableMethods.includes(m.key)) + // Create the buttons .map(m => ); return ( diff --git a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts index 78c608bc..018b97fe 100644 --- a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts +++ b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts @@ -5,9 +5,7 @@ import LogoutBehavior from '../../../behaviors/LogoutBehavior'; import { RootState } from '../../../reducers'; import { StateProps, DispatchProps } from '../../../components/SecondFactorForm/SecondFactorForm'; import FetchPrefered2faMethod from '../../../behaviors/FetchPrefered2faMethod'; -import SetPrefered2faMethod from '../../../behaviors/SetPrefered2faMethod'; -import { getPreferedMethodSuccess, setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; -import Method2FA from '../../../types/Method2FA'; +import { setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; const mapStateToProps = (state: RootState): StateProps => { return { @@ -16,15 +14,6 @@ const mapStateToProps = (state: RootState): StateProps => { } } -async function storeMethod(dispatch: Dispatch, method: Method2FA) { - // display the new option - dispatch(getPreferedMethodSuccess(method)); - dispatch(setUseAnotherMethod(false)); - - // And save the method for next time. - await SetPrefered2faMethod(dispatch, method); -} - const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { onInit: () => FetchPrefered2faMethod(dispatch), diff --git a/client/src/containers/components/SecondFactorU2F/SecondFactorU2F.ts b/client/src/containers/components/SecondFactorU2F/SecondFactorU2F.ts index bbf645f9..d05f31a9 100644 --- a/client/src/containers/components/SecondFactorU2F/SecondFactorU2F.ts +++ b/client/src/containers/components/SecondFactorU2F/SecondFactorU2F.ts @@ -10,7 +10,6 @@ import { securityKeySignSuccess, securityKeySign, securityKeySignFailure, - setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; import FetchStateBehavior from '../../../behaviors/FetchStateBehavior'; @@ -94,11 +93,7 @@ const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps) => { await dispatch(push('/confirmation-sent')); }, onInit: async () => { - const isU2FSupported = await u2fApi.isSupported(); - if (isU2FSupported) { - await dispatch(setSecurityKeySupported(true)); - await triggerSecurityKeySigning(dispatch, ownProps.redirectionUrl); - } + await triggerSecurityKeySigning(dispatch, ownProps.redirectionUrl); }, } } diff --git a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx index 9e502a1f..5e1e5db6 100644 --- a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -2,13 +2,16 @@ import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { RootState } from '../../../reducers'; import SetPrefered2faMethod from '../../../behaviors/SetPrefered2faMethod'; -import { getPreferedMethodSuccess, setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; +import { getPreferedMethodSuccess, setUseAnotherMethod, setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; import Method2FA from '../../../types/Method2FA'; import UseAnotherMethod, {StateProps, DispatchProps} from '../../../components/UseAnotherMethod/UseAnotherMethod'; import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; +import u2fApi from 'u2f-api'; + const mapStateToProps = (state: RootState): StateProps => ({ availableMethods: state.secondFactor.getAvailableMethodResponse, + isSecurityKeySupported: state.secondFactor.securityKeySupported, }) async function storeMethod(dispatch: Dispatch, method: Method2FA) { @@ -22,7 +25,10 @@ async function storeMethod(dispatch: Dispatch, method: Method2FA) { const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { - onInit: () => GetAvailable2faMethods(dispatch), + onInit: async () => { + dispatch(setSecurityKeySupported(await u2fApi.isSupported())); + await GetAvailable2faMethods(dispatch); + }, onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), From 28cc5e7e1b27838b3e38fac0ffce6e49d85ab202 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sun, 24 Mar 2019 23:29:46 +0100 Subject: [PATCH 7/7] Fix integration tests. --- .../components/UseAnotherMethod/UseAnotherMethod.tsx | 5 ----- .../components/SecondFactorForm/SecondFactorForm.ts | 11 +++++++++-- .../components/UseAnotherMethod/UseAnotherMethod.tsx | 7 ------- test/helpers/assertions/VerifyButtonDoesNotExist.ts | 6 +++++- ...rifyButtonExists.ts => VerifyButtonHasAppeared.ts} | 8 ++++++-- .../assertions/VerifyIsDuoPushNotificationView.ts | 6 ++++++ test/suites/basic/scenarii/NoDuoPushOption.ts | 7 ++----- test/suites/basic/test.ts | 2 -- .../{basic => duo-push}/scenarii/Prefered2faMethod.ts | 7 ++++--- test/suites/duo-push/test.ts | 2 ++ 10 files changed, 34 insertions(+), 27 deletions(-) rename test/helpers/assertions/{VerifyButtonExists.ts => VerifyButtonHasAppeared.ts} (60%) create mode 100644 test/helpers/assertions/VerifyIsDuoPushNotificationView.ts rename test/suites/{basic => duo-push}/scenarii/Prefered2faMethod.ts (88%) diff --git a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx index b255ed73..8f520d58 100644 --- a/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -12,7 +12,6 @@ export interface StateProps { } export interface DispatchProps { - onInit: () => void; onOneTimePasswordMethodClicked: () => void; onSecurityKeyMethodClicked: () => void; onDuoPushMethodClicked: () => void; @@ -27,10 +26,6 @@ interface MethodDescription { } class UseAnotherMethod extends Component { - componentDidMount() { - this.props.onInit(); - } - render() { const methods: MethodDescription[] = [ { diff --git a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts index 018b97fe..f2cb5038 100644 --- a/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts +++ b/client/src/containers/components/SecondFactorForm/SecondFactorForm.ts @@ -5,7 +5,10 @@ import LogoutBehavior from '../../../behaviors/LogoutBehavior'; import { RootState } from '../../../reducers'; import { StateProps, DispatchProps } from '../../../components/SecondFactorForm/SecondFactorForm'; import FetchPrefered2faMethod from '../../../behaviors/FetchPrefered2faMethod'; -import { setUseAnotherMethod } from '../../../reducers/Portal/SecondFactor/actions'; +import { setUseAnotherMethod, setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; +import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; +import u2fApi from 'u2f-api'; + const mapStateToProps = (state: RootState): StateProps => { return { @@ -16,7 +19,11 @@ const mapStateToProps = (state: RootState): StateProps => { const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { - onInit: () => FetchPrefered2faMethod(dispatch), + onInit: async () => { + dispatch(setSecurityKeySupported(await u2fApi.isSupported())); + FetchPrefered2faMethod(dispatch); + GetAvailable2faMethods(dispatch); + }, onLogoutClicked: () => LogoutBehavior(dispatch), onUseAnotherMethodClicked: () => dispatch(setUseAnotherMethod(true)), } diff --git a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx index 5e1e5db6..adcd88ee 100644 --- a/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx +++ b/client/src/containers/components/UseAnotherMethod/UseAnotherMethod.tsx @@ -5,9 +5,6 @@ import SetPrefered2faMethod from '../../../behaviors/SetPrefered2faMethod'; import { getPreferedMethodSuccess, setUseAnotherMethod, setSecurityKeySupported } from '../../../reducers/Portal/SecondFactor/actions'; import Method2FA from '../../../types/Method2FA'; import UseAnotherMethod, {StateProps, DispatchProps} from '../../../components/UseAnotherMethod/UseAnotherMethod'; -import GetAvailable2faMethods from '../../../behaviors/GetAvailable2faMethods'; -import u2fApi from 'u2f-api'; - const mapStateToProps = (state: RootState): StateProps => ({ availableMethods: state.secondFactor.getAvailableMethodResponse, @@ -25,10 +22,6 @@ async function storeMethod(dispatch: Dispatch, method: Method2FA) { const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { return { - onInit: async () => { - dispatch(setSecurityKeySupported(await u2fApi.isSupported())); - await GetAvailable2faMethods(dispatch); - }, onOneTimePasswordMethodClicked: () => storeMethod(dispatch, 'totp'), onSecurityKeyMethodClicked: () => storeMethod(dispatch, 'u2f'), onDuoPushMethodClicked: () => storeMethod(dispatch, "duo_push"), diff --git a/test/helpers/assertions/VerifyButtonDoesNotExist.ts b/test/helpers/assertions/VerifyButtonDoesNotExist.ts index e0a63d71..d22c9eb0 100644 --- a/test/helpers/assertions/VerifyButtonDoesNotExist.ts +++ b/test/helpers/assertions/VerifyButtonDoesNotExist.ts @@ -8,5 +8,9 @@ import VerifyElementDoesNotExist from "./VerifyElementDoesNotExist"; * @param content The content of the button to select. */ export default async function(driver: WebDriver, content: string) { - await VerifyElementDoesNotExist(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")) + try { + await VerifyElementDoesNotExist(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + } catch (err) { + throw new Error(`Button with content "${content}" should not exist.`); + } } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyButtonExists.ts b/test/helpers/assertions/VerifyButtonHasAppeared.ts similarity index 60% rename from test/helpers/assertions/VerifyButtonExists.ts rename to test/helpers/assertions/VerifyButtonHasAppeared.ts index a9f45085..84ce2ad5 100644 --- a/test/helpers/assertions/VerifyButtonExists.ts +++ b/test/helpers/assertions/VerifyButtonHasAppeared.ts @@ -1,5 +1,5 @@ import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; -import VerifyElementExists from "./VerifyElementExists"; +import VerifyHasAppeared from "./VerifyHasAppeared"; /** * Verify if a button with given content exists in the DOM. @@ -7,5 +7,9 @@ import VerifyElementExists from "./VerifyElementExists"; * @param content The content of the button to find in the DOM. */ export default async function(driver: WebDriver, content: string) { - await VerifyElementExists(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + try { + await VerifyHasAppeared(driver, SeleniumWebDriver.By.xpath("//button[text()='" + content + "']")); + } catch (err) { + throw new Error(`Button with content "${content}" should have appeared.`); + } } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts b/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts new file mode 100644 index 00000000..9c327706 --- /dev/null +++ b/test/helpers/assertions/VerifyIsDuoPushNotificationView.ts @@ -0,0 +1,6 @@ +import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; + +export default async function(driver: WebDriver, timeout: number = 5000) { + await driver.wait(SeleniumWebDriver.until.elementLocated( + SeleniumWebDriver.By.className('duo-push-view')), timeout); +} \ No newline at end of file diff --git a/test/suites/basic/scenarii/NoDuoPushOption.ts b/test/suites/basic/scenarii/NoDuoPushOption.ts index d5929d5a..4ab0a7ea 100644 --- a/test/suites/basic/scenarii/NoDuoPushOption.ts +++ b/test/suites/basic/scenarii/NoDuoPushOption.ts @@ -3,10 +3,8 @@ import LoginAs from "../../../helpers/LoginAs"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; import ClickOnLink from "../../../helpers/ClickOnLink"; import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUseAnotherMethodView"; -import VerifyElementDoesNotExist from "../../../helpers/assertions/VerifyElementDoesNotExist"; -import SeleniumWebDriver from "selenium-webdriver"; import VerifyButtonDoesNotExist from "../../../helpers/assertions/VerifyButtonDoesNotExist"; -import VerifyButtonExists from "../../../helpers/assertions/VerifyButtonExists"; +import VerifyButtonHasAppeared from "../../../helpers/assertions/VerifyButtonHasAppeared"; @@ -26,8 +24,7 @@ export default function() { await ClickOnLink(this.driver, 'Use another method'); await VerifyIsUseAnotherMethodView(this.driver); - await VerifyButtonExists(this.driver, "Security Key (U2F)"); - await VerifyButtonExists(this.driver, "One-Time Password"); + await VerifyButtonHasAppeared(this.driver, "One-Time Password"); await VerifyButtonDoesNotExist(this.driver, "Duo Push Notification"); }); } \ No newline at end of file diff --git a/test/suites/basic/test.ts b/test/suites/basic/test.ts index 12ecce4b..02e03593 100644 --- a/test/suites/basic/test.ts +++ b/test/suites/basic/test.ts @@ -10,7 +10,6 @@ import LogoutRedirectToAlreadyLoggedIn from './scenarii/LogoutRedirectToAlreadyL import { exec } from '../../helpers/utils/exec'; import TwoFactorAuthentication from "../../helpers/scenarii/TwoFactorAuthentication"; import BypassPolicy from "./scenarii/BypassPolicy"; -import Prefered2faMethod from "./scenarii/Prefered2faMethod"; import NoDuoPushOption from "./scenarii/NoDuoPushOption"; AutheliaSuite(__dirname, function() { @@ -30,6 +29,5 @@ AutheliaSuite(__dirname, function() { describe('TOTP Validation', TOTPValidation); describe('Required two factor', RequiredTwoFactor); describe('Logout endpoint redirect to already logged in page', LogoutRedirectToAlreadyLoggedIn); - describe('Prefered 2FA method', Prefered2faMethod); describe('No Duo Push method available', NoDuoPushOption); }); \ No newline at end of file diff --git a/test/suites/basic/scenarii/Prefered2faMethod.ts b/test/suites/duo-push/scenarii/Prefered2faMethod.ts similarity index 88% rename from test/suites/basic/scenarii/Prefered2faMethod.ts rename to test/suites/duo-push/scenarii/Prefered2faMethod.ts index 65ccff2c..20895dd7 100644 --- a/test/suites/basic/scenarii/Prefered2faMethod.ts +++ b/test/suites/duo-push/scenarii/Prefered2faMethod.ts @@ -6,6 +6,7 @@ import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUs import ClickOnButton from "../../../helpers/behaviors/ClickOnButton"; import VerifyIsSecurityKeyView from "../../../helpers/assertions/VerifyIsSecurityKeyView"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; +import VerifyIsDuoPushNotificationView from "../../../helpers/assertions/VerifyIsDuoPushNotificationView"; // This fixture tests that the latest used method is still used when the user gets back. @@ -26,10 +27,10 @@ export default function() { await ClickOnLink(this.driver, 'Use another method'); await VerifyIsUseAnotherMethodView(this.driver); - await ClickOnButton(this.driver, 'Security Key (U2F)'); + await ClickOnButton(this.driver, 'Duo Push Notification'); // Verify that the user is redirected to the new method - await VerifyIsSecurityKeyView(this.driver); + await VerifyIsDuoPushNotificationView(this.driver); await ClickOnLink(this.driver, "Logout"); // Login with another user to check that he gets TOTP view. @@ -39,7 +40,7 @@ export default function() { // Log john again to check that the prefered method has been persisted await LoginAs(this.driver, "john", "password", "https://secure.example.com:8080/"); - await VerifyIsSecurityKeyView(this.driver); + await VerifyIsDuoPushNotificationView(this.driver); // Restore the prefered method to one-time password. await ClickOnLink(this.driver, 'Use another method'); diff --git a/test/suites/duo-push/test.ts b/test/suites/duo-push/test.ts index 1dab3d4d..d8863bc2 100644 --- a/test/suites/duo-push/test.ts +++ b/test/suites/duo-push/test.ts @@ -1,6 +1,7 @@ import AutheliaSuite from "../../helpers/context/AutheliaSuite"; import { exec } from '../../helpers/utils/exec'; import DuoPushNotification from "./scenarii/DuoPushNotification"; +import Prefered2faMethod from "./scenarii/Prefered2faMethod"; // required to query duo-api over https process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any; @@ -13,4 +14,5 @@ AutheliaSuite(__dirname, function() { }); describe("Duo Push Notication", DuoPushNotification); + describe("Prefered 2FA methods", Prefered2faMethod); }); \ No newline at end of file