From 81e274babd9ebe8ad8a08efdc7001ac19ac90135 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Sat, 8 Jun 2024 22:09:48 +0530 Subject: [PATCH] Move to service layer --- .../accounts/src/pages/passkeys/verify.tsx | 46 ++----------------- web/apps/accounts/src/services/passkey.ts | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/web/apps/accounts/src/pages/passkeys/verify.tsx b/web/apps/accounts/src/pages/passkeys/verify.tsx index 6af8104df5..b3dec84c83 100644 --- a/web/apps/accounts/src/pages/passkeys/verify.tsx +++ b/web/apps/accounts/src/pages/passkeys/verify.tsx @@ -5,7 +5,6 @@ import { nullToUndefined } from "@/utils/transform"; import { VerticallyCentered } from "@ente/shared/components/Container"; import EnteButton from "@ente/shared/components/EnteButton"; import EnteSpinner from "@ente/shared/components/EnteSpinner"; -import { fromB64URLSafeNoPadding } from "@ente/shared/crypto/internal/libsodium"; import HTTPService from "@ente/shared/network/HTTPService"; import InfoIcon from "@mui/icons-material/Info"; import { Paper, Typography, styled } from "@mui/material"; @@ -13,6 +12,7 @@ import { t } from "i18next"; import _sodium from "libsodium-wrappers"; import { useEffect, useState } from "react"; import { + authenticatePasskey, beginPasskeyAuthentication, finishPasskeyAuthentication, isWebAuthnSupported, @@ -99,23 +99,9 @@ const Page = () => { setStatus("waitingForUser"); - let credential: Credential | null = null; - - let tries = 0; - const maxTries = 3; - - while (tries < maxTries) { - try { - credential = await getCredential(beginData.options.publicKey); - } catch (e) { - log.error("Couldn't get credential", e); - continue; - } finally { - tries++; - } - - break; - } + const credential = await authenticatePasskey( + beginData.options.publicKey, + ); if (!credential) { setStatus("failed"); @@ -147,30 +133,6 @@ const Page = () => { window.location.href = `${redirect}?response=${encodedResponse}`; }; - const getCredential = async ( - publicKey: any, - timeoutMillis: number = 60000, // Default timeout of 60 seconds - ): Promise => { - publicKey.challenge = await fromB64URLSafeNoPadding( - publicKey.challenge, - ); - for (const listItem of publicKey.allowCredentials ?? []) { - listItem.id = await fromB64URLSafeNoPadding(listItem.id); - // note: we are orverwriting the transports array with all possible values. - // This is because the browser will only prompt the user for the transport that is available. - // Warning: In case of invalid transport value, the webauthn will fail on Safari & iOS browsers - listItem.transports = ["usb", "nfc", "ble", "internal"]; - } - publicKey.timeout = timeoutMillis; - const publicKeyCredentialCreationOptions: CredentialRequestOptions = { - publicKey: publicKey, - }; - const credential = await navigator.credentials.get( - publicKeyCredentialCreationOptions, - ); - return credential; - }; - useEffect(() => { void authenticate(); }, []); diff --git a/web/apps/accounts/src/services/passkey.ts b/web/apps/accounts/src/services/passkey.ts index c9235d0ef9..38fc3e5731 100644 --- a/web/apps/accounts/src/services/passkey.ts +++ b/web/apps/accounts/src/services/passkey.ts @@ -355,6 +355,52 @@ export const beginPasskeyAuthentication = async ( } }; +/** + * Authenticate using a passkey that the user has previously created for the + * current domain. + * + * @param options + */ +export const authenticatePasskey = async ( + publicKey: PublicKeyCredentialRequestOptions, +) => { + let tries = 0; + const maxTries = 3; + + while (tries < maxTries) { + try { + return await getCredential(publicKey); + } catch (e) { + log.error("Couldn't get credential", e); + continue; + } finally { + tries++; + } + } + + return undefined; +}; + +const getCredential = async (publicKey: any): Promise => { + const timeoutMillis: number = 60000; // Default timeout of 60 seconds + publicKey.challenge = await fromB64URLSafeNoPadding(publicKey.challenge); + for (const listItem of publicKey.allowCredentials ?? []) { + listItem.id = await fromB64URLSafeNoPadding(listItem.id); + // note: we are orverwriting the transports array with all possible values. + // This is because the browser will only prompt the user for the transport that is available. + // Warning: In case of invalid transport value, the webauthn will fail on Safari & iOS browsers + listItem.transports = ["usb", "nfc", "ble", "internal"]; + } + publicKey.timeout = timeoutMillis; + const publicKeyCredentialCreationOptions: CredentialRequestOptions = { + publicKey: publicKey, + }; + const credential = await navigator.credentials.get( + publicKeyCredentialCreationOptions, + ); + return credential; +}; + export const finishPasskeyAuthentication = async ( credential: Credential, sessionId: string,