Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better DPK support #291

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f12457d
Implement `verifyDevicePubKey`
agektmr Aug 16, 2022
54b2629
Working `verifyDevicePublicKey`.
agektmr Aug 17, 2022
9506187
Update DPK impl.
agektmr Aug 18, 2022
f124eca
Bump the version to 6.0.0
agektmr Aug 18, 2022
82b74ff
Add temporary attestation verification
agektmr Aug 18, 2022
f83709c
Update DPK logic
agektmr Aug 18, 2022
1c072b4
DPK update
agektmr Aug 19, 2022
6dc3e80
Update DPK verifications
agektmr Aug 20, 2022
8e8cfb4
Reorganize the interface
agektmr Aug 21, 2022
9250cd5
Update packages/server/src/extensions/devicePublicKey/verifyDpkSignat…
agektmr Aug 22, 2022
9c21bea
Update packages/server/src/extensions/devicePublicKey/verifyDpkSignat…
agektmr Aug 22, 2022
5edeb63
Update packages/server/src/extensions/devicePublicKey/verifyDpkSignat…
agektmr Aug 22, 2022
9694a80
Update DPK
agektmr Aug 22, 2022
9f0ea58
Update DPK logic
agektmr Aug 26, 2022
d5745b6
Attestation binary equality check
agektmr Aug 26, 2022
000c9b4
Add comment
agektmr Aug 26, 2022
76153ff
Return device public key to RP
agektmr Aug 26, 2022
2228dd3
Successful test cases added
agektmr Aug 27, 2022
ac5413e
Reflect feedback
agektmr Aug 29, 2022
a20cade
chore(release): publish v6.3.0-alpha.0
MasterKale Sep 29, 2022
4ca4a97
Align DPK registration with DPK authentication
agektmr Sep 2, 2022
bf719c4
Some progress aligning with the latest DPK spec
agektmr Sep 6, 2022
501b6c3
All tests succeed now
agektmr Sep 9, 2022
41ba0e6
Better test coverage
agektmr Sep 10, 2022
0747463
Reflect feedback
agektmr Sep 20, 2022
f6a99a2
Remove package.json files
agektmr Sep 20, 2022
e188128
Parse dpk result on the browser
agektmr Oct 2, 2022
3fad254
Update packages/browser/src/helpers/parseClientExtensionResults.ts
agektmr Oct 5, 2022
72f5f64
Revert package-lock.json
agektmr Oct 5, 2022
edca70e
Update packages/server/src/authentication/verifyAuthenticationRespons…
agektmr Oct 5, 2022
efe0c41
Reflect feedback
agektmr Oct 5, 2022
cacb165
Reflect feedback
agektmr Oct 6, 2022
883b49d
chore(release): publish v6.3.0-alpha.1
MasterKale Oct 6, 2022
de678e7
Encode DPK output for simplicity
agektmr Oct 13, 2022
8d3602f
Update AuthenticationExtensionsClientInputs
agektmr Oct 13, 2022
4ce11e7
DPK related changes:
agektmr Nov 21, 2022
c20bde9
Allow extending DPK object
agektmr Nov 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "6.2.1",
"version": "6.3.0-alpha.1",
"npmClient": "npm",
"useNx": true,
"command": {
Expand Down
4 changes: 2 additions & 2 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@simplewebauthn/browser",
"version": "6.2.1",
"version": "6.3.0-alpha.1",
"description": "SimpleWebAuthn for Browsers",
"main": "dist/bundle/index.js",
"unpkg": "dist/bundle/index.umd.min.js",
Expand Down Expand Up @@ -33,7 +33,7 @@
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.2.1",
"@simplewebauthn/typescript-types": "*",
"@simplewebauthn/typescript-types": "^6.3.0-alpha.1",
"rollup": "^2.52.1",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-version-injector": "^1.3.3"
Expand Down
44 changes: 44 additions & 0 deletions packages/browser/src/helpers/parseClientExtensionResults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { parseClientExtensionResults } from './parseClientExtensionResults';
import { utf8StringToBuffer } from './utf8StringToBuffer';
import {
AuthenticationExtensionsClientOutputsFuture,
AuthenticationCredential
} from '@simplewebauthn/typescript-types';

jest.mock('../helpers/browserSupportsWebAuthn');

const mockAuthenticatorData = 'mockAuthenticatorData';
const mockClientDataJSON = 'mockClientDataJSON';
const mockSignature = 'mockSignature';
const mockUserHandle = 'mockUserHandle';

test('should return client extension results in JSON format', async () => {
const credential: AuthenticationCredential = {
id: 'foobar',
rawId: utf8StringToBuffer('foobar'),
response: {
authenticatorData: Buffer.from(mockAuthenticatorData, 'ascii'),
clientDataJSON: Buffer.from(mockClientDataJSON, 'ascii'),
signature: Buffer.from(mockSignature, 'ascii'),
userHandle: Buffer.from(mockUserHandle, 'ascii'),
},
getClientExtensionResults: (): AuthenticationExtensionsClientOutputsFuture => {
return {
devicePubKey: {
authenticatorOutput: Buffer.from('a66364706b584da50102032620012158206c2411290f2f5dc0d590c25ed9f4b9645a94bb8ecba2377765b103dad5c99243225820c10b2ab6051e0610388d3f2d75a624b94454f4f51948b3dcc6f34b7518f2455263666d74646e6f6e65656e6f6e6365406573636f7065006661616775696450000000000000000000000000000000006761747453746d74a0', 'hex'),
signature: Buffer.from('3045022078ac013e33175eb74f335374715b70010f0fbef2a6697ce9402ec2a7485b7b45022100ca763fe60a93e03041df55c7020221d7c621849f8196126845d6d251e3bfd6b2', 'hex')
}
};
},
type: 'webauthn.create',
};

const result = parseClientExtensionResults(credential);

expect(result).toMatchObject({
devicePubKey: {
authenticatorOutput: 'pmNkcGtYTaUBAgMmIAEhWCBsJBEpDy9dwNWQwl7Z9LlkWpS7jsuiN3dlsQPa1cmSQyJYIMELKrYFHgYQOI0_LXWmJLlEVPT1GUiz3MbzS3UY8kVSY2ZtdGRub25lZW5vbmNlQGVzY29wZQBmYWFndWlkUAAAAAAAAAAAAAAAAAAAAABnYXR0U3RtdKA',
signature: 'MEUCIHisAT4zF163TzNTdHFbcAEPD77ypml86UAuwqdIW3tFAiEAynY_5gqT4DBB31XHAgIh18YhhJ-BlhJoRdbSUeO_1rI'
}
});
});
37 changes: 37 additions & 0 deletions packages/browser/src/helpers/parseClientExtensionResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
AuthenticationExtensionsClientOutputsJSON,
AuthenticationExtensionsClientOutputsFuture,
RegistrationCredential,
AuthenticationCredential
} from "@simplewebauthn/typescript-types";
import { bufferToBase64URLString } from "./bufferToBase64URLString";

