From ecad1e2e9d8c479a0f1a525a9b6f99383d6413fe Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 14:59:40 -0700 Subject: Stop stripping base64 padding in browser --- packages/browser/src/helpers/toBase64String.test.ts | 15 +++++++++++++++ packages/browser/src/helpers/toBase64String.ts | 3 +-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 packages/browser/src/helpers/toBase64String.test.ts (limited to 'packages/browser/src') diff --git a/packages/browser/src/helpers/toBase64String.test.ts b/packages/browser/src/helpers/toBase64String.test.ts new file mode 100644 index 0000000..c16ec72 --- /dev/null +++ b/packages/browser/src/helpers/toBase64String.test.ts @@ -0,0 +1,15 @@ +import toBase64String from './toBase64String'; + +import toUintArray from './toUint8Array'; + +test('should convert a Buffer to a string with a length that is a multiple of 4', () => { + const base64 = toBase64String(Buffer.from('123456', 'ascii')); + + expect(base64.length % 4).toEqual(0); +}); + +test('should convert a Uint8Array to a string with a length that is a multiple of 4', () => { + const base64 = toBase64String(toUintArray('123456')); + + expect(base64.length % 4).toEqual(0); +}); diff --git a/packages/browser/src/helpers/toBase64String.ts b/packages/browser/src/helpers/toBase64String.ts index 8234002..9c949be 100644 --- a/packages/browser/src/helpers/toBase64String.ts +++ b/packages/browser/src/helpers/toBase64String.ts @@ -4,6 +4,5 @@ export default function toBase64String(buffer: ArrayBuffer): string { // TODO: Make sure converting buffer to Uint8Array() is correct return base64js.fromByteArray(new Uint8Array(buffer)) .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); + .replace(/\//g, "_"); } -- cgit v1.2.3 From c0a3828820d32d90289b24597c6110abba2117ba Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 15:04:27 -0700 Subject: Adjust how cred ID is converted for assertion --- packages/browser/src/methods/startAssertion.test.ts | 7 ++++--- packages/browser/src/methods/startAssertion.ts | 14 ++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'packages/browser/src') diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts index b069f60..ffc693c 100644 --- a/packages/browser/src/methods/startAssertion.test.ts +++ b/packages/browser/src/methods/startAssertion.test.ts @@ -4,6 +4,7 @@ import { AssertionCredential, PublicKeyCredentialRequestOptionsJSON } from '@web import toUint8Array from '../helpers/toUint8Array'; import supportsWebauthn from '../helpers/supportsWebauthn'; +import toBase64String from '../helpers/toBase64String'; import startAssertion from './startAssertion'; @@ -49,9 +50,9 @@ test('should convert options before passing to navigator.credentials.get(...)', const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.publicKey.challenge)); - expect(argsPublicKey.allowCredentials[0].id).toEqual( - toUint8Array(goodOpts1.publicKey.allowCredentials[0].id), - ); + // Make sure the credential ID is a proper base64 with a length that's a multiple of 4 + expect(argsPublicKey.allowCredentials[0].id.length % 4).toEqual(0); + expect(argsPublicKey.allowCredentials[0].id).toEqual(base64js.toByteArray('credId==')); done(); }); diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAssertion.ts index 603c6fb..37a7915 100644 --- a/packages/browser/src/methods/startAssertion.ts +++ b/packages/browser/src/methods/startAssertion.ts @@ -3,6 +3,7 @@ import { AuthenticatorAssertionResponseJSON, AssertionCredential, } from '@webauthntine/typescript-types'; +import base64js from 'base64-js'; import toUint8Array from '../helpers/toUint8Array'; import toBase64String from '../helpers/toBase64String'; @@ -24,10 +25,15 @@ export default async function startAssertion( const publicKey: PublicKeyCredentialRequestOptions = { ...requestOptionsJSON.publicKey, challenge: toUint8Array(requestOptionsJSON.publicKey.challenge), - allowCredentials: requestOptionsJSON.publicKey.allowCredentials.map((cred) => ({ - ...cred, - id: toUint8Array(cred.id), - })) + allowCredentials: requestOptionsJSON.publicKey.allowCredentials.map((cred) => { + // Make sure the credential ID length is a multiple of 4 + let id = cred.id.padEnd(cred.id.length + (cred.id.length % 4), '='); + + return { + ...cred, + id: base64js.toByteArray(id), + }; + }) }; // Wait for the user to complete assertion -- cgit v1.2.3 From 08ba78c59a1556734c465c1db990a27f45bdfb3b Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 15:05:29 -0700 Subject: Adjust some mock values in startAssertion tests --- packages/browser/src/methods/startAssertion.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'packages/browser/src') diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts index ffc693c..bbde1e7 100644 --- a/packages/browser/src/methods/startAssertion.test.ts +++ b/packages/browser/src/methods/startAssertion.test.ts @@ -13,10 +13,10 @@ jest.mock('../helpers/supportsWebauthn'); const mockNavigatorGet = (window.navigator.credentials.get as jest.Mock); const mockSupportsWebauthn = (supportsWebauthn as jest.Mock); -const mockAttestationObject = 'mockAsse'; -const mockClientDataJSON = 'mockClie'; -const mockSignature = 'mockSign'; -const mockUserHandle = 'mockUser'; +const mockAuthenticatorData = toBase64String(toUint8Array('mockAuthenticatorData')); +const mockClientDataJSON = toBase64String(toUint8Array('mockClientDataJSON')); +const mockSignature = toBase64String(toUint8Array('mockSignature')); +const mockUserHandle = toBase64String(toUint8Array('mockUserHandle')); const goodOpts1: PublicKeyCredentialRequestOptionsJSON = { publicKey: { @@ -66,8 +66,8 @@ test('should return base64-encoded response values', async (done) => { id: 'foobar', rawId: toUint8Array('foobar'), response: { + authenticatorData: base64js.toByteArray(mockAuthenticatorData), clientDataJSON: base64js.toByteArray(mockClientDataJSON), - authenticatorData: base64js.toByteArray(mockClientDataJSON), signature: base64js.toByteArray(mockSignature), userHandle: base64js.toByteArray(mockUserHandle), }, @@ -80,7 +80,7 @@ test('should return base64-encoded response values', async (done) => { const response = await startAssertion(goodOpts1); expect(response).toEqual({ - base64AuthenticatorData: mockClientDataJSON, + base64AuthenticatorData: mockAuthenticatorData, base64ClientDataJSON: mockClientDataJSON, base64Signature: mockSignature, base64UserHandle: mockUserHandle, -- cgit v1.2.3 From 8a464cdfdd9bb707aa0525b414c00342f58fd94d Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 15:23:29 -0700 Subject: Fix misspelling in toBase64String test --- packages/browser/src/helpers/toBase64String.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/browser/src') diff --git a/packages/browser/src/helpers/toBase64String.test.ts b/packages/browser/src/helpers/toBase64String.test.ts index c16ec72..bbcb11b 100644 --- a/packages/browser/src/helpers/toBase64String.test.ts +++ b/packages/browser/src/helpers/toBase64String.test.ts @@ -1,6 +1,6 @@ import toBase64String from './toBase64String'; -import toUintArray from './toUint8Array'; +import toUint8Array from './toUint8Array'; test('should convert a Buffer to a string with a length that is a multiple of 4', () => { const base64 = toBase64String(Buffer.from('123456', 'ascii')); @@ -9,7 +9,7 @@ test('should convert a Buffer to a string with a length that is a multiple of 4' }); test('should convert a Uint8Array to a string with a length that is a multiple of 4', () => { - const base64 = toBase64String(toUintArray('123456')); + const base64 = toBase64String(toUint8Array('123456')); expect(base64.length % 4).toEqual(0); }); -- cgit v1.2.3 From fe68bbcf520250b624fbea5ff33ca99988e7f9db Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 15:24:06 -0700 Subject: Clarify usage of toUint8Array --- packages/browser/src/helpers/toUint8Array.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/browser/src') diff --git a/packages/browser/src/helpers/toUint8Array.ts b/packages/browser/src/helpers/toUint8Array.ts index a807a88..ed4aa5d 100644 --- a/packages/browser/src/helpers/toUint8Array.ts +++ b/packages/browser/src/helpers/toUint8Array.ts @@ -1,6 +1,6 @@ /** - * A helper method to convert a string sent from the server to a Uint8Array the authenticator will - * expect. + * A helper method to convert an arbitrary string sent from the server to a Uint8Array the + * authenticator will expect. */ export default function toUint8Array(value: string): Uint8Array { return Uint8Array.from(value, c => c.charCodeAt(0)); -- cgit v1.2.3 From 8611db505392a951007974a85534671d5279521e Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 22 May 2020 16:53:04 -0700 Subject: Require credential ID from browser after assertion --- packages/browser/src/methods/startAssertion.test.ts | 3 +++ packages/browser/src/methods/startAssertion.ts | 1 + packages/typescript-types/src/index.ts | 1 + 3 files changed, 5 insertions(+) (limited to 'packages/browser/src') diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts index bbde1e7..4e3bb07 100644 --- a/packages/browser/src/methods/startAssertion.test.ts +++ b/packages/browser/src/methods/startAssertion.test.ts @@ -60,6 +60,8 @@ test('should convert options before passing to navigator.credentials.get(...)', test('should return base64-encoded response values', async (done) => { mockSupportsWebauthn.mockReturnValue(true); + const credentialID = 'foobar'; + mockNavigatorGet.mockImplementation((): Promise => { return new Promise((resolve) => { resolve({ @@ -80,6 +82,7 @@ test('should return base64-encoded response values', async (done) => { const response = await startAssertion(goodOpts1); expect(response).toEqual({ + base64CredentialID: credentialID, base64AuthenticatorData: mockAuthenticatorData, base64ClientDataJSON: mockClientDataJSON, base64Signature: mockSignature, diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAssertion.ts index 37a7915..8e411ec 100644 --- a/packages/browser/src/methods/startAssertion.ts +++ b/packages/browser/src/methods/startAssertion.ts @@ -52,6 +52,7 @@ export default async function startAssertion( // Convert values to base64 to make it easier to send back to the server return { + base64CredentialID: credential.id, base64AuthenticatorData: toBase64String(response.authenticatorData), base64ClientDataJSON: toBase64String(response.clientDataJSON), base64Signature: toBase64String(response.signature), diff --git a/packages/typescript-types/src/index.ts b/packages/typescript-types/src/index.ts index 6ad3b81..ee714ff 100644 --- a/packages/typescript-types/src/index.ts +++ b/packages/typescript-types/src/index.ts @@ -87,6 +87,7 @@ AuthenticatorAttestationResponse, 'clientDataJSON' | 'attestationObject' export interface AuthenticatorAssertionResponseJSON extends Omit< AuthenticatorAssertionResponse, 'clientDataJSON' | 'authenticatorData' | 'signature' | 'userHandle' > { + base64CredentialID: string; base64AuthenticatorData: string; base64ClientDataJSON: string; base64Signature: string; -- cgit v1.2.3