diff options
-rw-r--r-- | packages/browser/src/index.test.ts | 8 | ||||
-rw-r--r-- | packages/browser/src/index.ts | 8 | ||||
-rw-r--r-- | packages/browser/src/methods/startAuthentication.test.ts (renamed from packages/browser/src/methods/startAssertion.test.ts) | 28 | ||||
-rw-r--r-- | packages/browser/src/methods/startAuthentication.ts (renamed from packages/browser/src/methods/startAssertion.ts) | 12 | ||||
-rw-r--r-- | packages/browser/src/methods/startRegistration.test.ts (renamed from packages/browser/src/methods/startAttestation.test.ts) | 22 | ||||
-rw-r--r-- | packages/browser/src/methods/startRegistration.ts (renamed from packages/browser/src/methods/startAttestation.ts) | 16 | ||||
-rw-r--r-- | packages/server/src/authentication/generateAuthenticationOptions.test.ts (renamed from packages/server/src/assertion/generateAssertionOptions.test.ts) | 20 | ||||
-rw-r--r-- | packages/server/src/authentication/generateAuthenticationOptions.ts (renamed from packages/server/src/assertion/generateAssertionOptions.ts) | 12 | ||||
-rw-r--r-- | packages/server/src/authentication/verifyAuthenticationResponse.test.ts (renamed from packages/server/src/assertion/verifyAssertionResponse.test.ts) | 53 | ||||
-rw-r--r-- | packages/server/src/authentication/verifyAuthenticationResponse.ts (renamed from packages/server/src/assertion/verifyAssertionResponse.ts) | 43 | ||||
-rw-r--r-- | packages/server/src/index.test.ts | 16 | ||||
-rw-r--r-- | packages/server/src/index.ts | 44 | ||||
-rw-r--r-- | packages/server/src/registration/generateRegistrationOptions.test.ts (renamed from packages/server/src/attestation/generateAttestationOptions.test.ts) | 18 | ||||
-rw-r--r-- | packages/server/src/registration/generateRegistrationOptions.ts (renamed from packages/server/src/attestation/generateAttestationOptions.ts) | 6 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/tpm/constants.ts (renamed from packages/server/src/attestation/verifications/tpm/constants.ts) | 0 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/tpm/parseCertInfo.ts (renamed from packages/server/src/attestation/verifications/tpm/parseCertInfo.ts) | 0 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/tpm/parsePubArea.ts (renamed from packages/server/src/attestation/verifications/tpm/parsePubArea.ts) | 0 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/tpm/verifyTPM.test.ts (renamed from packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts) | 12 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/tpm/verifyTPM.ts (renamed from packages/server/src/attestation/verifications/tpm/verifyTPM.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyAndroidKey.test.ts (renamed from packages/server/src/attestation/verifications/verifyAndroidKey.test.ts) | 4 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyAndroidKey.ts (renamed from packages/server/src/attestation/verifications/verifyAndroidKey.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyAndroidSafetyNet.test.ts (renamed from packages/server/src/attestation/verifications/verifyAndroidSafetyNet.test.ts) | 0 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyAndroidSafetyNet.ts (renamed from packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyApple.test.ts (renamed from packages/server/src/attestation/verifications/verifyApple.test.ts) | 4 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyApple.ts (renamed from packages/server/src/attestation/verifications/verifyApple.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyFIDOU2F.ts (renamed from packages/server/src/attestation/verifications/verifyFIDOU2F.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyPacked.test.ts (renamed from packages/server/src/attestation/verifications/verifyPacked.test.ts) | 4 | ||||
-rw-r--r-- | packages/server/src/registration/verifications/verifyPacked.ts (renamed from packages/server/src/attestation/verifications/verifyPacked.ts) | 2 | ||||
-rw-r--r-- | packages/server/src/registration/verifyRegistrationResponse.test.ts (renamed from packages/server/src/attestation/verifyAttestationResponse.test.ts) | 142 | ||||
-rw-r--r-- | packages/server/src/registration/verifyRegistrationResponse.ts (renamed from packages/server/src/attestation/verifyAttestationResponse.ts) | 68 | ||||
-rw-r--r-- | packages/typescript-types/src/index.ts | 18 |
31 files changed, 290 insertions, 280 deletions
diff --git a/packages/browser/src/index.test.ts b/packages/browser/src/index.test.ts index 5e396ca..e82191c 100644 --- a/packages/browser/src/index.test.ts +++ b/packages/browser/src/index.test.ts @@ -1,11 +1,11 @@ import * as index from './index'; -test('should export method `startAttestation`', () => { - expect(index.startAttestation).toBeDefined(); +test('should export method `startRegistration`', () => { + expect(index.startRegistration).toBeDefined(); }); -test('should export method `startAssertion`', () => { - expect(index.startAssertion).toBeDefined(); +test('should export method `startAuthentication`', () => { + expect(index.startAuthentication).toBeDefined(); }); test('should export method `browserSupportsWebauthn`', () => { diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 94c9755..4c83297 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -2,14 +2,14 @@ * @packageDocumentation * @module @simplewebauthn/browser */ -import startAttestation from './methods/startAttestation'; -import startAssertion from './methods/startAssertion'; +import startRegistration from './methods/startRegistration'; +import startAuthentication from './methods/startAuthentication'; import { browserSupportsWebauthn } from './helpers/browserSupportsWebauthn'; import { platformAuthenticatorIsAvailable } from './helpers/platformAuthenticatorIsAvailable'; export { - startAttestation, - startAssertion, + startRegistration, + startAuthentication, browserSupportsWebauthn, platformAuthenticatorIsAvailable, }; diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAuthentication.test.ts index 060d918..5e74789 100644 --- a/packages/browser/src/methods/startAssertion.test.ts +++ b/packages/browser/src/methods/startAuthentication.test.ts @@ -1,5 +1,5 @@ import { - AssertionCredential, + AuthenticationCredential, PublicKeyCredentialRequestOptionsJSON, AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, @@ -9,7 +9,7 @@ import { browserSupportsWebauthn } from '../helpers/browserSupportsWebauthn'; import utf8StringToBuffer from '../helpers/utf8StringToBuffer'; import bufferToBase64URLString from '../helpers/bufferToBase64URLString'; -import startAssertion from './startAssertion'; +import startAuthentication from './startAuthentication'; jest.mock('../helpers/browserSupportsWebauthn'); @@ -61,7 +61,7 @@ afterEach(() => { }); test('should convert options before passing to navigator.credentials.get(...)', async done => { - await startAssertion(goodOpts1); + await startAuthentication(goodOpts1); const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; const credId = argsPublicKey.allowCredentials[0].id; @@ -75,7 +75,7 @@ test('should convert options before passing to navigator.credentials.get(...)', }); test('should support optional allowCredential', async () => { - await startAssertion({ + await startAuthentication({ challenge: bufferToBase64URLString(utf8StringToBuffer('fizz')), timeout: 1, }); @@ -84,7 +84,7 @@ test('should support optional allowCredential', async () => { }); test('should convert allow allowCredential to undefined when empty', async () => { - await startAssertion({ + await startAuthentication({ challenge: bufferToBase64URLString(utf8StringToBuffer('fizz')), timeout: 1, allowCredentials: [], @@ -93,7 +93,7 @@ test('should convert allow allowCredential to undefined when empty', async () => }); test('should return base64url-encoded response values', async done => { - mockNavigatorGet.mockImplementation((): Promise<AssertionCredential> => { + mockNavigatorGet.mockImplementation((): Promise<AuthenticationCredential> => { return new Promise(resolve => { resolve({ id: 'foobar', @@ -110,7 +110,7 @@ test('should return base64url-encoded response values', async done => { }); }); - const response = await startAssertion(goodOpts1); + const response = await startAuthentication(goodOpts1); expect(response.rawId).toEqual('Zm9vYmFy'); expect(response.response.authenticatorData).toEqual('bW9ja0F1dGhlbnRpY2F0b3JEYXRh'); @@ -124,7 +124,7 @@ test('should return base64url-encoded response values', async done => { test("should throw error if WebAuthn isn't supported", async done => { mockSupportsWebauthn.mockReturnValue(false); - await expect(startAssertion(goodOpts1)).rejects.toThrow( + await expect(startAuthentication(goodOpts1)).rejects.toThrow( 'WebAuthn is not supported in this browser', ); @@ -138,13 +138,13 @@ test('should throw error if assertion is cancelled for some reason', async done }); }); - await expect(startAssertion(goodOpts1)).rejects.toThrow('Assertion was not completed'); + await expect(startAuthentication(goodOpts1)).rejects.toThrow('Authentication was not completed'); done(); }); test('should handle UTF-8 challenges', async done => { - await startAssertion(goodOpts2UTF8); + await startAuthentication(goodOpts2UTF8); const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; @@ -168,7 +168,7 @@ test('should send extensions to authenticator if present in options', async done ...goodOpts1, extensions, }; - await startAssertion(optsWithExts); + await startAuthentication(optsWithExts); const argsExtensions = mockNavigatorGet.mock.calls[0][0].publicKey.extensions; @@ -178,7 +178,7 @@ test('should send extensions to authenticator if present in options', async done }); test('should not set any extensions if not present in options', async done => { - await startAssertion(goodOpts1); + await startAuthentication(goodOpts1); const argsExtensions = mockNavigatorGet.mock.calls[0][0].publicKey.extensions; @@ -203,7 +203,7 @@ test('should include extension results', async done => { }); // Extensions aren't present in this object, but it doesn't matter since we're faking the response - const response = await startAssertion(goodOpts1); + const response = await startAuthentication(goodOpts1); expect(response.clientExtensionResults).toEqual(extResults); @@ -211,7 +211,7 @@ test('should include extension results', async done => { }); test('should include extension results when no extensions specified', async done => { - const response = await startAssertion(goodOpts1); + const response = await startAuthentication(goodOpts1); expect(response.clientExtensionResults).toEqual({}); diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAuthentication.ts index 26fd755..277b8f0 100644 --- a/packages/browser/src/methods/startAssertion.ts +++ b/packages/browser/src/methods/startAuthentication.ts @@ -1,7 +1,7 @@ import { PublicKeyCredentialRequestOptionsJSON, - AssertionCredential, - AssertionCredentialJSON, + AuthenticationCredential, + AuthenticationCredentialJSON, } from '@simplewebauthn/typescript-types'; import bufferToBase64URLString from '../helpers/bufferToBase64URLString'; @@ -15,9 +15,9 @@ import toPublicKeyCredentialDescriptor from '../helpers/toPublicKeyCredentialDes * * @param requestOptionsJSON Output from @simplewebauthn/server's generateAssertionOptions(...) */ -export default async function startAssertion( +export default async function startAuthentication( requestOptionsJSON: PublicKeyCredentialRequestOptionsJSON, -): Promise<AssertionCredentialJSON> { +): Promise<AuthenticationCredentialJSON> { if (!browserSupportsWebauthn()) { throw new Error('WebAuthn is not supported in this browser'); } @@ -37,10 +37,10 @@ export default async function startAssertion( }; // Wait for the user to complete assertion - const credential = (await navigator.credentials.get({ publicKey })) as AssertionCredential; + const credential = (await navigator.credentials.get({ publicKey })) as AuthenticationCredential; if (!credential) { - throw new Error('Assertion was not completed'); + throw new Error('Authentication was not completed'); } const { id, rawId, response, type } = credential; diff --git a/packages/browser/src/methods/startAttestation.test.ts b/packages/browser/src/methods/startRegistration.test.ts index a6a2beb..e11718e 100644 --- a/packages/browser/src/methods/startAttestation.test.ts +++ b/packages/browser/src/methods/startRegistration.test.ts @@ -1,5 +1,5 @@ import { - AttestationCredential, + RegistrationCredential, AuthenticationExtensionsClientInputs, AuthenticationExtensionsClientOutputs, PublicKeyCredentialCreationOptionsJSON, @@ -9,7 +9,7 @@ import utf8StringToBuffer from '../helpers/utf8StringToBuffer'; import { browserSupportsWebauthn } from '../helpers/browserSupportsWebauthn'; import bufferToBase64URLString from '../helpers/bufferToBase64URLString'; -import startAttestation from './startAttestation'; +import startRegistration from './startRegistration'; jest.mock('../helpers/browserSupportsWebauthn'); @@ -64,7 +64,7 @@ afterEach(() => { }); test('should convert options before passing to navigator.credentials.create(...)', async done => { - await startAttestation(goodOpts1); + await startRegistration(goodOpts1); const argsPublicKey = mockNavigatorCreate.mock.calls[0][0].publicKey; const credId = argsPublicKey.excludeCredentials[0].id; @@ -83,7 +83,7 @@ test('should convert options before passing to navigator.credentials.create(...) }); test('should return base64url-encoded response values', async done => { - mockNavigatorCreate.mockImplementation((): Promise<AttestationCredential> => { + mockNavigatorCreate.mockImplementation((): Promise<RegistrationCredential> => { return new Promise(resolve => { resolve({ id: 'foobar', @@ -98,7 +98,7 @@ test('should return base64url-encoded response values', async done => { }); }); - const response = await startAttestation(goodOpts1); + const response = await startRegistration(goodOpts1); expect(response.rawId).toEqual('Zm9vYmFy'); expect(response.response.attestationObject).toEqual('bW9ja0F0dGU'); @@ -110,7 +110,7 @@ test('should return base64url-encoded response values', async done => { test("should throw error if WebAuthn isn't supported", async done => { mockSupportsWebauthn.mockReturnValue(false); - await expect(startAttestation(goodOpts1)).rejects.toThrow( + await expect(startRegistration(goodOpts1)).rejects.toThrow( 'WebAuthn is not supported in this browser', ); @@ -124,7 +124,7 @@ test('should throw error if attestation is cancelled for some reason', async don }); }); - await expect(startAttestation(goodOpts1)).rejects.toThrow('Attestation was not completed'); + await expect(startRegistration(goodOpts1)).rejects.toThrow('Registration was not completed'); done(); }); @@ -140,7 +140,7 @@ test('should send extensions to authenticator if present in options', async done ...goodOpts1, extensions, }; - await startAttestation(optsWithExts); + await startRegistration(optsWithExts); const argsExtensions = mockNavigatorCreate.mock.calls[0][0].publicKey.extensions; @@ -150,7 +150,7 @@ test('should send extensions to authenticator if present in options', async done }); test('should not set any extensions if not present in options', async done => { - await startAttestation(goodOpts1); + await startRegistration(goodOpts1); const argsExtensions = mockNavigatorCreate.mock.calls[0][0].publicKey.extensions; @@ -175,7 +175,7 @@ test('should include extension results', async done => { }); // Extensions aren't present in this object, but it doesn't matter since we're faking the response - const response = await startAttestation(goodOpts1); + const response = await startRegistration(goodOpts1); expect(response.clientExtensionResults).toEqual(extResults); @@ -183,7 +183,7 @@ test('should include extension results', async done => { }); test('should include extension results when no extensions specified', async done => { - const response = await startAttestation(goodOpts1); + const response = await startRegistration(goodOpts1); expect(response.clientExtensionResults).toEqual({}); diff --git a/packages/browser/src/methods/startAttestation.ts b/packages/browser/src/methods/startRegistration.ts index c762c6f..eec07c5 100644 --- a/packages/browser/src/methods/startAttestation.ts +++ b/packages/browser/src/methods/startRegistration.ts @@ -1,7 +1,7 @@ import { PublicKeyCredentialCreationOptionsJSON, - AttestationCredential, - AttestationCredentialJSON, + RegistrationCredential, + RegistrationCredentialJSON, } from '@simplewebauthn/typescript-types'; import utf8StringToBuffer from '../helpers/utf8StringToBuffer'; @@ -13,11 +13,11 @@ import toPublicKeyCredentialDescriptor from '../helpers/toPublicKeyCredentialDes /** * Begin authenticator "registration" via WebAuthn attestation * - * @param creationOptionsJSON Output from @simplewebauthn/server's generateAttestationOptions(...) + * @param creationOptionsJSON Output from @simplewebauthn/server's generateRegistrationOptions(...) */ -export default async function startAttestation( +export default async function startRegistration( creationOptionsJSON: PublicKeyCredentialCreationOptionsJSON, -): Promise<AttestationCredentialJSON> { +): Promise<RegistrationCredentialJSON> { if (!browserSupportsWebauthn()) { throw new Error('WebAuthn is not supported in this browser'); } @@ -34,16 +34,16 @@ export default async function startAttestation( }; // Wait for the user to complete attestation - const credential = (await navigator.credentials.create({ publicKey })) as AttestationCredential; + const credential = (await navigator.credentials.create({ publicKey })) as RegistrationCredential; if (!credential) { - throw new Error('Attestation was not completed'); + throw new Error('Registration was not completed'); } const { id, rawId, response, type } = credential; // Convert values to base64 to make it easier to send back to the server - const credentialJSON: AttestationCredentialJSON = { + const credentialJSON: RegistrationCredentialJSON = { id, rawId: bufferToBase64URLString(rawId), response: { diff --git a/packages/server/src/assertion/generateAssertionOptions.test.ts b/packages/server/src/authentication/generateAuthenticationOptions.test.ts index 0208d9d..3f11437 100644 --- a/packages/server/src/assertion/generateAssertionOptions.test.ts +++ b/packages/server/src/authentication/generateAuthenticationOptions.test.ts @@ -1,11 +1,11 @@ jest.mock('../helpers/generateChallenge'); -import generateAssertionOptions from './generateAssertionOptions'; +import generateAuthenticationOptions from './generateAuthenticationOptions'; test('should generate credential request options suitable for sending via JSON', () => { const challenge = 'totallyrandomvalue'; - const options = generateAssertionOptions({ + const options = generateAuthenticationOptions({ allowCredentials: [ { id: Buffer.from('1234', 'ascii'), @@ -42,7 +42,7 @@ test('should generate credential request options suitable for sending via JSON', }); test('defaults to 60 seconds if no timeout is specified', () => { - const options = generateAssertionOptions({ + const options = generateAuthenticationOptions({ challenge: 'totallyrandomvalue', allowCredentials: [ { id: Buffer.from('1234', 'ascii'), type: 'public-key' }, @@ -54,7 +54,7 @@ test('defaults to 60 seconds if no timeout is specified', () => { }); test('should not set userVerification if not specified', () => { - const options = generateAssertionOptions({ + const options = generateAuthenticationOptions({ challenge: 'totallyrandomvalue', allowCredentials: [ { id: Buffer.from('1234', 'ascii'), type: 'public-key' }, @@ -66,13 +66,13 @@ test('should not set userVerification if not specified', () => { }); test('should not set allowCredentials if not specified', () => { - const options = generateAssertionOptions({ rpID: 'test' }); + const options = generateAuthenticationOptions({ rpID: 'test' }); expect(options.allowCredentials).toEqual(undefined); }); test('should generate without params', () => { - const options = generateAssertionOptions(); + const options = generateAuthenticationOptions(); const { challenge, ...otherFields } = options; expect(otherFields).toEqual({ allowCredentials: undefined, @@ -85,7 +85,7 @@ test('should generate without params', () => { }); test('should set userVerification if specified', () => { - const options = generateAssertionOptions({ + const options = generateAuthenticationOptions({ challenge: 'totallyrandomvalue', allowCredentials: [ { id: Buffer.from('1234', 'ascii'), type: 'public-key' }, @@ -98,7 +98,7 @@ test('should set userVerification if specified', () => { }); test('should set extensions if specified', () => { - const options = generateAssertionOptions({ + const options = generateAuthenticationOptions({ challenge: 'totallyrandomvalue', allowCredentials: [ { id: Buffer.from('1234', 'ascii'), type: 'public-key' }, @@ -121,7 +121,7 @@ test('should generate a challenge if one is not provided', () => { }; // @ts-ignore 2345 - const options = generateAssertionOptions(opts); + const options = generateAuthenticationOptions(opts); // base64url-encoded 16-byte buffer from mocked `generateChallenge()` expect(options.challenge).toEqual('AQIDBAUGBwgJCgsMDQ4PEA'); @@ -130,7 +130,7 @@ test('should generate a challenge if one is not provided', () => { test('should set rpId if specified', () => { const rpID = 'simplewebauthn.dev'; - const opts = generateAssertionOptions({ + const opts = generateAuthenticationOptions({ allowCredentials: [], rpID, }); diff --git a/packages/server/src/assertion/generateAssertionOptions.ts b/packages/server/src/authentication/generateAuthenticationOptions.ts index 35bf13e..f176835 100644 --- a/packages/server/src/assertion/generateAssertionOptions.ts +++ b/packages/server/src/authentication/generateAuthenticationOptions.ts @@ -8,7 +8,7 @@ import base64url from 'base64url'; import generateChallenge from '../helpers/generateChallenge'; -export type GenerateAssertionOptionsOpts = { +export type GenerateAuthenticationOptionsOpts = { allowCredentials?: PublicKeyCredentialDescriptor[]; challenge?: string | Buffer; timeout?: number; @@ -23,15 +23,15 @@ export type GenerateAssertionOptionsOpts = { * @param allowCredentials Authenticators previously registered by the user, if any. If undefined * the client will ask the user which credential they want to use * @param challenge Random value the authenticator needs to sign and pass back - * user for assertion - * @param timeout How long (in ms) the user can take to complete assertion + * user for authentication + * @param timeout How long (in ms) the user can take to complete authentication * @param userVerification Set to `'discouraged'` when asserting as part of a 2FA flow, otherwise * set to `'preferred'` or `'required'` as desired. - * @param extensions Additional plugins the authenticator or browser should use during assertion + * @param extensions Additional plugins the authenticator or browser should use during authentication * @param rpID Valid domain name (after `https://`) */ -export default function generateAssertionOptions( - options: GenerateAssertionOptionsOpts = {}, +export default function generateAuthenticationOptions( + options: GenerateAuthenticationOptionsOpts = {}, ): PublicKeyCredentialRequestOptionsJSON { const { allowCredentials, diff --git a/packages/server/src/assertion/verifyAssertionResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts index 705f3cb..6b874a5 100644 --- a/packages/server/src/assertion/verifyAssertionResponse.test.ts +++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts @@ -1,10 +1,13 @@ import base64url from 'base64url'; -import verifyAssertionResponse from './verifyAssertionResponse'; +import verifyAuthenticationResponse from './verifyAuthenticationResponse'; import * as decodeClientDataJSON from '../helpers/decodeClientDataJSON'; import * as parseAuthenticatorData from '../helpers/parseAuthenticatorData'; import toHash from '../helpers/toHash'; -import { AuthenticatorDevice, AssertionCredentialJSON } from '@simplewebauthn/typescript-types'; +import { + AuthenticatorDevice, + AuthenticationCredentialJSON, +} from '@simplewebauthn/typescript-types'; let mockDecodeClientData: jest.SpyInstance; let mockParseAuthData: jest.SpyInstance; @@ -20,7 +23,7 @@ afterEach(() => { }); test('should verify an assertion response', () => { - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -32,7 +35,7 @@ test('should verify an assertion response', () => { }); test('should return authenticator info after verification', () => { - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -40,32 +43,32 @@ test('should return authenticator info after verification', () => { authenticator: authenticator, }); - expect(verification.assertionInfo.newCounter).toEqual(144); - expect(verification.assertionInfo.credentialID).toEqual(authenticator.credentialID); + expect(verification.authenticationInfo.newCounter).toEqual(144); + expect(verification.authenticationInfo.credentialID).toEqual(authenticator.credentialID); }); test('should throw when response challenge is not expected value', () => { expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: 'shouldhavebeenthisvalue', expectedOrigin: 'https://different.address', expectedRPID: 'dev.dontneeda.pw', authenticator: authenticator, }); - }).toThrow(/assertion challenge/i); + }).toThrow(/authentication response challenge/i); }); test('should throw when response origin is not expected value', () => { expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: 'https://different.address', expectedRPID: 'dev.dontneeda.pw', authenticator: authenticator, }); - }).toThrow(/assertion origin/i); + }).toThrow(/authentication response origin/i); }); test('should throw when assertion type is not webauthn.create', () => { @@ -77,14 +80,14 @@ test('should throw when assertion type is not webauthn.create', () => { }); expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, expectedRPID: 'dev.dontneeda.pw', authenticator: authenticator, }); - }).toThrow(/assertion type/i); + }).toThrow(/authentication response type/i); }); test('should throw error if user was not present', () => { @@ -94,7 +97,7 @@ test('should throw error if user was not present', () => { }); expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -113,7 +116,7 @@ test('should throw error if previous counter value is not less than in response' }; expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -130,7 +133,7 @@ test('should throw error if assertion RP ID is unexpected value', () => { }); expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -141,7 +144,7 @@ test('should throw error if assertion RP ID is unexpected value', () => { }); test('should not compare counters if both are 0', () => { - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: assertionFirstTimeUsedResponse, expectedChallenge: assertionFirstTimeUsedChallenge, expectedOrigin: assertionFirstTimeUsedOrigin, @@ -166,7 +169,7 @@ test('should throw an error if user verification is required but user was not ve }); expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -181,7 +184,7 @@ test('should throw an error if user verification is required but user was not ve test.skip('should verify TPM assertion', () => { const expectedChallenge = 'dG90YWxseVVuaXF1ZVZhbHVlRXZlcnlBc3NlcnRpb24'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: { id: 'YJ8FMM-AmcUt73XPX341WXWd7ypBMylGjjhu0g3VzME', rawId: 'YJ8FMM-AmcUt73XPX341WXWd7ypBMylGjjhu0g3VzME', @@ -210,7 +213,7 @@ test.skip('should verify TPM assertion', () => { }); test('should support multiple possible origins', () => { - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: ['https://simplewebauthn.dev', assertionOrigin], @@ -223,18 +226,18 @@ test('should support multiple possible origins', () => { test('should throw an error if origin not in list of expected origins', async () => { expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: ['https://simplewebauthn.dev', 'https://fizz.buzz'], expectedRPID: 'dev.dontneeda.pw', authenticator: authenticator, }); - }).toThrow(/unexpected assertion origin/i); + }).toThrow(/unexpected authentication response origin/i); }); test('should support multiple possible RP IDs', async () => { - const verification = verifyAssertionResponse({ + const verification = verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -247,7 +250,7 @@ test('should support multiple possible RP IDs', async () => { test('should throw an error if RP ID not in list of possible RP IDs', async () => { expect(() => { - verifyAssertionResponse({ + verifyAuthenticationResponse({ credential: assertionResponse, expectedChallenge: assertionChallenge, expectedOrigin: assertionOrigin, @@ -261,7 +264,7 @@ test('should throw an error if RP ID not in list of possible RP IDs', async () = * Assertion examples below */ -const assertionResponse: AssertionCredentialJSON = { +const assertionResponse: AuthenticationCredentialJSON = { id: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew', rawId: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew', response: { @@ -293,7 +296,7 @@ const authenticator: AuthenticatorDevice = { /** * Represented a device that's being used on the website for the first time */ -const assertionFirstTimeUsedResponse: AssertionCredentialJSON = { +const assertionFirstTimeUsedResponse: AuthenticationCredentialJSON = { id: 'wSisR0_4hlzw3Y1tj4uNwwifIhRa-ZxWJwWbnfror0pVK9qPdBPO5pW3gasPqn6wXHb0LNhXB_IrA1nFoSQJ9A', rawId: 'wSisR0_4hlzw3Y1tj4uNwwifIhRa-ZxWJwWbnfror0pVK9qPdBPO5pW3gasPqn6wXHb0LNhXB_IrA1nFoSQJ9A', response: { diff --git a/packages/server/src/assertion/verifyAssertionResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts index 2203360..6e5f3e0 100644 --- a/packages/server/src/assertion/verifyAssertionResponse.ts +++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts @@ -1,6 +1,6 @@ import base64url from 'base64url'; import { - AssertionCredentialJSON, + AuthenticationCredentialJSON, AuthenticatorDevice, UserVerificationRequirement, } from '@simplewebauthn/typescript-types'; @@ -12,8 +12,8 @@ import verifySignature from '../helpers/verifySignature'; import parseAuthenticatorData from '../helpers/parseAuthenticatorData'; import isBase64URLString from '../helpers/isBase64URLString'; -export type VerifyAssertionResponseOpts = { - credential: AssertionCredentialJSON; +export type VerifyAuthenticationResponseOpts = { + credential: AuthenticationCredentialJSON; expectedChallenge: string; expectedOrigin: string | string[]; expectedRPID: string | string[]; @@ -29,16 +29,16 @@ export type VerifyAssertionResponseOpts = { * @param credential Authenticator credential returned by browser's `startAssertion()` * @param expectedChallenge The base64url-encoded `options.challenge` returned by * `generateAssertionOptions()` - * @param expectedOrigin Website URL (or array of URLs) that the attestation should have occurred on - * @param expectedRPID RP ID (or array of IDs) that was specified in the attestation options + * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on + * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options * @param authenticator An internal {@link AuthenticatorDevice} matching the credential's ID * @param fidoUserVerification (Optional) The value specified for `userVerification` when calling * `generateAssertionOptions()`. Activates FIDO-specific user presence and verification checks. * Omitting this value defaults verification to a WebAuthn-specific user presence requirement. */ -export default function verifyAssertionResponse( - options: VerifyAssertionResponseOpts, -): VerifiedAssertion { +export default function verifyAuthenticationResponse( + options: VerifyAuthenticationResponseOpts, +): VerifiedAuthenticationResponse { const { credential, expectedChallenge, @@ -78,26 +78,29 @@ export default function verifyAssertionResponse( // Make sure we're handling an assertion if (type !== 'webauthn.get') { - throw new Error(`Unexpected assertion type: ${type}`); + throw new Error(`Unexpected authentication response type: ${type}`); } // Ensure the device provided the challenge we gave it if (challenge !== expectedChallenge) { throw new Error( - `Unexpected assertion challenge "${challenge}", expected "${expectedChallenge}"`, + `Unexpected authentication response challenge "${challenge}", expected "${expectedChallenge}"`, ); } // Check that the origin is our site if (Array.isArray(expectedOrigin)) { if (!expectedOrigin.includes(origin)) { + const joinedExpectedOrigin = expectedOrigin.join(', '); throw new Error( - `Unexpected assertion origin "${origin}", expected one of: ${expectedOrigin.join(', ')}`, + `Unexpected authentication response origin "${origin}", expected one of: ${joinedExpectedOrigin}`, ); } } else { if (origin !== expectedOrigin) { - throw new Error(`Unexpected assertion origin "${origin}", expected "${expectedOrigin}"`); + throw new Error( + `Unexpected authentication response origin "${origin}", expected "${expectedOrigin}"`, + ); } } @@ -158,7 +161,7 @@ export default function verifyAssertionResponse( } else { // WebAuthn only requires the user presence flag be true if (!flags.up) { - throw new Error('User not present during assertion'); + throw new Error('User not present during authentication'); } } @@ -180,7 +183,7 @@ export default function verifyAssertionResponse( const toReturn = { verified: verifySignature(signature, signatureBase, publicKey), - assertionInfo: { + authenticationInfo: { newCounter: counter, credentialID: authenticator.credentialID, }, @@ -190,19 +193,19 @@ export default function verifyAssertionResponse( } /** - * Result of assertion verification + * Result of authentication verification * - * @param verified If the assertion response could be verified - * @param assertionInfo.credentialID The ID of the authenticator used during assertion. + * @param verified If the authentication response could be verified + * @param authenticationInfo.credentialID The ID of the authenticator used during authentication. * Should be used to identify which DB authenticator entry needs its `counter` updated to the value * below - * @param assertionInfo.newCounter The number of times the authenticator identified above + * @param authenticationInfo.newCounter The number of times the authenticator identified above * reported it has been used. **Should be kept in a DB for later reference to help prevent replay * attacks!** */ -export type VerifiedAssertion = { +export type VerifiedAuthenticationResponse = { verified: boolean; - assertionInfo: { + authenticationInfo: { credentialID: Buffer; newCounter: number; }; diff --git a/packages/server/src/index.test.ts b/packages/server/src/index.test.ts index ea02a04..b782ee7 100644 --- a/packages/server/src/index.test.ts +++ b/packages/server/src/index.test.ts @@ -1,17 +1,17 @@ import * as index from './index'; -test('should export method `generateAttestationOptions`', () => { - expect(index.generateAttestationOptions).toBeDefined(); +test('should export method `generateRegistrationOptions`', () => { + expect(index.generateRegistrationOptions).toBeDefined(); }); -test('should export method `verifyAttestationResponse`', () => { - expect(index.verifyAttestationResponse).toBeDefined(); +test('should export method `verifyRegistrationResponse`', () => { + expect(index.verifyRegistrationResponse).toBeDefined(); }); -test('should export method `generateAssertionOptions`', () => { - expect(index.generateAssertionOptions).toBeDefined(); +test('should export method `generateAuthenticationOptions`', () => { + expect(index.generateAuthenticationOptions).toBeDefined(); }); -test('should export method `verifyAssertionResponse`', () => { - expect(index.verifyAssertionResponse).toBeDefined(); +test('should export method `verifyAuthenticationResponse`', () => { + expect(index.verifyAuthenticationResponse).toBeDefined(); }); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 4cc95de..c0af4f5 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -2,40 +2,40 @@ * @packageDocumentation * @module @simplewebauthn/server */ -import generateAttestationOptions from './attestation/generateAttestationOptions'; -import verifyAttestationResponse from './attestation/verifyAttestationResponse'; -import generateAssertionOptions from './assertion/generateAssertionOptions'; -import verifyAssertionResponse from './assertion/verifyAssertionResponse'; +import generateRegistrationOptions from './registration/generateRegistrationOptions'; +import verifyRegistrationResponse from './registration/verifyRegistrationResponse'; +import generateAuthenticationOptions from './authentication/generateAuthenticationOptions'; +import verifyAuthenticationResponse from './authentication/verifyAuthenticationResponse'; import MetadataService from './services/metadataService'; import SettingsService from './services/settingsService'; export { - generateAttestationOptions, - verifyAttestationResponse, - generateAssertionOptions, - verifyAssertionResponse, + generateRegistrationOptions, + verifyRegistrationResponse, + generateAuthenticationOptions as generateAuthenticationOptions, + verifyAuthenticationResponse, MetadataService, SettingsService, }; -import type { GenerateAttestationOptionsOpts } from './attestation/generateAttestationOptions'; -import type { GenerateAssertionOptionsOpts } from './assertion/generateAssertionOptions'; +import type { GenerateRegistrationOptionsOpts } from './registration/generateRegistrationOptions'; +import type { GenerateAuthenticationOptionsOpts } from './authentication/generateAuthenticationOptions'; import type { MetadataStatement } from './metadata/mdsTypes'; import type { - VerifiedAttestation, - VerifyAttestationResponseOpts, -} from './attestation/verifyAttestationResponse'; + VerifiedRegistrationResponse, + VerifyRegistrationResponseOpts, +} from './registration/verifyRegistrationResponse'; import type { - VerifiedAssertion, - VerifyAssertionResponseOpts, -} from './assertion/verifyAssertionResponse'; + VerifiedAuthenticationResponse, + VerifyAuthenticationResponseOpts, +} from './authentication/verifyAuthenticationResponse'; export type { - GenerateAttestationOptionsOpts, - GenerateAssertionOptionsOpts, + GenerateRegistrationOptionsOpts, + GenerateAuthenticationOptionsOpts, MetadataStatement, - VerifyAttestationResponseOpts, - VerifyAssertionResponseOpts, - VerifiedAttestation, - VerifiedAssertion, + VerifyRegistrationResponseOpts, + VerifyAuthenticationResponseOpts, + VerifiedRegistrationResponse, + VerifiedAuthenticationResponse, }; diff --git a/packages/server/src/attestation/generateAttestationOptions.test.ts b/packages/server/src/registration/generateRegistrationOptions.test.ts index eb7dcd7..19fa0a6 100644 --- a/packages/server/src/attestation/generateAttestationOptions.test.ts +++ b/packages/server/src/registration/generateRegistrationOptions.test.ts @@ -1,6 +1,6 @@ jest.mock('../helpers/generateChallenge'); -import generateAttestationOptions from './generateAttestationOptions'; +import generateRegistrationOptions from './generateRegistrationOptions'; test('should generate credential request options suitable for sending via JSON', () => { const rpName = 'SimpleWebAuthn'; @@ -11,7 +11,7 @@ test('should generate credential request options suitable for sending via JSON', const timeout = 1; const attestationType = 'indirect'; - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName, rpID, challenge, @@ -55,7 +55,7 @@ test('should generate credential request options suitable for sending via JSON', }); test('should map excluded credential IDs if specified', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName: 'SimpleWebAuthn', rpID: 'not.real', challenge: 'totallyrandomvalue', @@ -80,7 +80,7 @@ test('should map excluded credential IDs if specified', () => { }); test('defaults to 60 seconds if no timeout is specified', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName: 'SimpleWebAuthn', rpID: 'not.real', challenge: 'totallyrandomvalue', @@ -92,7 +92,7 @@ test('defaults to 60 seconds if no timeout is specified', () => { }); test('defaults to none attestation if no attestation type is specified', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName: 'SimpleWebAuthn', rpID: 'not.real', challenge: 'totallyrandomvalue', @@ -104,7 +104,7 @@ test('defaults to none attestation if no attestation type is specified', () => { }); test('should set authenticatorSelection if specified', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName: 'SimpleWebAuthn', rpID: 'not.real', challenge: 'totallyrandomvalue', @@ -125,7 +125,7 @@ test('should set authenticatorSelection if specified', () => { }); test('should set extensions if specified', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpName: 'SimpleWebAuthn', rpID: 'not.real', challenge: 'totallyrandomvalue', @@ -140,7 +140,7 @@ test('should set extensions if specified', () => { }); test('should generate a challenge if one is not provided', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpID: 'not.real', rpName: 'SimpleWebAuthn', userID: '1234', @@ -152,7 +152,7 @@ test('should generate a challenge if one is not provided', () => { }); test('should use custom supported algorithm IDs as-is when provided', () => { - const options = generateAttestationOptions({ + const options = generateRegistrationOptions({ rpID: 'not.real', rpName: 'SimpleWebAuthn', userID: '1234', diff --git a/packages/server/src/attestation/generateAttestationOptions.ts b/packages/server/src/registration/generateRegistrationOptions.ts index 6faf30c..c55b308 100644 --- a/packages/server/src/attestation/generateAttestationOptions.ts +++ b/packages/server/src/registration/generateRegistrationOptions.ts @@ -11,7 +11,7 @@ import base64url from 'base64url'; import generateChallenge from '../helpers/generateChallenge'; -export type GenerateAttestationOptionsOpts = { +export type GenerateRegistrationOptionsOpts = { rpName: string; rpID: string; userID: string; @@ -93,8 +93,8 @@ const defaultSupportedAlgorithmIDs = supportedCOSEAlgorithmIdentifiers.filter(id * @param supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for * attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms */ -export default function generateAttestationOptions( - options: GenerateAttestationOptionsOpts, +export default function generateRegistrationOptions( + options: GenerateRegistrationOptionsOpts, ): PublicKeyCredentialCreationOptionsJSON { const { rpName, diff --git a/packages/server/src/attestation/verifications/tpm/constants.ts b/packages/server/src/registration/verifications/tpm/constants.ts index 9b9cfa6..9b9cfa6 100644 --- a/packages/server/src/attestation/verifications/tpm/constants.ts +++ b/packages/server/src/registration/verifications/tpm/constants.ts diff --git a/packages/server/src/attestation/verifications/tpm/parseCertInfo.ts b/packages/server/src/registration/verifications/tpm/parseCertInfo.ts index 1ac391e..1ac391e 100644 --- a/packages/server/src/attestation/verifications/tpm/parseCertInfo.ts +++ b/packages/server/src/registration/verifications/tpm/parseCertInfo.ts diff --git a/packages/server/src/attestation/verifications/tpm/parsePubArea.ts b/packages/server/src/registration/verifications/tpm/parsePubArea.ts index 5f0d63b..5f0d63b 100644 --- a/packages/server/src/attestation/verifications/tpm/parsePubArea.ts +++ b/packages/server/src/registration/verifications/tpm/parsePubArea.ts diff --git a/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts b/packages/server/src/registration/verifications/tpm/verifyTPM.test.ts index 81f1fbe..819ed81 100644 --- a/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts +++ b/packages/server/src/registration/verifications/tpm/verifyTPM.test.ts @@ -1,10 +1,10 @@ -import verifyAttestationResponse from '../../verifyAttestationResponse'; +import verifyRegistrationResponse from '../../verifyRegistrationResponse'; import base64url from 'base64url'; test('should verify TPM response', async () => { const expectedChallenge = 'a4de0d36-057d-4e9d-831a-2c578fa89170'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'SErwRhxIzjPowcnM3e-D-u89EQXLUe1NYewpshd7Mc0', rawId: 'SErwRhxIzjPowcnM3e-D-u89EQXLUe1NYewpshd7Mc0', @@ -34,7 +34,7 @@ test('should verify SHA1 TPM response', async () => { const expectedChallenge = '9JyUfJkg8PqoKZuD7FHzOE9dbyculC9urGTpGqBnEwnhKmni4rGRXxm3-ZBHK8x6riJQqIpC8qEa-T0qIFTKTQ'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { rawId: 'UJDoUJoGiDQF_EEZ3G_z9Lfq16_KFaXtMTjwTUrrRlc', id: 'UJDoUJoGiDQF_EEZ3G_z9Lfq16_KFaXtMTjwTUrrRlc', @@ -64,7 +64,7 @@ test('should verify SHA256 TPM response', async () => { const expectedChallenge = 'gHrAk4pNe2VlB0HLeKclI2P6QEa83PuGeijTHMtpbhY9KlybyhlwF_VzRe7yhabXagWuY6rkDWfvvhNqgh2o7A'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { rawId: 'h9XMhkVePN1Prq9Ks_VfwIsVZvt-jmSRTEnevTc-KB8', id: 'h9XMhkVePN1Prq9Ks_VfwIsVZvt-jmSRTEnevTc-KB8', @@ -101,7 +101,7 @@ test('should verify TPM response with spec-compliant tcgAtTpm SAN structure', as */ const expectedChallenge = 'VfmZXKDxqdoXFMHXO3SE2Q2b8u5Ki64OL_XICELcGKg'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'LVwzXx0fStkvsos_jdl9DTd6O3-6be8Ua4tcdXc5XeM', rawId: 'LVwzXx0fStkvsos_jdl9DTd6O3-6be8Ua4tcdXc5XeM', @@ -134,7 +134,7 @@ test('should verify TPM response with non-spec-compliant tcgAtTpm SAN structure' */ const expectedChallenge = '4STWgmXrgJxzigqe6nFuIg'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'X7TPi7o8WfiIz1bP0Vciz1xRvSMyiitgOR1sUqY724s', rawId: 'X7TPi7o8WfiIz1bP0Vciz1xRvSMyiitgOR1sUqY724s', diff --git a/packages/server/src/attestation/verifications/tpm/verifyTPM.ts b/packages/server/src/registration/verifications/tpm/verifyTPM.ts index e2fb772..7579f1f 100644 --- a/packages/server/src/attestation/verifications/tpm/verifyTPM.ts +++ b/packages/server/src/registration/verifications/tpm/verifyTPM.ts @@ -8,7 +8,7 @@ import { Name, } from '@peculiar/asn1-x509'; -import type { AttestationFormatVerifierOpts } from '../../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../../verifyRegistrationResponse'; import decodeCredentialPublicKey from '../../../helpers/decodeCredentialPublicKey'; import { COSEKEYS, COSEALGHASH } from '../../../helpers/convertCOSEtoPKCS'; diff --git a/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts b/packages/server/src/registration/verifications/verifyAndroidKey.test.ts index d1f7a4e..d7f5ce7 100644 --- a/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts +++ b/packages/server/src/registration/verifications/verifyAndroidKey.test.ts @@ -2,7 +2,7 @@ import base64url from 'base64url'; import SettingsService from '../../services/settingsService'; -import verifyAttestationResponse from '../verifyAttestationResponse'; +import verifyRegistrationResponse from '../verifyRegistrationResponse'; /** * Clear out root certs for android-key since responses were captured from FIDO Conformance testing @@ -13,7 +13,7 @@ SettingsService.setRootCertificates({ identifier: 'android-key', certificates: [ test('should verify Android KeyStore response', async () => { const expectedChallenge = '4ab7dfd1-a695-4777-985f-ad2993828e99'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'V51GE29tGbhby7sbg1cZ_qL8V8njqEsXpAnwQBobvgw', rawId: 'V51GE29tGbhby7sbg1cZ_qL8V8njqEsXpAnwQBobvgw', diff --git a/packages/server/src/attestation/verifications/verifyAndroidKey.ts b/packages/server/src/registration/verifications/verifyAndroidKey.ts index 29d184e..391f8eb 100644 --- a/packages/server/src/attestation/verifications/verifyAndroidKey.ts +++ b/packages/server/src/registration/verifications/verifyAndroidKey.ts @@ -2,7 +2,7 @@ import { AsnParser } from '@peculiar/asn1-schema'; import { Certificate } from '@peculiar/asn1-x509'; import { KeyDescription, id_ce_keyDescription } from '@peculiar/asn1-android'; -import type { AttestationFormatVerifierOpts } from '../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; import convertCertBufferToPEM from '../../helpers/convertCertBufferToPEM'; import validateCertificatePath from '../../helpers/validateCertificatePath'; diff --git a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.test.ts b/packages/server/src/registration/verifications/verifyAndroidSafetyNet.test.ts index 5b71663..5b71663 100644 --- a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.test.ts +++ b/packages/server/src/registration/verifications/verifyAndroidSafetyNet.test.ts diff --git a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts b/packages/server/src/registration/verifications/verifyAndroidSafetyNet.ts index 85eaba9..4375efa 100644 --- a/packages/server/src/attestation/verifications/verifyAndroidSafetyNet.ts +++ b/packages/server/src/registration/verifications/verifyAndroidSafetyNet.ts @@ -1,6 +1,6 @@ import base64url from 'base64url'; -import type { AttestationFormatVerifierOpts } from '../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; import toHash from '../../helpers/toHash'; import verifySignature from '../../helpers/verifySignature'; diff --git a/packages/server/src/attestation/verifications/verifyApple.test.ts b/packages/server/src/registration/verifications/verifyApple.test.ts index 6ba0a5e..e1f5e89 100644 --- a/packages/server/src/attestation/verifications/verifyApple.test.ts +++ b/packages/server/src/registration/verifications/verifyApple.test.ts @@ -1,11 +1,11 @@ import base64url from 'base64url'; -import verifyAttestationResponse from '../verifyAttestationResponse'; +import verifyRegistrationResponse from '../verifyRegistrationResponse'; test('should verify Apple attestation', async () => { const expectedChallenge = 'h5xSyIRMx2IQPr1mQk6GD98XSQOBHgMHVpJIkMV9Nkc'; jest.spyOn(base64url, 'encode').mockReturnValueOnce(expectedChallenge); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'J4lAqPXhefDrUD7oh5LQMbBH5TE', rawId: 'J4lAqPXhefDrUD7oh5LQMbBH5TE', diff --git a/packages/server/src/attestation/verifications/verifyApple.ts b/packages/server/src/registration/verifications/verifyApple.ts index d0c3059..00b21ff 100644 --- a/packages/server/src/attestation/verifications/verifyApple.ts +++ b/packages/server/src/registration/verifications/verifyApple.ts @@ -1,7 +1,7 @@ import { AsnParser } from '@peculiar/asn1-schema'; import { Certificate } from '@peculiar/asn1-x509'; -import type { AttestationFormatVerifierOpts } from '../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; import validateCertificatePath from '../../helpers/validateCertificatePath'; import convertCertBufferToPEM from '../../helpers/convertCertBufferToPEM'; diff --git a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts b/packages/server/src/registration/verifications/verifyFIDOU2F.ts index a2bfd53..f11a1c9 100644 --- a/packages/server/src/attestation/verifications/verifyFIDOU2F.ts +++ b/packages/server/src/registration/verifications/verifyFIDOU2F.ts @@ -1,4 +1,4 @@ -import type { AttestationFormatVerifierOpts } from '../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; import convertCOSEtoPKCS from '../../helpers/convertCOSEtoPKCS'; import convertCertBufferToPEM from '../../helpers/convertCertBufferToPEM'; diff --git a/packages/server/src/attestation/verifications/verifyPacked.test.ts b/packages/server/src/registration/verifications/verifyPacked.test.ts index 5884e53..43c7d9d 100644 --- a/packages/server/src/attestation/verifications/verifyPacked.test.ts +++ b/packages/server/src/registration/verifications/verifyPacked.test.ts @@ -1,4 +1,4 @@ -import verifyAttestationResponse from '../verifyAttestationResponse'; +import verifyRegistrationResponse from '../verifyRegistrationResponse'; test('should verify (broken) Packed response from Chrome virtual authenticator', async () => { /** @@ -11,7 +11,7 @@ test('should verify (broken) Packed response from Chrome virtual authenticator', * virtual authenticators should stop returning faulty values like this one starting in Chrome 91. * This unit test will remain for now in case this issue comes up again. */ - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: '5Hwc78jGjXrzOS8Mke9KhFZEtX54iYD-UEBKgvMXM64', rawId: '5Hwc78jGjXrzOS8Mke9KhFZEtX54iYD-UEBKgvMXM64', diff --git a/packages/server/src/attestation/verifications/verifyPacked.ts b/packages/server/src/registration/verifications/verifyPacked.ts index c285ec0..41fddf1 100644 --- a/packages/server/src/attestation/verifications/verifyPacked.ts +++ b/packages/server/src/registration/verifications/verifyPacked.ts @@ -1,7 +1,7 @@ import elliptic from 'elliptic'; import NodeRSA from 'node-rsa'; -import type { AttestationFormatVerifierOpts } from '../verifyAttestationResponse'; +import type { AttestationFormatVerifierOpts } from '../verifyRegistrationResponse'; import convertCOSEtoPKCS, { COSEKEYS, diff --git a/packages/server/src/attestation/verifyAttestationResponse.test.ts b/packages/server/src/registration/verifyRegistrationResponse.test.ts index 977b152..cc88d72 100644 --- a/packages/server/src/attestation/verifyAttestationResponse.test.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.test.ts @@ -1,6 +1,6 @@ import base64url from 'base64url'; -import verifyAttestationResponse from './verifyAttestationResponse'; +import verifyRegistrationResponse from './verifyRegistrationResponse'; import * as decodeAttestationObject from '../helpers/decodeAttestationObject'; import * as decodeClientDataJSON from '../helpers/decodeClientDataJSON'; @@ -11,7 +11,7 @@ import SettingsService from '../services/settingsService'; import * as verifyFIDOU2F from './verifications/verifyFIDOU2F'; import toHash from '../helpers/toHash'; -import { AttestationCredentialJSON } from '@simplewebauthn/typescript-types'; +import { RegistrationCredentialJSON } from '@simplewebauthn/typescript-types'; /** * Clear out root certs for android-key since responses were captured from FIDO Conformance testing @@ -42,7 +42,7 @@ afterEach(() => { }); test('should verify FIDO U2F attestation', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationFIDOU2F, expectedChallenge: attestationFIDOU2FChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -50,28 +50,28 @@ test('should verify FIDO U2F attestation', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('fido-u2f'); - expect(verification.attestationInfo?.counter).toEqual(0); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('fido-u2f'); + expect(verification.registrationInfo?.counter).toEqual(0); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pQECAyYgASFYIMiRyw5pUoMhBjCrcQND6lJPaRHA0f-XWcKBb5ZwWk1eIlggFJu6aan4o7epl6qa9n9T-6KsIMvZE2PcTnLj8rN58is', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer( 'VHzbxaYaJu2P8m1Y2iHn2gRNHrgK0iYbn9E978L3Qi7Q-chFeicIHwYCRophz5lth2nCgEVKcgWirxlgidgbUQ', ), ); - expect(verification.attestationInfo?.aaguid).toEqual('00000000-0000-0000-0000-000000000000'); - expect(verification.attestationInfo?.credentialType).toEqual('public-key'); - expect(verification.attestationInfo?.userVerified).toEqual(false); - expect(verification.attestationInfo?.attestationObject).toEqual( + expect(verification.registrationInfo?.aaguid).toEqual('00000000-0000-0000-0000-000000000000'); + expect(verification.registrationInfo?.credentialType).toEqual('public-key'); + expect(verification.registrationInfo?.userVerified).toEqual(false); + expect(verification.registrationInfo?.attestationObject).toEqual( base64url.toBuffer(attestationFIDOU2F.response.attestationObject), ); }); test('should verify Packed (EC2) attestation', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationPacked, expectedChallenge: attestationPackedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -79,14 +79,14 @@ test('should verify Packed (EC2) attestation', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('packed'); - expect(verification.attestationInfo?.counter).toEqual(1589874425); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('packed'); + expect(verification.registrationInfo?.counter).toEqual(1589874425); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pQECAyYgASFYIEoxVVqK-oIGmqoDEyO4KjmMx5R2HeMM4LQQXh8sE01PIlggtzuuoMN5fWnAIuuXdlfshOGu1k3ApBUtDJ8eKiuo_6c', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer( 'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3U' + 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q', @@ -95,7 +95,7 @@ test('should verify Packed (EC2) attestation', async () => { }); test('should verify Packed (X5C) attestation', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationPackedX5C, expectedChallenge: attestationPackedX5CChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -103,14 +103,14 @@ test('should verify Packed (X5C) attestation', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('packed'); - expect(verification.attestationInfo?.counter).toEqual(28); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('packed'); + expect(verification.registrationInfo?.counter).toEqual(28); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pQECAyYgASFYIGwlsYCNyRb4AD9cyTw6cH5VS-uzflmmO1UldGGe9eIaIlggvadzKD8p6wKLjgYfxRxldjCMGRV0YyM13osWbKIPrF8', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer( '4rrvMciHCkdLQ2HghazIp1sMc8TmV8W8RgoX-x8tqV_1AmlqWACqUK8mBGLandr-htduQKPzgb2yWxOFV56Tlg', ), @@ -118,7 +118,7 @@ test('should verify Packed (X5C) attestation', async () => { }); test('should verify None attestation', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -126,14 +126,14 @@ test('should verify None attestation', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('none'); - expect(verification.attestationInfo?.counter).toEqual(0); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('none'); + expect(verification.registrationInfo?.counter).toEqual(0); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pQECAyYgASFYID5PQTZQQg6haZFQWFzqfAOyQ_ENsMH8xxQ4GRiNPsqrIlggU8IVUOV8qpgk_Jh-OTaLuZL52KdX1fTht07X4DiQPow', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer( 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY', ), @@ -142,7 +142,7 @@ test('should verify None attestation', async () => { test('should verify None attestation w/RSA public key', async () => { const expectedChallenge = 'pYZ3VX2yb8dS9yplNxJChiXhPGBk8gZzTAyJ2iU5x1k'; - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo', rawId: 'kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo', @@ -161,38 +161,38 @@ test('should verify None attestation w/RSA public key', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('none'); - expect(verification.attestationInfo?.counter).toEqual(0); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('none'); + expect(verification.registrationInfo?.counter).toEqual(0); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pAEDAzkBACBZAQDxfpXrj0ba_AH30JJ_-W7BHSOPugOD8aEDdNBKc1gjB9AmV3FPl2aL0fwiOMKtM_byI24qXb2FzcyjC7HUVkHRtzkAQnahXckI4wY_01koaY6iwXuIE3Ya0Zjs2iZyz6u4G_abGnWdObqa_kHxc3CHR7Xy5MDkAkKyX6TqU0tgHZcEhDd_Lb5ONJDwg4wvKlZBtZYElfMuZ6lonoRZ7qR_81rGkDZyFaxp6RlyvzEbo4ijeIaHQylqCz-oFm03ifZMOfRHYuF4uTjJDRH-g4BW1f3rdi7DTHk1hJnIw1IyL_VFIQ9NifkAguYjNCySCUNpYli2eMrPhAu5dYJFFjINIUMBAAE', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer('kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo'), ); }); test('should throw when response challenge is not expected value', async () => { await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: 'shouldhavebeenthisvalue', expectedOrigin: 'https://dev.dontneeda.pw', expectedRPID: 'dev.dontneeda.pw', }), - ).rejects.toThrow(/attestation challenge/i); + ).rejects.toThrow(/registration response challenge/i); }); test('should throw when response origin is not expected value', async () => { await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://different.address', expectedRPID: 'dev.dontneeda.pw', }), - ).rejects.toThrow(/attestation origin/i); + ).rejects.toThrow(/registration response origin/i); }); test('should throw when attestation type is not webauthn.create', async () => { @@ -207,13 +207,13 @@ test('should throw when attestation type is not webauthn.create', async () => { }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: challenge, expectedOrigin: origin, expectedRPID: 'dev.dontneeda.pw', }), - ).rejects.toThrow(/attestation type/i); + ).rejects.toThrow(/registration response type/i); }); test('should throw if an unexpected attestation format is specified', async () => { @@ -230,7 +230,7 @@ test('should throw if an unexpected attestation format is specified', async () = }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -251,7 +251,7 @@ test('should throw error if assertion RP ID is unexpected value', async () => { }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -269,7 +269,7 @@ test('should throw error if user was not present', async () => { }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -288,7 +288,7 @@ test('should throw if the authenticator does not give back credential ID', async }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -308,7 +308,7 @@ test('should throw if the authenticator does not give back credential public key }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -325,7 +325,7 @@ test('should throw error if no alg is specified in public key', async () => { }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -342,7 +342,7 @@ test('should throw error if unsupported alg is used', async () => { }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -354,7 +354,7 @@ test('should throw error if unsupported alg is used', async () => { test('should not include authenticator info if not verified', async () => { mockVerifyFIDOU2F.mockReturnValue(false); - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationFIDOU2F, expectedChallenge: attestationFIDOU2FChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -362,7 +362,7 @@ test('should not include authenticator info if not verified', async () => { }); expect(verification.verified).toBe(false); - expect(verification.attestationInfo).toBeUndefined(); + expect(verification.registrationInfo).toBeUndefined(); }); test('should throw an error if user verification is required but user was not verified', async () => { @@ -375,7 +375,7 @@ test('should throw an error if user verification is required but user was not ve }); await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationFIDOU2F, expectedChallenge: attestationFIDOU2FChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -387,7 +387,7 @@ test('should throw an error if user verification is required but user was not ve test('should validate TPM RSA response (SHA256)', async () => { const expectedChallenge = '3a07cf85-e7b6-447f-8270-b25433f6018e'; - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM', rawId: 'lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM', @@ -406,21 +406,21 @@ test('should validate TPM RSA response (SHA256)', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('tpm'); - expect(verification.attestationInfo?.counter).toEqual(30); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('tpm'); + expect(verification.registrationInfo?.counter).toEqual(30); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pAEDAzkBACBZAQCtxzw59Wsl8xWP97wPTu2TSDlushwshL8GedHAHO1R62m3nNy21hCLJlQabfLepRUQ_v9mq3PCmV81tBSqtRGU5_YlK0R2yeu756SnT39c6hKC3PBPt_xdjL_ccz4H_73DunfB63QZOtdeAsswV7WPLqMARofuM-LQ_LHnNguCypDcxhADuUqQtogfwZsknTVIPxzGcfqnQ7ERF9D9AOWIQ8YjOsTi_B2zS8SOySKIFUGwwYcPG7DiCE-QJcI-fpydRDnEq6UxbkYgB7XK4BlmPKlwuXkBDX9egl_Ma4B7W2WJvYbKevu6Z8Kc5y-OITpNVDYKbBK3qKyh4yIUpB1NIUMBAAE', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer('lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM'), ); }); test('should validate TPM RSA response (SHA1)', async () => { const expectedChallenge = 'f4e8d87b-d363-47cc-ab4d-1a84647bf245'; - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU', rawId: 'oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU', @@ -439,21 +439,21 @@ test('should validate TPM RSA response (SHA1)', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('tpm'); - expect(verification.attestationInfo?.counter).toEqual(97); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('tpm'); + expect(verification.registrationInfo?.counter).toEqual(97); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pAEDAzn__iBZAQCzl_wD24PZ5z-po2FrwoQVdd13got_CkL8p4B_NvJBC5OwAYKDilii_wj-0CA8ManbpSInx9Tdnz6t91OhudwUT0-W_BHSLK_MqFcjZWrR5LYVmVpz1EgH3DrOTra4AlogEq2D2CYktPrPe7joE-oT3vAYXK8vzQDLRyaxI_Z1qS4KLlLCdajW8PGpw1YRjMDw6s69GZU8mXkgNPMCUh1TZ1bnCvJTO9fnmLjDjqdQGRU4bWo8tFjCL8g1-2WD_2n0-twt6n-Uox5VnR1dQJG4awMlanBCkGGpOb3WBDQ8K10YJJ2evPhJKGJahBvu2Dxmq6pLCAXCv0ma3EHj-PmDIUMBAAE', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer('oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU'), ); }); test('should validate Android-Key response', async () => { const expectedChallenge = '14e0d1b6-9c36-4849-aeec-ea64676449ef'; - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: { id: 'PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o', rawId: 'PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o', @@ -472,20 +472,20 @@ test('should validate Android-Key response', async () => { }); expect(verification.verified).toEqual(true); - expect(verification.attestationInfo?.fmt).toEqual('android-key'); - expect(verification.attestationInfo?.counter).toEqual(108); - expect(verification.attestationInfo?.credentialPublicKey).toEqual( + expect(verification.registrationInfo?.fmt).toEqual('android-key'); + expect(verification.registrationInfo?.counter).toEqual(108); + expect(verification.registrationInfo?.credentialPublicKey).toEqual( base64url.toBuffer( 'pQECAyYgASFYIEjCq7woGNN_42rbaqMgJvz0nuKTWNRrR29lMX3J239oIlgg6IcAXqPJPIjSrClHDAmbJv_EShYhYq0R9-G3k744n7Y', ), ); - expect(verification.attestationInfo?.credentialID).toEqual( + expect(verification.registrationInfo?.credentialID).toEqual( base64url.toBuffer('PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o'), ); }); test('should support multiple possible origins', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: ['https://dev.dontneeda.pw', 'https://different.address'], @@ -497,17 +497,17 @@ test('should support multiple possible origins', async () => { test('should throw an error if origin not in list of expected origins', async () => { await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: ['https://different.address'], expectedRPID: 'dev.dontneeda.pw', }), - ).rejects.toThrow(/unexpected attestation origin/i); + ).rejects.toThrow(/unexpected registration response origin/i); }); test('should support multiple possible RP IDs', async () => { - const verification = await verifyAttestationResponse({ + const verification = await verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -519,7 +519,7 @@ test('should support multiple possible RP IDs', async () => { test('should throw an error if RP ID not in list of possible RP IDs', async () => { await expect( - verifyAttestationResponse({ + verifyRegistrationResponse({ credential: attestationNone, expectedChallenge: attestationNoneChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -532,7 +532,7 @@ test('should throw an error if RP ID not in list of possible RP IDs', async () = * Various Attestations Below */ -const attestationFIDOU2F: AttestationCredentialJSON = { +const attestationFIDOU2F: RegistrationCredentialJSON = { id: 'VHzbxaYaJu2P8m1Y2iHn2gRNHrgK0iYbn9E978L3Qi7Q-chFeicIHwYCRophz5lth2nCgEVKcgWirxlgidgbUQ', rawId: 'VHzbxaYaJu2P8m1Y2iHn2gRNHrgK0iYbn9E978L3Qi7Q-chFeicIHwYCRophz5lth2nCgEVKcgWirxlgidgbUQ', response: { @@ -546,7 +546,7 @@ const attestationFIDOU2F: AttestationCredentialJSON = { }; const attestationFIDOU2FChallenge = base64url.encode('totallyUniqueValueEveryAttestation'); -const attestationPacked: AttestationCredentialJSON = { +const attestationPacked: RegistrationCredentialJSON = { id: 'bbb', rawId: 'bbb', response: { @@ -567,7 +567,7 @@ const attestationPacked: AttestationCredentialJSON = { }; const attestationPackedChallenge = base64url.encode('s6PIbBnPPnrGNSBxNdtDrT7UrVYJK9HM'); -const attestationPackedX5C: AttestationCredentialJSON = { +const attestationPackedX5C: RegistrationCredentialJSON = { // TODO: Grab these from another iPhone attestation id: 'aaa', rawId: 'aaa', @@ -598,7 +598,7 @@ const attestationPackedX5C: AttestationCredentialJSON = { }; const attestationPackedX5CChallenge = base64url.encode('totallyUniqueValueEveryTime'); -const attestationNone: AttestationCredentialJSON = { +const attestationNone: RegistrationCredentialJSON = { id: 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY', rawId: 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY', response: { diff --git a/packages/server/src/attestation/verifyAttestationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts index 02e8ef0..555f877 100644 --- a/packages/server/src/attestation/verifyAttestationResponse.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.ts @@ -1,6 +1,6 @@ import base64url from 'base64url'; import { - AttestationCredentialJSON, + RegistrationCredentialJSON, COSEAlgorithmIdentifier, } from '@simplewebauthn/typescript-types'; @@ -16,7 +16,7 @@ import { COSEKEYS } from '../helpers/convertCOSEtoPKCS'; import convertAAGUIDToString from '../helpers/convertAAGUIDToString'; import settingsService from '../services/settingsService'; -import { supportedCOSEAlgorithmIdentifiers } from './generateAttestationOptions'; +import { supportedCOSEAlgorithmIdentifiers } from './generateRegistrationOptions'; import verifyFIDOU2F from './verifications/verifyFIDOU2F'; import verifyPacked from './verifications/verifyPacked'; import verifyAndroidSafetynet from './verifications/verifyAndroidSafetyNet'; @@ -24,8 +24,8 @@ import verifyTPM from './verifications/tpm/verifyTPM'; import verifyAndroidKey from './verifications/verifyAndroidKey'; import verifyApple from './verifications/verifyApple'; -export type VerifyAttestationResponseOpts = { - credential: AttestationCredentialJSON; +export type VerifyRegistrationResponseOpts = { + credential: RegistrationCredentialJSON; expectedChallenge: string; expectedOrigin: string | string[]; expectedRPID?: string | string[]; @@ -38,19 +38,19 @@ export type VerifyAttestationResponseOpts = { * * **Options:** * - * @param credential Authenticator credential returned by browser's `startAttestation()` + * @param credential Authenticator credential returned by browser's `startAuthentication()` * @param expectedChallenge The base64url-encoded `options.challenge` returned by - * `generateAttestationOptions()` - * @param expectedOrigin Website URL (or array of URLs) that the attestation should have occurred on - * @param expectedRPID RP ID (or array of IDs) that was specified in the attestation options + * `generateRegistrationOptions()` + * @param expectedOrigin Website URL (or array of URLs) that the registration should have occurred on + * @param expectedRPID RP ID (or array of IDs) that was specified in the registration options * @param requireUserVerification (Optional) Enforce user verification by the authenticator * (via PIN, fingerprint, etc...) * @param supportedAlgorithmIDs Array of numeric COSE algorithm identifiers supported for * attestation by this RP. See https://www.iana.org/assignments/cose/cose.xhtml#algorithms */ -export default async function verifyAttestationResponse( - options: VerifyAttestationResponseOpts, -): Promise<VerifiedAttestation> { +export default async function verifyRegistrationResponse( + options: VerifyRegistrationResponseOpts, +): Promise<VerifiedRegistrationResponse> { const { credential, expectedChallenge, @@ -80,15 +80,15 @@ export default async function verifyAttestationResponse( const { type, origin, challenge, tokenBinding } = clientDataJSON; - // Make sure we're handling an attestation + // Make sure we're handling an registration if (type !== 'webauthn.create') { - throw new Error(`Unexpected attestation type: ${type}`); + throw new Error(`Unexpected registration response type: ${type}`); } // Ensure the device provided the challenge we gave it if (challenge !== expectedChallenge) { throw new Error( - `Unexpected attestation challenge "${challenge}", expected "${expectedChallenge}"`, + `Unexpected registration response challenge "${challenge}", expected "${expectedChallenge}"`, ); } @@ -96,12 +96,16 @@ export default async function verifyAttestationResponse( if (Array.isArray(expectedOrigin)) { if (!expectedOrigin.includes(origin)) { throw new Error( - `Unexpected attestation origin "${origin}", expected one of: ${expectedOrigin.join(', ')}`, + `Unexpected registration response origin "${origin}", expected one of: ${expectedOrigin.join( + ', ', + )}`, ); } } else { if (origin !== expectedOrigin) { - throw new Error(`Unexpected attestation origin "${origin}", expected "${expectedOrigin}"`); + throw new Error( + `Unexpected registration response origin "${origin}", expected "${expectedOrigin}"`, + ); } } @@ -144,7 +148,7 @@ export default async function verifyAttestationResponse( // Make sure someone was physically present if (!flags.up) { - throw new Error('User not present during assertion'); + throw new Error('User not present during registration'); } // Enforce user verification if specified @@ -161,7 +165,7 @@ export default async function verifyAttestationResponse( } if (!aaguid) { - throw new Error('No AAGUID was present in attestation'); + throw new Error('No AAGUID was present during registration'); } const decodedPublicKey = decodeCredentialPublicKey(credentialPublicKey); @@ -171,7 +175,7 @@ export default async function verifyAttestationResponse( throw new Error('Credential public key was missing numeric alg'); } - // Make sure the key algorithm is one we specified within the attestation options + // Make sure the key algorithm is one we specified within the registration options if (!supportedAlgorithmIDs.includes(alg as number)) { const supported = supportedAlgorithmIDs.join(', '); throw new Error(`Unexpected public key alg "${alg}", expected one of "${supported}"`); @@ -218,12 +222,12 @@ export default async function verifyAttestationResponse( throw new Error(`Unsupported Attestation Format: ${fmt}`); } - const toReturn: VerifiedAttestation = { + const toReturn: VerifiedRegistrationResponse = { verified, }; if (toReturn.verified) { - toReturn.attestationInfo = { + toReturn.registrationInfo = { fmt, counter, aaguid: convertAAGUIDToString(aaguid), @@ -239,24 +243,24 @@ export default async function verifyAttestationResponse( } /** - * Result of attestation verification + * Result of registration verification * * @param verified If the assertion response could be verified - * @param attestationInfo.fmt Type of attestation - * @param attestationInfo.counter The number of times the authenticator reported it has been used. + * @param registrationInfo.fmt Type of attestation + * @param registrationInfo.counter The number of times the authenticator reported it has been used. * Should be kept in a DB for later reference to help prevent replay attacks - * @param attestationInfo.aaguid Authenticator's Attestation GUID indicating the type of the + * @param registrationInfo.aaguid Authenticator's Attestation GUID indicating the type of the * authenticator - * @param attestationInfo.credentialPublicKey The credential's public key - * @param attestationInfo.credentialID The credential's credential ID for the public key above - * @param attestationInfo.credentialType The type of the credential returned by the browser - * @param attestationInfo.userVerified Whether the user was uniquely identified during attestation - * @param attestationInfo.attestationObject The raw `response.attestationObject` Buffer returned by + * @param registrationInfo.credentialPublicKey The credential's public key + * @param registrationInfo.credentialID The credential's credential ID for the public key above + * @param registrationInfo.credentialType The type of the credential returned by the browser + * @param registrationInfo.userVerified Whether the user was uniquely identified during attestation + * @param registrationInfo.attestationObject The raw `response.attestationObject` Buffer returned by * the authenticator */ -export type VerifiedAttestation = { +export type VerifiedRegistrationResponse = { verified: boolean; - attestationInfo?: { + registrationInfo?: { fmt: AttestationFormat; counter: number; aaguid: string; diff --git a/packages/typescript-types/src/index.ts b/packages/typescript-types/src/index.ts index 4834a37..a930861 100644 --- a/packages/typescript-types/src/index.ts +++ b/packages/typescript-types/src/index.ts @@ -55,16 +55,16 @@ export interface PublicKeyCredentialUserEntityJSON /** * The value returned from navigator.credentials.create() */ -export interface AttestationCredential extends PublicKeyCredential { +export interface RegistrationCredential extends PublicKeyCredential { response: AuthenticatorAttestationResponseFuture; } /** - * A slightly-modified AttestationCredential to simplify working with ArrayBuffers that + * A slightly-modified RegistrationCredential to simplify working with ArrayBuffers that * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. */ -export interface AttestationCredentialJSON - extends Omit<AttestationCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { +export interface RegistrationCredentialJSON + extends Omit<RegistrationCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { rawId: Base64URLString; response: AuthenticatorAttestationResponseJSON; clientExtensionResults: AuthenticationExtensionsClientOutputs; @@ -74,16 +74,16 @@ export interface AttestationCredentialJSON /** * The value returned from navigator.credentials.get() */ -export interface AssertionCredential extends PublicKeyCredential { +export interface AuthenticationCredential extends PublicKeyCredential { response: AuthenticatorAssertionResponse; } /** - * A slightly-modified AssertionCredential to simplify working with ArrayBuffers that + * A slightly-modified AuthenticationCredential to simplify working with ArrayBuffers that * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. */ -export interface AssertionCredentialJSON - extends Omit<AssertionCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { +export interface AuthenticationCredentialJSON + extends Omit<AuthenticationCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { rawId: Base64URLString; response: AuthenticatorAssertionResponseJSON; clientExtensionResults: AuthenticationExtensionsClientOutputs; @@ -122,7 +122,7 @@ export type AuthenticatorDevice = { credentialID: Buffer; // Number of times this authenticator is expected to have been used counter: number; - // From browser's `startAttestation()` -> AttestationCredentialJSON.transports (API L2 and up) + // From browser's `startRegistration()` -> RegistrationCredentialJSON.transports (API L2 and up) transports?: AuthenticatorTransport[]; }; |