summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/assertion
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2020-06-02 15:50:11 -0700
committerGitHub <noreply@github.com>2020-06-02 15:50:11 -0700
commited960d81a9667d5cca2d444839f5ce63e2f38911 (patch)
tree2d9f2f8e7ce60a83e5409d073f74422bcc2df60e /packages/server/src/assertion
parent743de54fa9b0cbef261cdbedf1c567c2202737cd (diff)
parentbb5e3e99f7e50b9cec607b4fda34dcbd1e04aae9 (diff)
Merge pull request #21 from MasterKale/feature/improve-browser
Refactor Megamix 1
Diffstat (limited to 'packages/server/src/assertion')
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.test.ts2
-rw-r--r--packages/server/src/assertion/generateAssertionOptions.ts9
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.test.ts30
-rw-r--r--packages/server/src/assertion/verifyAssertionResponse.ts39
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;
+ };
+};