summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src/authentication
diff options
context:
space:
mode:
authorJordan Tucker <jordanbtucker@gmail.com>2023-08-28 14:28:52 -0500
committerJordan Tucker <jordanbtucker@gmail.com>2023-08-28 14:28:52 -0500
commitab1a3e42dfd14301b278d86a677f73b0cb7cf37a (patch)
tree3fab7c8134e779e9677c4534e5958dc1dfaea570 /packages/server/src/authentication
parent0d9eda359379d8704eeda1995607bbd27de4ebe2 (diff)
Allow expectedChallenge to return a Promise
Diffstat (limited to 'packages/server/src/authentication')
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.test.ts76
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.ts4
2 files changed, 78 insertions, 2 deletions
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
index bf2a79a..822bdd9 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
@@ -394,6 +394,82 @@ Deno.test('should fail verification if custom challenge verifier returns false',
);
});
+Deno.test('should pass verification if custom challenge verifier returns a Promise that resolves with true', async () => {
+ const verification = await verifyAuthenticationResponse({
+ response: {
+ id:
+ 'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
+ rawId:
+ 'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
+ response: {
+ authenticatorData: 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFYftypQ',
+ clientDataJSON:
+ 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZXlKaFkzUjFZV3hEYUdGc2JHVnVaMlVpT2lKTE0xRjRUMnB1VmtwTWFVZHNibFpGY0RWMllUVlJTbVZOVmxkT1psODNVRmxuZFhSbllrRjBRVlZCSWl3aVlYSmlhWFJ5WVhKNVJHRjBZU0k2SW5OcFoyNU5aVkJzWldGelpTSjkiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJjcm9zc09yaWdpbiI6ZmFsc2V9',
+ signature:
+ 'MEUCIByFAVGfkoKPEzynp-37BX_HOXSaC6-58-ELjB7BG9opAiEAyD_1mN9YAPrphcwpzK3ym2Xx8EjAapgQ326mKgQ1pW0',
+ userHandle: 'internalUserId',
+ },
+ type: 'public-key',
+ clientExtensionResults: {},
+ },
+ expectedChallenge: (challenge: string) => {
+ const parsedChallenge: {
+ actualChallenge: string;
+ arbitraryData: string;
+ } = JSON.parse(
+ isoBase64URL.toString(challenge),
+ );
+ return Promise.resolve(
+ parsedChallenge.actualChallenge ===
+ 'K3QxOjnVJLiGlnVEp5va5QJeMVWNf_7PYgutgbAtAUA',
+ );
+ },
+ expectedOrigin: 'http://localhost:8000',
+ expectedRPID: 'localhost',
+ authenticator: {
+ credentialID: isoBase64URL.toBuffer(
+ 'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
+ ),
+ credentialPublicKey: isoBase64URL.toBuffer(
+ 'pQECAyYgASFYILTrxTUQv3X4DRM6L_pk65FSMebenhCx3RMsTKoBm-AxIlggEf3qk5552QLNSh1T1oQs7_2C2qysDwN4r4fCp52Hsqs',
+ ),
+ counter: 0,
+ },
+ });
+
+ assert(verification.verified);
+});
+
+Deno.test('should fail verification if custom challenge verifier returns a Promise that resolves with false', async () => {
+ await assertRejects(
+ () =>
+ verifyAuthenticationResponse({
+ response: assertionResponse,
+ expectedChallenge: (challenge) => Promise.resolve(challenge === 'willNeverMatch'),
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ }),
+ Error,
+ 'Custom challenge verifier returned false',
+ );
+});
+
+Deno.test('should fail verification if custom challenge verifier returns a Promise that rejects', async () => {
+ await assertRejects(
+ () =>
+ verifyAuthenticationResponse({
+ response: assertionResponse,
+ expectedChallenge: () => Promise.reject(new Error('rejected')),
+ expectedOrigin: assertionOrigin,
+ expectedRPID: 'dev.dontneeda.pw',
+ authenticator: authenticator,
+ }),
+ Error,
+ 'rejected',
+ );
+});
+
Deno.test('should return authenticator extension output', async () => {
const verification = await verifyAuthenticationResponse({
response: {
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts
index d3c2484..41370a0 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts
@@ -15,7 +15,7 @@ import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.ts';
export type VerifyAuthenticationResponseOpts = {
response: AuthenticationResponseJSON;
- expectedChallenge: string | ((challenge: string) => boolean);
+ expectedChallenge: string | ((challenge: string) => boolean | Promise<boolean>);
expectedOrigin: string | string[];
expectedRPID: string | string[];
authenticator: AuthenticatorDevice;
@@ -94,7 +94,7 @@ export async function verifyAuthenticationResponse(
// Ensure the device provided the challenge we gave it
if (typeof expectedChallenge === 'function') {
- if (!expectedChallenge(challenge)) {
+ if (!(await expectedChallenge(challenge))) {
throw new Error(
`Custom challenge verifier returned false for registration response challenge "${challenge}"`,
);