From 5023c42b53ec7de0334aa0eafbb3dd361caca841 Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Fri, 2 Aug 2024 17:08:12 +0200 Subject: [PATCH] Move additional base64 encoding of WebAuthn responses to backend We already have JSON body parsers configured to automatically wrap and unwrap binary data, so we don't need this encoding for the frontend's sake. That the backend uses SimpleWebauthn which expects inputs base64url encoded is an implementation detail that should not leak into the API exposed to the frontend. --- src/routers/user.router.ts | 44 +++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/routers/user.router.ts b/src/routers/user.router.ts index 6fb44ae..1bccdaf 100644 --- a/src/routers/user.router.ts +++ b/src/routers/user.router.ts @@ -163,7 +163,16 @@ noAuthUserController.post('/register-webauthn-finish', async (req: Request, res: const credential = req.body.credential; const verification = await SimpleWebauthn.verifyRegistrationResponse({ - response: credential, + response: { + type: credential.type, + id: credential.id, + rawId: credential.id, // SimpleWebauthn requires this base64url encoded + response: { + attestationObject: base64url.encode(credential.response.attestationObject), + clientDataJSON: base64url.encode(credential.response.clientDataJSON), + }, + clientExtensionResults: credential.clientExtensionResults, + }, expectedChallenge: base64url.encode(challenge.challenge), expectedOrigin: config.webauthn.origin, expectedRPID: config.webauthn.rp.id, @@ -189,14 +198,14 @@ noAuthUserController.post('/register-webauthn-finish', async (req: Request, res: webauthnUserHandle, webauthnCredentials: [ newWebauthnCredentialEntity({ - credentialId: Buffer.from(verification.registrationInfo.credentialID), + credentialId: credential.rawId, userHandle: Buffer.from(webauthnUserHandle), nickname: req.body.nickname, publicKeyCose: Buffer.from(verification.registrationInfo.credentialPublicKey), signatureCount: verification.registrationInfo.counter, transports: credential.response.transports || [], - attestationObject: Buffer.from(verification.registrationInfo.attestationObject), - create_clientDataJSON: Buffer.from(credential.response.clientDataJSON), + attestationObject: credential.response.attestationObject, + create_clientDataJSON: credential.response.clientDataJSON, prfCapable: credential.clientExtensionResults?.prf?.enabled || false, }), ], @@ -235,8 +244,8 @@ noAuthUserController.post('/login-webauthn-finish', async (req: Request, res: Re console.log("webauthn login-finish", req.body); const credential = req.body.credential; - const userHandle = base64url.toBuffer(credential.response.userHandle).toString(); - const credentialId = base64url.toBuffer(credential.id); + const userHandle = credential.response.userHandle.toString(); + const credentialId = credential.rawId; const userRes = await getUserByWebauthnCredential(userHandle, credentialId); if (userRes.err) { @@ -259,7 +268,17 @@ noAuthUserController.post('/login-webauthn-finish', async (req: Request, res: Re console.log("webauthn login-finish challenge", challenge); const verification = await SimpleWebauthn.verifyAuthenticationResponse({ - response: credential, + response: { + type: credential.type, + id: credential.id, + rawId: credential.id, // SimpleWebauthn requires this base64url encoded + response: { + authenticatorData: base64url.encode(credential.response.authenticatorData), + clientDataJSON: base64url.encode(credential.response.clientDataJSON), + signature: base64url.encode(credential.response.signature), + }, + clientExtensionResults: credential.clientExtensionResults, + }, expectedChallenge: base64url.encode(challenge.challenge), expectedOrigin: config.webauthn.origin, expectedRPID: config.webauthn.rp.id, @@ -400,7 +419,16 @@ userController.post('/webauthn/register-finish', async (req: Request, res: Respo const credential = req.body.credential; const verification = await SimpleWebauthn.verifyRegistrationResponse({ - response: credential, + response: { + type: credential.type, + id: credential.id, + rawId: credential.id, // SimpleWebauthn requires this base64url encoded + response: { + attestationObject: base64url.encode(credential.response.attestationObject), + clientDataJSON: base64url.encode(credential.response.clientDataJSON), + }, + clientExtensionResults: credential.clientExtensionResults, + }, expectedChallenge: base64url.encode(challenge.challenge), expectedOrigin: config.webauthn.origin, expectedRPID: config.webauthn.rp.id,