import base64js from 'base64-js'; import { AssertionCredential, PublicKeyCredentialRequestOptionsJSON, } from '@webauthntine/typescript-types'; import toUint8Array from '../helpers/toUint8Array'; import supportsWebauthn from '../helpers/supportsWebauthn'; import toBase64String from '../helpers/toBase64String'; import startAssertion from './startAssertion'; jest.mock('../helpers/supportsWebauthn'); const mockNavigatorGet = window.navigator.credentials.get as jest.Mock; const mockSupportsWebauthn = supportsWebauthn as jest.Mock; const mockAuthenticatorData = toBase64String(toUint8Array('mockAuthenticatorData')); const mockClientDataJSON = toBase64String(toUint8Array('mockClientDataJSON')); const mockSignature = toBase64String(toUint8Array('mockSignature')); const mockUserHandle = toBase64String(toUint8Array('mockUserHandle')); const goodOpts1: PublicKeyCredentialRequestOptionsJSON = { publicKey: { challenge: 'fizz', allowCredentials: [ { id: 'abcdefgfdnsdfunguisdfgs', type: 'public-key', transports: ['nfc'], }, ], timeout: 1, }, }; beforeEach(() => { mockNavigatorGet.mockReset(); mockSupportsWebauthn.mockReset(); }); test('should convert options before passing to navigator.credentials.get(...)', async done => { mockSupportsWebauthn.mockReturnValue(true); // Stub out a response so the method won't throw mockNavigatorGet.mockImplementation( (): Promise => { return new Promise(resolve => { resolve({ response: {} }); }); }, ); await startAssertion(goodOpts1); const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; const credId = base64js.fromByteArray(argsPublicKey.allowCredentials[0].id); expect(argsPublicKey.challenge).toEqual(toUint8Array(goodOpts1.publicKey.challenge)); // Make sure the credential ID is a proper base64 with a length that's a multiple of 4 expect(credId.length % 4).toEqual(0); done(); }); test('should return base64-encoded response values', async done => { mockSupportsWebauthn.mockReturnValue(true); const credentialID = 'foobar'; mockNavigatorGet.mockImplementation( (): Promise => { return new Promise(resolve => { resolve({ id: 'foobar', rawId: toUint8Array('foobar'), response: { authenticatorData: base64js.toByteArray(mockAuthenticatorData), clientDataJSON: base64js.toByteArray(mockClientDataJSON), signature: base64js.toByteArray(mockSignature), userHandle: base64js.toByteArray(mockUserHandle), }, getClientExtensionResults: () => ({}), type: 'webauthn.get', }); }); }, ); const response = await startAssertion(goodOpts1); expect(response).toEqual({ base64CredentialID: credentialID, base64AuthenticatorData: mockAuthenticatorData, base64ClientDataJSON: mockClientDataJSON, base64Signature: mockSignature, base64UserHandle: mockUserHandle, }); done(); }); test("should throw error if WebAuthn isn't supported", async done => { mockSupportsWebauthn.mockReturnValue(false); await expect(startAssertion(goodOpts1)).rejects.toThrow( 'WebAuthn is not supported in this browser', ); done(); }); test('should throw error if assertion is cancelled for some reason', async done => { mockSupportsWebauthn.mockReturnValue(true); mockNavigatorGet.mockImplementation( (): Promise => { return new Promise(resolve => { resolve(null); }); }, ); await expect(startAssertion(goodOpts1)).rejects.toThrow('Assertion was not completed'); done(); });