summaryrefslogtreecommitdiffhomepage
path: root/packages/server/src
diff options
context:
space:
mode:
authorMatthew Miller <matthew@millerti.me>2024-02-24 16:45:51 -0800
committerGitHub <noreply@github.com>2024-02-24 16:45:51 -0800
commit6eb5ac62e778c6c39c936cfce3309b9aa852021d (patch)
tree1d33f0a5ea58d74d0b41e6cc407ef604d76c399e /packages/server/src
parentfe2245b9c1b7ada2099a6411c8ce2e6e6f18bbf9 (diff)
parentb835ce41e1936765a49d7f7114116d78ddb67a1c (diff)
Merge pull request #529 from MasterKale/fix/528-simplify-use-of-credential-id
fix/528-simplify-use-of-credential-id
Diffstat (limited to 'packages/server/src')
-rw-r--r--packages/server/src/authentication/generateAuthenticationOptions.test.ts40
-rw-r--r--packages/server/src/authentication/generateAuthenticationOptions.ts23
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.test.ts19
-rw-r--r--packages/server/src/authentication/verifyAuthenticationResponse.ts15
-rw-r--r--packages/server/src/deps.ts2
-rw-r--r--packages/server/src/helpers/convertCertBufferToPEM.ts2
-rw-r--r--packages/server/src/helpers/iso/isoBase64URL.ts12
-rw-r--r--packages/server/src/registration/generateRegistrationOptions.test.ts5
-rw-r--r--packages/server/src/registration/generateRegistrationOptions.ts23
-rw-r--r--packages/server/src/registration/verifyRegistrationResponse.test.ts29
-rw-r--r--packages/server/src/registration/verifyRegistrationResponse.ts13
11 files changed, 102 insertions, 81 deletions
diff --git a/packages/server/src/authentication/generateAuthenticationOptions.test.ts b/packages/server/src/authentication/generateAuthenticationOptions.test.ts
index 8d55580..49d668e 100644
--- a/packages/server/src/authentication/generateAuthenticationOptions.test.ts
+++ b/packages/server/src/authentication/generateAuthenticationOptions.test.ts
@@ -11,13 +11,11 @@ Deno.test('should generate credential request options suitable for sending via J
const options = await generateAuthenticationOptions({
allowCredentials: [
{
- id: isoUint8Array.fromASCIIString('1234'),
- type: 'public-key',
+ id: '1234',
transports: ['usb', 'nfc'],
},
{
- id: isoUint8Array.fromASCIIString('5678'),
- type: 'public-key',
+ id: '5678',
transports: ['internal'],
},
],
@@ -30,12 +28,12 @@ Deno.test('should generate credential request options suitable for sending via J
challenge: challengeString,
allowCredentials: [
{
- id: 'MTIzNA',
+ id: '1234',
type: 'public-key',
transports: ['usb', 'nfc'],
},
{
- id: 'NTY3OA',
+ id: '5678',
type: 'public-key',
transports: ['internal'],
},
@@ -51,8 +49,8 @@ Deno.test('defaults to 60 seconds if no timeout is specified', async () => {
const options = await generateAuthenticationOptions({
challenge: challengeBuffer,
allowCredentials: [
- { id: isoUint8Array.fromASCIIString('1234'), type: 'public-key' },
- { id: isoUint8Array.fromASCIIString('5678'), type: 'public-key' },
+ { id: '1234' },
+ { id: '5678' },
],
});
@@ -63,8 +61,8 @@ Deno.test('should set userVerification to "preferred" if not specified', async (
const options = await generateAuthenticationOptions({
challenge: challengeBuffer,
allowCredentials: [
- { id: isoUint8Array.fromASCIIString('1234'), type: 'public-key' },
- { id: isoUint8Array.fromASCIIString('5678'), type: 'public-key' },
+ { id: '1234' },
+ { id: '5678' },
],
});
@@ -94,8 +92,8 @@ Deno.test('should set userVerification if specified', async () => {
const options = await generateAuthenticationOptions({
challenge: challengeBuffer,
allowCredentials: [
- { id: isoUint8Array.fromASCIIString('1234'), type: 'public-key' },
- { id: isoUint8Array.fromASCIIString('5678'), type: 'public-key' },
+ { id: '1234' },
+ { id: '5678' },
],
userVerification: 'required',
});
@@ -107,8 +105,8 @@ Deno.test('should set extensions if specified', async () => {
const options = await generateAuthenticationOptions({
challenge: challengeBuffer,
allowCredentials: [
- { id: isoUint8Array.fromASCIIString('1234'), type: 'public-key' },
- { id: isoUint8Array.fromASCIIString('5678'), type: 'public-key' },
+ { id: '1234' },
+ { id: '5678' },
],
extensions: { appid: 'simplewebauthn' },
});
@@ -117,19 +115,17 @@ Deno.test('should set extensions if specified', async () => {
});
Deno.test('should generate a challenge if one is not provided', async () => {
- const opts = {
+ // @ts-ignore 2345
+ const options = await generateAuthenticationOptions({
allowCredentials: [
- { id: isoUint8Array.fromASCIIString('1234'), type: 'public-key' },
- { id: isoUint8Array.fromASCIIString('5678'), type: 'public-key' },
+ { id: '1234' },
+ { id: '5678' },
],
- };
-
- // @ts-ignore 2345
- const options = await generateAuthenticationOptions(opts);
+ });
// Assert basic properties of the challenge
assert(options.challenge.length >= 16);
- assert(isoBase64URL.isBase64url(options.challenge));
+ assert(isoBase64URL.isBase64URL(options.challenge));
});
Deno.test('should treat string challenges as UTF-8 strings', async () => {
diff --git a/packages/server/src/authentication/generateAuthenticationOptions.ts b/packages/server/src/authentication/generateAuthenticationOptions.ts
index b1c8166..44ed2b7 100644
--- a/packages/server/src/authentication/generateAuthenticationOptions.ts
+++ b/packages/server/src/authentication/generateAuthenticationOptions.ts
@@ -1,6 +1,7 @@
import type {
AuthenticationExtensionsClientInputs,
- PublicKeyCredentialDescriptorFuture,
+ AuthenticatorTransportFuture,
+ Base64URLString,
PublicKeyCredentialRequestOptionsJSON,
UserVerificationRequirement,
} from '../deps.ts';
@@ -8,7 +9,10 @@ import { isoBase64URL, isoUint8Array } from '../helpers/iso/index.ts';
import { generateChallenge } from '../helpers/generateChallenge.ts';
export type GenerateAuthenticationOptionsOpts = {
- allowCredentials?: PublicKeyCredentialDescriptorFuture[];
+ allowCredentials?: {
+ id: Base64URLString;
+ transports?: AuthenticatorTransportFuture[];
+ }[];
challenge?: string | Uint8Array;
timeout?: number;
userVerification?: UserVerificationRequirement;
@@ -51,10 +55,17 @@ export async function generateAuthenticationOptions(
return {
challenge: isoBase64URL.fromBuffer(_challenge),
- allowCredentials: allowCredentials?.map((cred) => ({
- ...cred,
- id: isoBase64URL.fromBuffer(cred.id as Uint8Array),
- })),
+ allowCredentials: allowCredentials?.map((cred) => {
+ if (!isoBase64URL.isBase64URL(cred.id)) {
+ throw new Error(`excludeCredential id "${cred.id}" is not a valid base64url string`);
+ }
+
+ return {
+ ...cred,
+ id: isoBase64URL.trimPadding(cred.id),
+ type: 'public-key',
+ };
+ }),
timeout,
userVerification,
extensions,
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
index b150aff..844726f 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.test.ts
@@ -267,9 +267,7 @@ Deno.test('should verify TPM assertion', { ignore: true }, async () => {
expectedRPID: 'dev.dontneeda.pw',
authenticator: {
credentialPublicKey: isoBase64URL.toBuffer('BAEAAQ'),
- credentialID: isoBase64URL.toBuffer(
- 'YJ8FMM-AmcUt73XPX341WXWd7ypBMylGjjhu0g3VzME',
- ),
+ credentialID: 'YJ8FMM-AmcUt73XPX341WXWd7ypBMylGjjhu0g3VzME',
counter: 0,
},
});
@@ -400,9 +398,8 @@ Deno.test('should pass verification if custom challenge verifier returns true',
expectedOrigin: 'http://localhost:8000',
expectedRPID: 'localhost',
authenticator: {
- credentialID: isoBase64URL.toBuffer(
+ credentialID:
'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
- ),
credentialPublicKey: isoBase64URL.toBuffer(
'pQECAyYgASFYILTrxTUQv3X4DRM6L_pk65FSMebenhCx3RMsTKoBm-AxIlggEf3qk5552QLNSh1T1oQs7_2C2qysDwN4r4fCp52Hsqs',
),
@@ -461,9 +458,8 @@ Deno.test('should pass verification if custom challenge verifier returns a Promi
expectedOrigin: 'http://localhost:8000',
expectedRPID: 'localhost',
authenticator: {
- credentialID: isoBase64URL.toBuffer(
+ credentialID:
'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
- ),
credentialPublicKey: isoBase64URL.toBuffer(
'pQECAyYgASFYILTrxTUQv3X4DRM6L_pk65FSMebenhCx3RMsTKoBm-AxIlggEf3qk5552QLNSh1T1oQs7_2C2qysDwN4r4fCp52Hsqs',
),
@@ -525,9 +521,8 @@ Deno.test('should return authenticator extension output', async () => {
expectedRPID: 'try-webauthn.appspot.com',
expectedChallenge: 'iZsVCztrDW7D2U_GHCIlYKLwV2bCsBTRqVQUnJXn9Tk',
authenticator: {
- credentialID: isoBase64URL.toBuffer(
+ credentialID:
'AaIBxnYfL2pDWJmIii6CYgHBruhVvFGHheWamphVioG_TnEXxKA9MW4FWnJh21zsbmRpRJso9i2JmAtWOtXfVd4oXTgYVusXwhWWsA',
- ),
credentialPublicKey: isoBase64URL.toBuffer(
'pQECAyYgASFYILTrxTUQv3X4DRM6L_pk65FSMebenhCx3RMsTKoBm-AxIlggEf3qk5552QLNSh1T1oQs7_2C2qysDwN4r4fCp52Hsqs',
),
@@ -611,9 +606,8 @@ const authenticator: AuthenticatorDevice = {
credentialPublicKey: isoBase64URL.toBuffer(
'pQECAyYgASFYIIheFp-u6GvFT2LNGovf3ZrT0iFVBsA_76rRysxRG9A1Ilgg8WGeA6hPmnab0HAViUYVRkwTNcN77QBf_RR0dv3lIvQ',
),
- credentialID: isoBase64URL.toBuffer(
+ credentialID:
'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew',
- ),
counter: 143,
};
@@ -641,8 +635,7 @@ const authenticatorFirstTimeUsed: AuthenticatorDevice = {
credentialPublicKey: isoBase64URL.toBuffer(
'pQECAyYgASFYIGmaxR4mBbukc2QhtW2ldhAAd555r-ljlGQN8MbcTnPPIlgg9CyUlE-0AB2fbzZbNgBvJuRa7r6o2jPphOmtyNPR_kY',
),
- credentialID: isoBase64URL.toBuffer(
+ credentialID:
'wSisR0_4hlzw3Y1tj4uNwwifIhRa-ZxWJwWbnfror0pVK9qPdBPO5pW3gasPqn6wXHb0LNhXB_IrA1nFoSQJ9A',
- ),
counter: 0,
};
diff --git a/packages/server/src/authentication/verifyAuthenticationResponse.ts b/packages/server/src/authentication/verifyAuthenticationResponse.ts
index c938598..c6430c0 100644
--- a/packages/server/src/authentication/verifyAuthenticationResponse.ts
+++ b/packages/server/src/authentication/verifyAuthenticationResponse.ts
@@ -1,6 +1,7 @@
import type {
AuthenticationResponseJSON,
AuthenticatorDevice,
+ Base64URLString,
CredentialDeviceType,
UserVerificationRequirement,
} from '../deps.ts';
@@ -94,11 +95,15 @@ export async function verifyAuthenticationResponse(
if (Array.isArray(expectedType)) {
if (!expectedType.includes(type)) {
const joinedExpectedType = expectedType.join(', ');
- throw new Error(`Unexpected authentication response type "${type}", expected one of: ${joinedExpectedType}`);
+ throw new Error(
+ `Unexpected authentication response type "${type}", expected one of: ${joinedExpectedType}`,
+ );
}
} else if (expectedType) {
if (type !== expectedType) {
- throw new Error(`Unexpected authentication response type "${type}", expected "${expectedType}"`);
+ throw new Error(
+ `Unexpected authentication response type "${type}", expected "${expectedType}"`,
+ );
}
} else if (type !== 'webauthn.get') {
throw new Error(`Unexpected authentication response type: ${type}`);
@@ -133,13 +138,13 @@ export async function verifyAuthenticationResponse(
}
}
- if (!isoBase64URL.isBase64url(assertionResponse.authenticatorData)) {
+ if (!isoBase64URL.isBase64URL(assertionResponse.authenticatorData)) {
throw new Error(
'Credential response authenticatorData was not a base64url string',
);
}
- if (!isoBase64URL.isBase64url(assertionResponse.signature)) {
+ if (!isoBase64URL.isBase64URL(assertionResponse.signature)) {
throw new Error('Credential response signature was not a base64url string');
}
@@ -280,7 +285,7 @@ export async function verifyAuthenticationResponse(
export type VerifiedAuthenticationResponse = {
verified: boolean;
authenticationInfo: {
- credentialID: Uint8Array;
+ credentialID: Base64URLString;
newCounter: number;
userVerified: boolean;
credentialDeviceType: CredentialDeviceType;
diff --git a/packages/server/src/deps.ts b/packages/server/src/deps.ts
index 3acef11..53b6247 100644
--- a/packages/server/src/deps.ts
+++ b/packages/server/src/deps.ts
@@ -5,12 +5,12 @@ export type {
AuthenticationResponseJSON,
AuthenticatorDevice,
AuthenticatorSelectionCriteria,
+ AuthenticatorTransportFuture,
Base64URLString,
COSEAlgorithmIdentifier,
CredentialDeviceType,
Crypto,
PublicKeyCredentialCreationOptionsJSON,
- PublicKeyCredentialDescriptorFuture,
PublicKeyCredentialParameters,
PublicKeyCredentialRequestOptionsJSON,
RegistrationResponseJSON,
diff --git a/packages/server/src/helpers/convertCertBufferToPEM.ts b/packages/server/src/helpers/convertCertBufferToPEM.ts
index d7cd4c0..6e7d5fc 100644
--- a/packages/server/src/helpers/convertCertBufferToPEM.ts
+++ b/packages/server/src/helpers/convertCertBufferToPEM.ts
@@ -13,7 +13,7 @@ export function convertCertBufferToPEM(
* Get certBuffer to a base64 representation
*/
if (typeof certBuffer === 'string') {
- if (isoBase64URL.isBase64url(certBuffer)) {
+ if (isoBase64URL.isBase64URL(certBuffer)) {
b64cert = isoBase64URL.toBase64(certBuffer);
} else if (isoBase64URL.isBase64(certBuffer)) {
b64cert = certBuffer;
diff --git a/packages/server/src/helpers/iso/isoBase64URL.ts b/packages/server/src/helpers/iso/isoBase64URL.ts
index 5098b0c..99bf82a 100644
--- a/packages/server/src/helpers/iso/isoBase64URL.ts
+++ b/packages/server/src/helpers/iso/isoBase64URL.ts
@@ -1,4 +1,5 @@
import { base64 } from '../../deps.ts';
+import type { Base64URLString } from '../../deps.ts';
/**
* Decode from a Base64URL-encoded string to an ArrayBuffer. Best used when converting a
@@ -63,8 +64,15 @@ export function isBase64(input: string): boolean {
/**
* Confirm that the string is encoded into base64url, with support for optional padding
*/
-export function isBase64url(input: string): boolean {
+export function isBase64URL(input: string): boolean {
// Trim padding characters from the string if present
- input = input.replace(/=/g, '');
+ input = trimPadding(input);
return base64.validate(input, true);
}
+
+/**
+ * Remove optional padding from a base64url-encoded string
+ */
+export function trimPadding(input: Base64URLString): Base64URLString {
+ return input.replace(/=/g, '');
+}
diff --git a/packages/server/src/registration/generateRegistrationOptions.test.ts b/packages/server/src/registration/generateRegistrationOptions.test.ts
index fded674..6704d23 100644
--- a/packages/server/src/registration/generateRegistrationOptions.test.ts
+++ b/packages/server/src/registration/generateRegistrationOptions.test.ts
@@ -67,8 +67,7 @@ Deno.test('should map excluded credential IDs if specified', async () => {
userName: 'usernameHere',
excludeCredentials: [
{
- id: isoUint8Array.fromASCIIString('someIDhere'),
- type: 'public-key',
+ id: 'someIDhere',
transports: ['usb', 'ble', 'nfc', 'internal'],
},
],
@@ -78,7 +77,7 @@ Deno.test('should map excluded credential IDs if specified', async () => {
options.excludeCredentials,
[
{
- id: 'c29tZUlEaGVyZQ',
+ id: 'someIDhere',
type: 'public-key',
transports: ['usb', 'ble', 'nfc', 'internal'],
},
diff --git a/packages/server/src/registration/generateRegistrationOptions.ts b/packages/server/src/registration/generateRegistrationOptions.ts
index c894abb..2504751 100644
--- a/packages/server/src/registration/generateRegistrationOptions.ts
+++ b/packages/server/src/registration/generateRegistrationOptions.ts
@@ -2,9 +2,10 @@ import type {
AttestationConveyancePreference,
AuthenticationExtensionsClientInputs,
AuthenticatorSelectionCriteria,
+ AuthenticatorTransportFuture,
+ Base64URLString,
COSEAlgorithmIdentifier,
PublicKeyCredentialCreationOptionsJSON,
- PublicKeyCredentialDescriptorFuture,
PublicKeyCredentialParameters,
} from '../deps.ts';
import { generateChallenge } from '../helpers/generateChallenge.ts';
@@ -19,7 +20,10 @@ export type GenerateRegistrationOptionsOpts = {
userDisplayName?: string;
timeout?: number;
attestationType?: AttestationConveyancePreference;
- excludeCredentials?: PublicKeyCredentialDescriptorFuture[];
+ excludeCredentials?: {
+ id: Base64URLString;
+ transports?: AuthenticatorTransportFuture[];
+ }[];
authenticatorSelection?: AuthenticatorSelectionCriteria;
extensions?: AuthenticationExtensionsClientInputs;
supportedAlgorithmIDs?: COSEAlgorithmIdentifier[];
@@ -174,10 +178,17 @@ export async function generateRegistrationOptions(
pubKeyCredParams,
timeout,
attestation: attestationType,
- excludeCredentials: excludeCredentials.map((cred) => ({
- ...cred,
- id: isoBase64URL.fromBuffer(cred.id as Uint8Array),
- })),
+ excludeCredentials: excludeCredentials.map((cred) => {
+ if (!isoBase64URL.isBase64URL(cred.id)) {
+ throw new Error(`excludeCredential id "${cred.id}" is not a valid base64url string`);
+ }
+
+ return {
+ ...cred,
+ id: isoBase64URL.trimPadding(cred.id),
+ type: 'public-key',
+ };
+ }),
authenticatorSelection,
extensions: {
...extensions,
diff --git a/packages/server/src/registration/verifyRegistrationResponse.test.ts b/packages/server/src/registration/verifyRegistrationResponse.test.ts
index fbe7aed..89b4694 100644
--- a/packages/server/src/registration/verifyRegistrationResponse.test.ts
+++ b/packages/server/src/registration/verifyRegistrationResponse.test.ts
@@ -88,10 +88,7 @@ Deno.test('should verify Packed (EC2) attestation', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer(
- 'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3U' +
- 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q',
- ),
+ 'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3UB2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q',
);
});
@@ -115,9 +112,7 @@ Deno.test('should verify Packed (X5C) attestation', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer(
- '4rrvMciHCkdLQ2HghazIp1sMc8TmV8W8RgoX-x8tqV_1AmlqWACqUK8mBGLandr-htduQKPzgb2yWxOFV56Tlg',
- ),
+ '4rrvMciHCkdLQ2HghazIp1sMc8TmV8W8RgoX-x8tqV_1AmlqWACqUK8mBGLandr-htduQKPzgb2yWxOFV56Tlg',
);
});
@@ -140,9 +135,7 @@ Deno.test('should verify None attestation', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer(
- 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY',
- ),
+ 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY',
);
assertEquals(
verification.registrationInfo?.origin,
@@ -182,7 +175,7 @@ Deno.test('should verify None attestation w/RSA public key', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer('kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo'),
+ 'kGXv4RJWLeXRw8Yf3T22K3Gq_GGeDv9OKYmAHLm0Ylo',
);
assertEquals(
verification.registrationInfo?.origin,
@@ -227,10 +220,10 @@ Deno.test('should throw when response type is not expected value', async () => {
expectedChallenge: attestationNoneChallenge,
expectedOrigin: 'https://dev.dontneeda.pw',
expectedRPID: 'dev.dontneeda.pw',
- expectedType: 'something.get'
+ expectedType: 'something.get',
}),
Error,
- 'registration response type',
+ 'registration response type',
);
});
@@ -242,7 +235,7 @@ Deno.test('should throw when response type is not in list of expected types', as
expectedChallenge: attestationNoneChallenge,
expectedOrigin: 'https://dev.dontneeda.pw',
expectedRPID: 'dev.dontneeda.pw',
- expectedType: ['something.create', 'something.else.create']
+ expectedType: ['something.create', 'something.else.create'],
}),
Error,
'registration response type',
@@ -301,7 +294,7 @@ Deno.test('should validate when attestation type is not webauthn.create and expe
expectedChallenge: challenge,
expectedOrigin: origin,
expectedRPID: 'dev.dontneeda.pw',
- expectedType: 'webauthn.goodtype'
+ expectedType: 'webauthn.goodtype',
});
assert(verification.verified);
@@ -600,7 +593,7 @@ Deno.test('should validate TPM RSA response (SHA256)', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer('lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM'),
+ 'lGkWHPe88VpnNYgVBxzon_MRR9-gmgODveQ16uM_bPM',
);
assertEquals(
verification.registrationInfo?.origin,
@@ -642,7 +635,7 @@ Deno.test('should validate TPM RSA response (SHA1)', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer('oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU'),
+ 'oELnad0f6-g2BtzEn_78iLNoubarlq0xFtOtAMXnflU',
);
assertEquals(
verification.registrationInfo?.origin,
@@ -684,7 +677,7 @@ Deno.test('should validate Android-Key response', async () => {
);
assertEquals(
verification.registrationInfo?.credentialID,
- isoBase64URL.toBuffer('PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o'),
+ 'PPa1spYTB680cQq5q6qBtFuPLLdG1FQ73EastkT8n0o',
);
assertEquals(
verification.registrationInfo?.origin,
diff --git a/packages/server/src/registration/verifyRegistrationResponse.ts b/packages/server/src/registration/verifyRegistrationResponse.ts
index d2399e8..7851e20 100644
--- a/packages/server/src/registration/verifyRegistrationResponse.ts
+++ b/packages/server/src/registration/verifyRegistrationResponse.ts
@@ -1,4 +1,5 @@
import type {
+ Base64URLString,
COSEAlgorithmIdentifier,
CredentialDeviceType,
RegistrationResponseJSON,
@@ -95,11 +96,15 @@ export async function verifyRegistrationResponse(
if (Array.isArray(expectedType)) {
if (!expectedType.includes(type)) {
const joinedExpectedType = expectedType.join(', ');
- throw new Error(`Unexpected registration response type "${type}", expected one of: ${joinedExpectedType}`);
+ throw new Error(
+ `Unexpected registration response type "${type}", expected one of: ${joinedExpectedType}`,
+ );
}
} else if (expectedType) {
if (type !== expectedType) {
- throw new Error(`Unexpected registration response type "${type}", expected "${expectedType}"`);
+ throw new Error(
+ `Unexpected registration response type "${type}", expected "${expectedType}"`,
+ );
}
} else if (type !== 'webauthn.create') {
throw new Error(`Unexpected registration response type: ${type}`);
@@ -280,7 +285,7 @@ export async function verifyRegistrationResponse(
fmt,
counter,
aaguid: convertAAGUIDToString(aaguid),
- credentialID,
+ credentialID: isoBase64URL.fromBuffer(credentialID),
credentialPublicKey,
credentialType,
attestationObject,
@@ -328,7 +333,7 @@ export type VerifiedRegistrationResponse = {
fmt: AttestationFormat;
counter: number;
aaguid: string;
- credentialID: Uint8Array;
+ credentialID: Base64URLString;
credentialPublicKey: Uint8Array;
credentialType: 'public-key';
attestationObject: Uint8Array;