diff options
author | Matthew Miller <matthew@millerti.me> | 2020-06-02 15:50:11 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-02 15:50:11 -0700 |
commit | ed960d81a9667d5cca2d444839f5ce63e2f38911 (patch) | |
tree | 2d9f2f8e7ce60a83e5409d073f74422bcc2df60e /packages/server/src/assertion | |
parent | 743de54fa9b0cbef261cdbedf1c567c2202737cd (diff) | |
parent | bb5e3e99f7e50b9cec607b4fda34dcbd1e04aae9 (diff) |
Merge pull request #21 from MasterKale/feature/improve-browser
Refactor Megamix 1
Diffstat (limited to 'packages/server/src/assertion')
4 files changed, 51 insertions, 29 deletions
diff --git a/packages/server/src/assertion/generateAssertionOptions.test.ts b/packages/server/src/assertion/generateAssertionOptions.test.ts index bd2d48d..9cae665 100644 --- a/packages/server/src/assertion/generateAssertionOptions.test.ts +++ b/packages/server/src/assertion/generateAssertionOptions.test.ts @@ -61,7 +61,7 @@ test('should set extensions if specified', () => { const goodOpts1 = { challenge: 'totallyrandomvalue', - allowedBase64CredentialIDs: [ + allowedCredentialIDs: [ Buffer.from('1234', 'ascii').toString('base64'), Buffer.from('5678', 'ascii').toString('base64'), ], diff --git a/packages/server/src/assertion/generateAssertionOptions.ts b/packages/server/src/assertion/generateAssertionOptions.ts index 9444a54..6645e2e 100644 --- a/packages/server/src/assertion/generateAssertionOptions.ts +++ b/packages/server/src/assertion/generateAssertionOptions.ts @@ -1,10 +1,11 @@ import type { PublicKeyCredentialRequestOptionsJSON, + Base64URLString, } from '@simplewebauthn/typescript-types'; type Options = { challenge: string, - allowedBase64CredentialIDs: string[], + allowedCredentialIDs: Base64URLString[], suggestedTransports?: AuthenticatorTransport[], timeout?: number, userVerification?: UserVerificationRequirement, @@ -15,7 +16,7 @@ type Options = { * Prepare a value to pass into navigator.credentials.get(...) for authenticator "login" * * @param challenge Random string the authenticator needs to sign and pass back - * @param allowedBase64CredentialIDs Array of base64-encoded authenticator IDs registered by the + * @param allowedCredentialIDs Array of base64url-encoded authenticator IDs registered by the * user for assertion * @param timeout How long (in ms) the user can take to complete assertion * @param suggestedTransports Suggested types of authenticators for assertion @@ -28,7 +29,7 @@ export default function generateAssertionOptions( ): PublicKeyCredentialRequestOptionsJSON { const { challenge, - allowedBase64CredentialIDs, + allowedCredentialIDs, suggestedTransports = ['usb', 'ble', 'nfc', 'internal'], timeout = 60000, userVerification, @@ -37,7 +38,7 @@ export default function generateAssertionOptions( return { challenge, - allowCredentials: allowedBase64CredentialIDs.map(id => ({ + allowCredentials: allowedCredentialIDs.map(id => ({ id, type: 'public-key', transports: suggestedTransports, diff --git a/packages/server/src/assertion/verifyAssertionResponse.test.ts b/packages/server/src/assertion/verifyAssertionResponse.test.ts index 5895b66..9da06ce 100644 --- a/packages/server/src/assertion/verifyAssertionResponse.test.ts +++ b/packages/server/src/assertion/verifyAssertionResponse.test.ts @@ -48,7 +48,7 @@ test('should return authenticator info after verification', () => { expect(verification.authenticatorInfo.counter).toEqual(144); expect(verification.authenticatorInfo.base64CredentialID).toEqual( - authenticator.base64CredentialID, + authenticator.credentialID, ); }); @@ -111,24 +111,28 @@ test('should throw error if previous counter value is not less than in response' }); const assertionResponse = { - base64CredentialID: - 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' + 'g6jo_o0hYiew', - base64AuthenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA==', - base64ClientDataJSON: - 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJj' + - 'bGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5k' + - 'b250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=', - base64Signature: - 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' + - 'jhd45bDx92wjXKs900=', + id: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew', + rawId: '', + response: { + authenticatorData: 'PdxHEOnAiLIp26idVjIguzn3Ipr_RlsKZWsa-5qK-KABAAAAkA==', + clientDataJSON: + 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sVWFXMWwiLCJj' + + 'bGllbnRFeHRlbnNpb25zIjp7fSwiaGFzaEFsZ29yaXRobSI6IlNIQS0yNTYiLCJvcmlnaW4iOiJodHRwczovL2Rldi5k' + + 'b250bmVlZGEucHciLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=', + signature: + 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' + + 'jhd45bDx92wjXKs900=', + }, + getClientExtensionResults: () => ({}), + type: 'webauthn.get', }; const assertionChallenge = 'totallyUniqueValueEveryTime'; const assertionOrigin = 'https://dev.dontneeda.pw'; const authenticator = { - base64PublicKey: + publicKey: 'BIheFp-u6GvFT2LNGovf3ZrT0iFVBsA_76rRysxRG9A18WGeA6hPmnab0HAViUYVRkwTNcN77QBf_' + 'RR0dv3lIvQ', - base64CredentialID: + credentialID: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Px' + 'g6jo_o0hYiew', counter: 0, }; diff --git a/packages/server/src/assertion/verifyAssertionResponse.ts b/packages/server/src/assertion/verifyAssertionResponse.ts index 24ee17e..7d13271 100644 --- a/packages/server/src/assertion/verifyAssertionResponse.ts +++ b/packages/server/src/assertion/verifyAssertionResponse.ts @@ -1,8 +1,7 @@ import base64url from 'base64url'; import { - AuthenticatorAssertionResponseJSON, + AssertionCredentialJSON, AuthenticatorDevice, - VerifiedAssertion, } from '@simplewebauthn/typescript-types'; import decodeClientDataJSON from '../helpers/decodeClientDataJSON'; @@ -14,19 +13,19 @@ import parseAuthenticatorData from '../helpers/parseAuthenticatorData'; /** * Verify that the user has legitimately completed the login process * - * @param response Authenticator assertion response with base64-encoded values + * @param response Authenticator assertion response with base64url-encoded values * @param expectedChallenge The random value provided to generateAssertionOptions for the * authenticator to sign * @param expectedOrigin Expected URL of website assertion should have occurred on */ export default function verifyAssertionResponse( - response: AuthenticatorAssertionResponseJSON, + credential: AssertionCredentialJSON, expectedChallenge: string, expectedOrigin: string, authenticator: AuthenticatorDevice, ): VerifiedAssertion { - const { base64AuthenticatorData, base64ClientDataJSON, base64Signature } = response; - const clientDataJSON = decodeClientDataJSON(base64ClientDataJSON); + const { response } = credential; + const clientDataJSON = decodeClientDataJSON(response.clientDataJSON); const { type, origin, challenge } = clientDataJSON; @@ -50,7 +49,7 @@ export default function verifyAssertionResponse( throw new Error(`Unexpected assertion type: ${type}`); } - const authDataBuffer = base64url.toBuffer(base64AuthenticatorData); + const authDataBuffer = base64url.toBuffer(response.authenticatorData); const authDataStruct = parseAuthenticatorData(authDataBuffer); const { flags, counter } = authDataStruct; @@ -70,19 +69,37 @@ export default function verifyAssertionResponse( const { rpIdHash, flagsBuf, counterBuf } = authDataStruct; - const clientDataHash = toHash(base64url.toBuffer(base64ClientDataJSON)); + const clientDataHash = toHash(base64url.toBuffer(response.clientDataJSON)); const signatureBase = Buffer.concat([rpIdHash, flagsBuf, counterBuf, clientDataHash]); - const publicKey = convertASN1toPEM(base64url.toBuffer(authenticator.base64PublicKey)); - const signature = base64url.toBuffer(base64Signature); + const publicKey = convertASN1toPEM(base64url.toBuffer(authenticator.publicKey)); + const signature = base64url.toBuffer(response.signature); const toReturn = { verified: verifySignature(signature, signatureBase, publicKey), authenticatorInfo: { counter, - base64CredentialID: response.base64CredentialID, + base64CredentialID: credential.id, }, }; return toReturn; } + +/** + * Result of assertion verification + * + * @param verified If the assertion response could be verified + * @param authenticatorInfo.base64CredentialID The ID of the authenticator used during assertion. + * Should be used to identify which DB authenticator entry needs its `counter` updated to the value + * below + * @param authenticatorInfo.counter 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 = { + verified: boolean; + authenticatorInfo: { + counter: number; + base64CredentialID: string; + }; +}; |