From d01aa9272cd2b247f9db5c1bf0e6edcbb482ef4e Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 14 May 2021 12:18:29 +0200 Subject: [PATCH] Can authenticate using security key --- src/helpers/AccountHelper.ts | 66 ++++++++++++++++++++++++++++++++++-- src/ui/routes/LoginRoute.tsx | 30 +++++++++++++--- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/helpers/AccountHelper.ts b/src/helpers/AccountHelper.ts index 6a88cbb..f7d403c 100644 --- a/src/helpers/AccountHelper.ts +++ b/src/helpers/AccountHelper.ts @@ -180,12 +180,72 @@ export class AccountHelper { }, }; - console.info(cred); - console.info(res); - await serverRequest("accounts/register_key", { name: name, key: JSON.stringify(res), }); } + + /** + * First step of security key authentication + * + * @param mail Target admin account email address + * @param key The key to use to authentifcate + */ + static async GetAuthenticationChallenge( + mail: string, + key: AuthKey + ): Promise { + const res = await serverRequest("accounts/challenge_auth_with_key", { + mail: mail, + key_id: key.id, + }); + + res.publicKey.challenge = base64NoPaddingToUint8Array( + res.publicKey.challenge + ); + + for (let cred of res.publicKey.allowCredentials) { + cred.id = base64NoPaddingToUint8Array(cred.id); + } + + return res; + } + + /** + * Attempt to sign in using security key + * + * @param mail Target admin account email address + * @param key Key used to authenticate + * @param cred Response to authentication + */ + static async AuthenticateWithKey( + mail: string, + key: AuthKey, + cred: any + ): Promise { + const creds = { + id: cred.id, + rawId: ArrayBufferToBase64(cred.rawId), + type: cred.type, + response: { + authenticatorData: ArrayBufferToBase64( + cred.response.authenticatorData + ), + clientDataJSON: ArrayBufferToBase64( + cred.response.clientDataJSON + ), + signature: ArrayBufferToBase64(cred.response.signature), + userHandle: cred.response.userHandle, + }, + }; + + const res = await serverRequest("accounts/auth_with_key", { + mail: mail, + key_id: key.id, + credential: JSON.stringify(creds), + }); + + sessionStorage.setItem(SESSION_STORAGE_TOKEN, res.token); + } } diff --git a/src/ui/routes/LoginRoute.tsx b/src/ui/routes/LoginRoute.tsx index 336decb..dad72b0 100644 --- a/src/ui/routes/LoginRoute.tsx +++ b/src/ui/routes/LoginRoute.tsx @@ -21,7 +21,11 @@ import { import { ErrorOutline, Lock, VpnKey } from "@material-ui/icons"; import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; import React from "react"; -import { AccountHelper, AuthOptions } from "../../helpers/AccountHelper"; +import { + AccountHelper, + AuthKey, + AuthOptions, +} from "../../helpers/AccountHelper"; import { input, matAlert } from "../widgets/DialogsProvider"; function ErrorGettingOptions() { @@ -238,8 +242,26 @@ class AuthOptionsWidget extends React.Component< } } - async loginWithSecurityKey(id: number) { - console.info(id); + async loginWithSecurityKey(key: AuthKey) { + try { + const challenge = await AccountHelper.GetAuthenticationChallenge( + this.props.email, + key + ); + + const result = await navigator.credentials.get(challenge); + + await AccountHelper.AuthenticateWithKey( + this.props.email, + key, + result + ); + + document.location.href = document.location.href + ""; + } catch (e) { + console.error(e); + matAlert("Authentication with security key failed!"); + } } render() { @@ -266,7 +288,7 @@ class AuthOptionsWidget extends React.Component< {this.props.options.keys.map((key) => ( this.loginWithSecurityKey(key.id)} + onClick={() => this.loginWithSecurityKey(key)} key={key.id} >