From 7f7c03755562fc1623b1e33d6df247ba3700dd40 Mon Sep 17 00:00:00 2001 From: Paul Taiwo Date: Mon, 13 Jan 2025 00:45:48 +0100 Subject: [PATCH 1/6] feat: add to passkey creation process --- package-lock.json | 7 +++++-- src/client.ts | 1 + src/types.ts | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b5e694..d62b496 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@passwordless-id/webauthn", - "version": "2.0.0", + "version": "2.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@passwordless-id/webauthn", - "version": "2.0.0", + "version": "2.1.2", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.12", @@ -16,6 +16,9 @@ "jest-ts-webcompat-resolver": "^1.0.0", "ts-jest": "^29.1.3", "typescript": "^5.4.5" + }, + "funding": { + "url": "https://github.com/sponsors/passwordless-id" } }, "node_modules/@ampproject/remapping": { diff --git a/src/client.ts b/src/client.ts index d3d8f67..ee0296c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -92,6 +92,7 @@ export async function register(options: RegisterOptions): Promise Date: Mon, 13 Jan 2025 00:57:12 +0100 Subject: [PATCH 2/6] chore(docs): add reference link to WebAuthn spec for --- src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index ee0296c..1bd4102 100644 --- a/src/client.ts +++ b/src/client.ts @@ -92,7 +92,7 @@ export async function register(options: RegisterOptions): Promise Date: Sat, 25 Jan 2025 00:38:20 +0100 Subject: [PATCH 3/6] feat: add customProperties option to allow arbitrary properties in creationOptions --- src/client.ts | 4 +- src/types.ts | 125 +++++++++++++++++++++++--------------------------- 2 files changed, 59 insertions(+), 70 deletions(-) diff --git a/src/client.ts b/src/client.ts index 1bd4102..90f5c16 100644 --- a/src/client.ts +++ b/src/client.ts @@ -92,8 +92,8 @@ export async function register(options: RegisterOptions): Promise; } - export interface User { - id?: string - name: string - displayName?: string + id?: string; + name: string; + displayName?: string; } /** * @see PublicKeyCredentialDescriptor */ export interface CredentialDescriptor { - id: Base64URLString, - transports: ExtendedAuthenticatorTransport[] + id: Base64URLString; + transports: ExtendedAuthenticatorTransport[]; } export interface AuthenticateOptions extends CommonOptions { - allowCredentials?: (CredentialDescriptor | string)[] - autocomplete?: boolean + allowCredentials?: (CredentialDescriptor | string)[]; + autocomplete?: boolean; } - - /********************************** JSON PAYLOADS **********************/ export interface RegistrationJSON extends RegistrationResponseJSON { - user: User // Added by this library, not by the WebAuthn protocol + user: User; // Added by this library, not by the WebAuthn protocol } export type AuthenticationJSON = AuthenticationResponseJSON; @@ -83,7 +78,6 @@ export interface RegistrationResponseJSON { type: PublicKeyCredentialType; } - /** * A slightly-modified AuthenticatorAttestationResponse to simplify working with ArrayBuffers that * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. @@ -114,7 +108,6 @@ export interface AuthenticationResponseJSON { type: PublicKeyCredentialType; } - /** * A slightly-modified AuthenticatorAssertionResponse to simplify working with ArrayBuffers that * are Base64URL-encoded in the browser so that they can be sent as JSON to the server. @@ -131,11 +124,10 @@ export interface AuthenticatorAssertionResponseJSON { /** * WebAuthn added transports that are not yet defined in the DOM definitions. * However, it's partly obsoleted by the `hints` in the registration/authentication request. - * + * * https://w3c.github.io/webauthn/#enumdef-authenticatortransport */ -export type ExtendedAuthenticatorTransport = AuthenticatorTransport | 'smart-card'; // missing in the current DOM types - +export type ExtendedAuthenticatorTransport = AuthenticatorTransport | "smart-card"; // missing in the current DOM types /************************** PARSED **************************/ @@ -143,26 +135,26 @@ export type ExtendedAuthenticatorTransport = AuthenticatorTransport | 'smart-car * https://w3c.github.io/webauthn/#dictionary-client-data */ export interface CollectedClientData { - type: string - challenge: Base64URLString - origin: string - topOrigin?: string + type: string; + challenge: Base64URLString; + origin: string; + topOrigin?: string; crossOrigin?: boolean; } export interface AuthenticatorParsed { - rpIdHash:Base64URLString, + rpIdHash: Base64URLString; flags: { - userPresent: boolean, - userVerified: boolean, - backupEligibility: boolean, - backupState: boolean, - attestedData: boolean, - extensionsIncluded: boolean - }, - signCount: number, - aaguid: string, - attestation?: Base64URLString + userPresent: boolean; + userVerified: boolean; + backupEligibility: boolean; + backupState: boolean; + attestedData: boolean; + extensionsIncluded: boolean; + }; + signCount: number; + aaguid: string; + attestation?: Base64URLString; } /** @@ -172,41 +164,38 @@ export interface AuthenticatorParsed { /************************** RESULTS *************************/ export interface RegistrationInfo { - user: UserInfo - credential: CredentialInfo - authenticator: AuthenticatorInfo - synced: boolean - userVerified: boolean + user: UserInfo; + credential: CredentialInfo; + authenticator: AuthenticatorInfo; + synced: boolean; + userVerified: boolean; } - export interface AuthenticationInfo { - credentialId: Base64URLString - userId?: Base64URLString - userVerified: boolean - counter: number - authenticatorAttachment?: AuthenticatorAttachment + credentialId: Base64URLString; + userId?: Base64URLString; + userVerified: boolean; + counter: number; + authenticatorAttachment?: AuthenticatorAttachment; } - export interface UserInfo { - id: string - name: string - displayName: string + id: string; + name: string; + displayName: string; } - export interface CredentialInfo { - id: string - publicKey: string - algorithm: NamedAlgo - transports: ExtendedAuthenticatorTransport[] + id: string; + publicKey: string; + algorithm: NamedAlgo; + transports: ExtendedAuthenticatorTransport[]; } export interface AuthenticatorInfo { - aaguid: string - name: string - icon_light: string - icon_dark: string - counter: number + aaguid: string; + name: string; + icon_light: string; + icon_dark: string; + counter: number; } From 49d92d934d2454584ed731fc72bb68401894eb37 Mon Sep 17 00:00:00 2001 From: Paul Taiwo Date: Sat, 25 Jan 2025 00:52:07 +0100 Subject: [PATCH 4/6] docs: clarify usage of customProperties --- src/client.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/client.ts b/src/client.ts index 90f5c16..ada3861 100644 --- a/src/client.ts +++ b/src/client.ts @@ -53,6 +53,20 @@ let ongoingAuth: AbortController | null = null; * @param {'discouraged'|'preferred'|'required'} [discoverable] A "discoverable" credential can be selected using `authenticate(...)` without providing credential IDs. * Instead, a native pop-up will appear for user selection. * This may have an impact on the "passkeys" user experience and syncing behavior of the key. + *@param {Record} [options.customProperties] - **Advanced usage**: An object of additional + * properties that will be merged into the WebAuthn creation options. This can be used to + * explicitly set fields such as `excludeCredentials`. + * + * @example + * const registration = await register({ + * user: { id: 'user-id', name: 'john', displayName: 'John' }, + * challenge: 'base64url-encoded-challenge', + * customProperties: { + * excludeCredentials: [ + * { id: 'base64url-credential-id', type: 'public-key' }, + * ], + * }, + * }); */ export async function register(options: RegisterOptions): Promise { From b38fcaba75caee6dd7aba58b6830a37ea108995d Mon Sep 17 00:00:00 2001 From: Paul Taiwo Date: Sat, 25 Jan 2025 22:23:58 +0100 Subject: [PATCH 5/6] feat: add customProperties option to allow arbitrary properties in authenticateOptions --- src/client.ts | 18 +++++++++++++++++- src/types.ts | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index ada3861..7f94b67 100644 --- a/src/client.ts +++ b/src/client.ts @@ -53,7 +53,7 @@ let ongoingAuth: AbortController | null = null; * @param {'discouraged'|'preferred'|'required'} [discoverable] A "discoverable" credential can be selected using `authenticate(...)` without providing credential IDs. * Instead, a native pop-up will appear for user selection. * This may have an impact on the "passkeys" user experience and syncing behavior of the key. - *@param {Record} [options.customProperties] - **Advanced usage**: An object of additional + * @param {Record} [options.customProperties] - **Advanced usage**: An object of additional * properties that will be merged into the WebAuthn creation options. This can be used to * explicitly set fields such as `excludeCredentials`. * @@ -165,6 +165,21 @@ export async function isAutocompleteAvailable() { * @param {number} [timeout=60000] Number of milliseconds the user has to respond to the biometric/PIN check. * @param {'required'|'preferred'|'discouraged'} [userVerification='required'] Whether to prompt for biometric/PIN check or not. * @param {boolean} [conditional] Does not return directly, but only when the user has selected a credential in the input field with `autocomplete="username webauthn"` + * @param {Record} [options.customProperties] - **Advanced usage**: An object of additional + * properties that will be merged into the WebAuthn authenticate options. This can be used to + * explicitly set fields such as `extensions`. + * + * @example + * const authentication = await authenticate({ + * challenge: 'base64url-encoded-challenge', + * allowCredentials: [], + * customProperties: { + * extensions: { + * uvm: true, // User verification methods extension + * appid: "https://legacy-app-id.example.com", // App ID extension for backward compatibility + * }, + * }, + * }); */ export async function authenticate(options: AuthenticateOptions): Promise { if (!utils.isBase64url(options.challenge)) @@ -180,6 +195,7 @@ export async function authenticate(options: AuthenticateOptions): Promise; } /********************************** JSON PAYLOADS **********************/ From 6529e36022edc99c73bef3f35dfa4b0d54c312f6 Mon Sep 17 00:00:00 2001 From: Paul Taiwo Date: Sun, 26 Jan 2025 14:36:11 +0100 Subject: [PATCH 6/6] docs: update documentation to include details about customProperties --- docs/authentication.md | 3 +-- docs/registration.md | 2 +- src/client.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/authentication.md b/docs/authentication.md index 880e704..8c86a39 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -74,8 +74,7 @@ The following options are available. | `domain` | `window.location.hostname` | By default, the current domain name is used. Also known as "relying party id". You may want to customize it for ... | `allowedCredentials` | The list of credentials and the transports it supports. Used to skip passkey selection. Either a list of credential ids (discouraged) or list of credential objects with `id` and supported `transports` (recommended). | `autocomplete` | `false` | See concepts - - +| `customProperties` | `{}` | An object of additional properties that will be merged into the WebAuthn authenticate options. This can be used to explicitly set fields such as `extensions`. 3️⃣ Send the payload to the server diff --git a/docs/registration.md b/docs/registration.md index ee3dd04..7af0b28 100644 --- a/docs/registration.md +++ b/docs/registration.md @@ -64,7 +64,7 @@ Besides the required `user` and `challenge`, it has following options. | `timeout` | - | How long the native authentication popup stays open before aborting the authentication process. | `attestation` | `true` | Whether or not to provide "attestation" in the result. The attestation can be used to prove the authenticator device model's authenticity. Note that not all authenticators provide this (looking at you apple), it might be anonymized, and its verification is complex. | `domain` | `window.location.hostname` | This can be set to a parent domain, to have the passkey valid for all subdomains. - +| `customProperties` | `{}` | An object of additional properties that will be merged into the WebAuthn create options. This can be used to explicitly set fields such as `excludeCredentials`. diff --git a/src/client.ts b/src/client.ts index 7f94b67..8308da1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -54,7 +54,7 @@ let ongoingAuth: AbortController | null = null; * Instead, a native pop-up will appear for user selection. * This may have an impact on the "passkeys" user experience and syncing behavior of the key. * @param {Record} [options.customProperties] - **Advanced usage**: An object of additional - * properties that will be merged into the WebAuthn creation options. This can be used to + * properties that will be merged into the WebAuthn create options. This can be used to * explicitly set fields such as `excludeCredentials`. * * @example