export function parseClientExtensionResults(
credential: AuthenticationCredential | RegistrationCredential
): AuthenticationExtensionsClientOutputsJSON {
const clientExtensionResults: AuthenticationExtensionsClientOutputsFuture = credential.getClientExtensionResults()
const clientExtensionResultsJSON: AuthenticationExtensionsClientOutputsJSON = {};

const { appid, credProps, devicePubKey, uvm } = clientExtensionResults;

if (appid) {
clientExtensionResultsJSON.appid = appid;
}

if (credProps) {
clientExtensionResultsJSON.credProps = credProps;
}

if (uvm) {
clientExtensionResultsJSON.uvm = clientExtensionResults.uvm;
}

if (devicePubKey) {
clientExtensionResultsJSON.devicePubKey = {
authenticatorOutput: bufferToBase64URLString(devicePubKey.authenticatorOutput),
signature: bufferToBase64URLString(devicePubKey.signature),
};
}

return clientExtensionResultsJSON;
}
2 changes: 1 addition & 1 deletion packages/browser/src/methods/startAuthentication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ test('should return authenticatorAttachment if present', async () => {
return new Promise(resolve => {
resolve({
response: {},
getClientExtensionResults: () => {},
getClientExtensionResults: () => ({}),
authenticatorAttachment: 'cross-platform',
});
});
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/methods/startAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { browserSupportsWebAuthnAutofill } from '../helpers/browserSupportsWebAu
import { toPublicKeyCredentialDescriptor } from '../helpers/toPublicKeyCredentialDescriptor';
import { identifyAuthenticationError } from '../helpers/identifyAuthenticationError';
import { webauthnAbortService } from '../helpers/webAuthnAbortService';
import { parseClientExtensionResults } from '../helpers/parseClientExtensionResults';

/**
* Begin authenticator "login" via WebAuthn assertion
Expand Down Expand Up @@ -104,7 +105,7 @@ export async function startAuthentication(
userHandle,
},
type,
clientExtensionResults: credential.getClientExtensionResults(),
clientExtensionResults: parseClientExtensionResults(credential),
authenticatorAttachment: credential.authenticatorAttachment,
};
}
2 changes: 1 addition & 1 deletion packages/browser/src/methods/startRegistration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ test('should return authenticatorAttachment if present', async () => {
return new Promise(resolve => {
resolve({
response: {},
getClientExtensionResults: () => {},
getClientExtensionResults: () => ({}),
authenticatorAttachment: 'cross-platform',
});
});
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/methods/startRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { browserSupportsWebAuthn } from '../helpers/browserSupportsWebAuthn';
import { toPublicKeyCredentialDescriptor } from '../helpers/toPublicKeyCredentialDescriptor';
import { identifyRegistrationError } from '../helpers/identifyRegistrationError';
import { webauthnAbortService } from '../helpers/webAuthnAbortService';
import { parseClientExtensionResults } from '../helpers/parseClientExtensionResults';

/**
* Begin authenticator "registration" via WebAuthn attestation
Expand Down Expand Up @@ -63,7 +64,7 @@ export async function startRegistration(
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
},
type,
clientExtensionResults: credential.getClientExtensionResults(),
clientExtensionResults: parseClientExtensionResults(credential),
authenticatorAttachment: credential.authenticatorAttachment,
};

Expand Down
4 changes: 2 additions & 2 deletions packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@simplewebauthn/server",
"version": "6.2.1",
"version": "6.3.0-alpha.1",
"description": "SimpleWebAuthn for Servers",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -60,7 +60,7 @@
},
"gitHead": "33ccf8c6c9add811c87d3089e24156c2342b3498",
"devDependencies": {
"@simplewebauthn/typescript-types": "*",
"@simplewebauthn/typescript-types": "^6.3.0-alpha.1",
"@types/cbor": "^5.0.1",
"@types/debug": "^4.1.7",
"@types/jsrsasign": "^8.0.13",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {
AuthenticationExtensionsClientInputs,
AuthenticationExtensionsClientInputsFuture,
PublicKeyCredentialRequestOptionsJSON,
PublicKeyCredentialDescriptorFuture,
UserVerificationRequirement,
Expand All @@ -13,7 +13,7 @@ export type GenerateAuthenticationOptionsOpts = {
challenge?: string | Buffer;
timeout?: number;
userVerification?: UserVerificationRequirement;
extensions?: AuthenticationExtensionsClientInputs;
extensions?: AuthenticationExtensionsClientInputsFuture;
rpID?: string;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import base64url from 'base64url';
import { verifyAuthenticationResponse } from './verifyAuthenticationResponse';
import { verifyAuthenticationResponse, VerifyAuthenticationResponseOpts } from './verifyAuthenticationResponse';

import * as esmDecodeClientDataJSON from '../helpers/decodeClientDataJSON';
import * as esmParseAuthenticatorData from '../helpers/parseAuthenticatorData';
Expand All @@ -8,6 +8,10 @@ import {
AuthenticatorDevice,
AuthenticationCredentialJSON,
} from '@simplewebauthn/typescript-types';
import {
DevicePublicKeyAuthenticatorOutput,
DevicePublicKeyAuthenticatorOutputJSON,
} from '../extensions/devicePublicKey/decodeDevicePubKey';

let mockDecodeClientData: jest.SpyInstance;
let mockParseAuthData: jest.SpyInstance;
Expand Down Expand Up @@ -311,51 +315,106 @@ test('should fail verification if custom challenge verifier returns false', asyn
).rejects.toThrow(/custom challenge verifier returned false/i);
});

test('should return authenticator extension output', async () => {
const verification = await verifyAuthenticationResponse({
credential: {
response: {
clientDataJSON:
'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaVpzVkN6dHJEVzdEMlVfR0hDSWxZS0x3VjJiQ3NCVFJxVlFVbkpYbjlUayIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOmd4N3NxX3B4aHhocklRZEx5ZkcwcHhLd2lKN2hPazJESlE0eHZLZDQzOFEiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZmlkby5leGFtcGxlLmZpZG8yYXBpZXhhbXBsZSJ9',
authenticatorData:
'DXX8xWP9p3nbLjQ-6kiYiHWLeFSdSTpP2-oc2WqjHMSFAAAAAKFvZGV2aWNlUHVibGljS2V5pWNkcGtYTaUBAgMmIAEhWCCZGqvtneQnGp7erYgG-dyW1tzNDEdiU6VRBInsg3m-WyJYIKCXPP3tu3nif-9O50gWc_szElBN3KVDTP0jQx1q0p7aY3NpZ1hHMEUCIElSbNKK72tOYhp9WTbStQSVL8CuIxOk8DV6r_-uqWR0AiEAnVE6yu-wsyx2Wq5v66jClGhe_2P_HL8R7PIQevT-uPhlbm9uY2VAZXNjb3BlQQBmYWFndWlkULk_2WHy5kYvsSKCACJH3ng=',
signature:
'MEYCIQDlRuxY7cYre0sb3T6TovQdfYIUb72cRZYOQv_zS9wN_wIhAOvN-fwjtyIhWRceqJV4SX74-z6oALERbC7ohk8EdVPO',
userHandle: 'b2FPajFxcmM4MWo3QkFFel9RN2lEakh5RVNlU2RLNDF0Sl92eHpQYWV5UQ==',
},
id: 'E_Pko4wN1BXE23S0ftN3eQ',
rawId: 'E_Pko4wN1BXE23S0ftN3eQ',
type: 'public-key',
clientExtensionResults: {},
describe('device public key related tests', () => {
const DpkAuthCred: AuthenticationCredentialJSON = {
response: {
clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiS05aUmtPRU5KY1dCTzZHX0VjcE1GS2FWRDlham1xNExsZDZJMllJc1c3QSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOmd4N3NxX3B4aHhocklRZEx5ZkcwcHhLd2lKN2hPazJESlE0eHZLZDQzOFEiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZmlkby5leGFtcGxlLmZpZG8yYXBpZXhhbXBsZSJ9',
authenticatorData: 'DXX8xWP9p3nbLjQ-6kiYiHWLeFSdSTpP2-oc2WqjHMSdAAAAAKFsZGV2aWNlUHViS2V5WIymY2Rwa1hNpQECAyYgASFYIE3BmJ0MLxBA0B9-wVrFQrFNtUvF6l1X7X9rOD67T6uwIlggW2G32XUvyDaGpA6jyiacF319GPZvInOfUlCqenX2hVtjZm10ZG5vbmVlbm9uY2VAZXNjb3BlAGZhYWd1aWRQAAAAAAAAAAAAAAAAAAAAAGdhdHRTdG10oA==',
signature: 'MEUCIF1LvdGHiW5aq25ZrNVUeZOm7pcS_9a172pkO2C6ILE1AiEA8NYg-ZzOgt1pN0Bqv02t7lWCSMn_IPpvKHdT5Mjv75E=',
userHandle: 'b2FPajFxcmM4MWo3QkFFel9RN2lEakh5RVNlU2RLNDF0Sl92eHpQYWV5UQ==',
},
id: 'BxYpj3rs5WGW8UVnXsmMzg',
rawId: 'BxYpj3rs5WGW8UVnXsmMzg',
type: 'public-key',
clientExtensionResults: {
devicePubKey: {
'authenticatorOutput': 'pmNkcGtYTaUBAgMmIAEhWCBNwZidDC8QQNAffsFaxUKxTbVLxepdV-1_azg-u0-rsCJYIFtht9l1L8g2hqQOo8omnBd9fRj2byJzn1JQqnp19oVbY2ZtdGRub25lZW5vbmNlQGVzY29wZQBmYWFndWlkUAAAAAAAAAAAAAAAAAAAAABnYXR0U3RtdKA=',
'signature': 'MEQCIAdwrIjLt7ULTU5OzpnhzvbWJ3srVLoOCYs72Hlw6ugoAiAFl4_jfJJv89cM5qSx8lI_pIXLRIy6lO9N3O8SUjyNKQ==',
}
}
};

const DpkVerifyAuthRespOpts: VerifyAuthenticationResponseOpts = {
credential: DpkAuthCred,
expectedOrigin: 'android:apk-key-hash:gx7sq_pxhxhrIQdLyfG0pxKwiJ7hOk2DJQ4xvKd438Q',
expectedRPID: 'try-webauthn.appspot.com',
expectedChallenge: 'iZsVCztrDW7D2U_GHCIlYKLwV2bCsBTRqVQUnJXn9Tk',
expectedChallenge: 'KNZRkOENJcWBO6G_EcpMFKaVD9ajmq4Lld6I2YIsW7A',
authenticator: {
credentialID: base64url.toBuffer(
'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
),
credentialPublicKey: base64url.toBuffer(
'pQECAyYgASFYILTrxTUQv3X4DRM6L_pk65FSMebenhCx3RMsTKoBm-AxIlggEf3qk5552QLNSh1T1oQs7_2C2qysDwN4r4fCp52Hsqs',
),
credentialID: base64url.toBuffer('BxYpj3rs5WGW8UVnXsmMzg'),
credentialPublicKey: base64url.toBuffer('pQECAyYgASFYIPLEylOIRiI7z7q6zuYjWB9TcOj9yNwmawogQJ4ZKpNAIlggd9ZqIjd30p1tIU6A8ue5wEZl9q/AsKR/leaHFZ/bwWk='),
counter: 0,
},
}

if (!DpkAuthCred?.clientExtensionResults?.devicePubKey) {
throw new Error('This exception will not happen.');
}

// This DPK is baked into `DpkAuthCred`.
const devicePubKey: DevicePublicKeyAuthenticatorOutput = {
dpk: Buffer.from('a50102032620012158204dc1989d0c2f1040d01f7ec15ac542b14db54bc5ea5d57ed7f6b383ebb4fabb02258205b61b7d9752fc83686a40ea3ca269c177d7d18f66f22739f5250aa7a75f6855b', 'hex'),
nonce: Buffer.from('', 'hex'),
scope: 0,
aaguid: Buffer.from('00000000000000000000000000000000', 'hex'),
fmt: 'none',
attStmt: {}
}

// This is an encoded version of `devicePubKey`.
const devicePubKeyJSON: DevicePublicKeyAuthenticatorOutputJSON = {
dpk: 'pQECAyYgASFYIE3BmJ0MLxBA0B9-wVrFQrFNtUvF6l1X7X9rOD67T6uwIlggW2G32XUvyDaGpA6jyiacF319GPZvInOfUlCqenX2hVs',
nonce: '',
scope: 0,
aaguid: 'AAAAAAAAAAAAAAAAAAAAAA',
fmt: 'none',
attStmt: {}
}

// A different DPK example.
const differentDevicePubKey: DevicePublicKeyAuthenticatorOutput = {
dpk: Buffer.from('a5010203262001215820991aabed9de4271a9edead8806f9dc96d6dccd0c476253a5510489ec8379be5b225820a0973cfdedbb79e27fef4ee7481673fb3312504ddca5434cfd23431d6ad29eda', 'hex'),
nonce: Buffer.from('', 'hex'),
scope: 0,
aaguid: Buffer.from('00000000000000000000000000000000', 'hex'),
fmt: 'none',
attStmt: {},
}

// This is an encoded version of `differentDevicePubKey`.
const differentDevicePubKeyJSON: DevicePublicKeyAuthenticatorOutputJSON = {
dpk: 'pQECAyYgASFYIJkaq-2d5Ccant6tiAb53JbW3M0MR2JTpVEEieyDeb5bIlggoJc8_e27eeJ_707nSBZz-zMSUE3cpUNM_SNDHWrSnto',
nonce: '',
scope: 0,
aaguid: 'AAAAAAAAAAAAAAAAAAAAAA',
fmt: 'none',
attStmt: {},
};

test('should throw if multiple device public key matches', async () => {
await expect(verifyAuthenticationResponse({
...DpkVerifyAuthRespOpts,
userDevicePublicKeys: [devicePubKeyJSON, devicePubKeyJSON],
})).rejects.toThrowError(new Error('It is undetermined whether this is a known device.'));
});

expect(verification.authenticationInfo?.authenticatorExtensionResults).toMatchObject({
devicePublicKey: {
dpk: Buffer.from(
'A5010203262001215820991AABED9DE4271A9EDEAD8806F9DC96D6DCCD0C476253A5510489EC8379BE5B225820A0973CFDEDBB79E27FEF4EE7481673FB3312504DDCA5434CFD23431D6AD29EDA',
'hex',
),
sig: Buffer.from(
'3045022049526CD28AEF6B4E621A7D5936D2B504952FC0AE2313A4F0357AAFFFAEA964740221009D513ACAEFB0B32C765AAE6FEBA8C294685EFF63FF1CBF11ECF2107AF4FEB8F8',
'hex',
),
nonce: Buffer.from('', 'hex'),
scope: Buffer.from('00', 'hex'),
aaguid: Buffer.from('B93FD961F2E6462FB12282002247DE78', 'hex'),
},
test('should return the new device public key when no device public key matches', async () => {
await expect(verifyAuthenticationResponse({
...DpkVerifyAuthRespOpts,
userDevicePublicKeys: [differentDevicePubKeyJSON, differentDevicePubKeyJSON],
}).then(verification => verification.authenticationInfo.extensionOutputs?.devicePubKey)).resolves.toMatchObject({
authenticatorOutput: devicePubKey,
recognitionResult: 'unrecognized'
});
});

test('should return undefined when one device public key matches', async () => {
await expect(verifyAuthenticationResponse({
...DpkVerifyAuthRespOpts,
userDevicePublicKeys: [devicePubKeyJSON, differentDevicePubKeyJSON]
}).then(verification => verification.authenticationInfo.extensionOutputs?.devicePubKey)).resolves.toMatchObject({
authenticatorOutput: devicePubKey,
recognitionResult: 'recognized'
});
});
});

Expand Down
Loading