Skip to content

Commit aad120c

Browse files
committed
feat(passkeys): Add passkey manager functions
Because: - The passkey routes needs to enforce business rules before touching the database This commit: - Implements PasskeyManager with the full passkey lifecycle: register, post-auth update, list, rename, delete, and bulk delete - Guards maxPasskeysPerUser config with @min(1) to prevent accidentally disabling registration - Adds unit and integration tests for the manager Closes #FXA-13064
1 parent 98a9c12 commit aad120c

File tree

7 files changed

+899
-77
lines changed

7 files changed

+899
-77
lines changed

libs/accounts/passkey/src/lib/passkey.config.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import {
66
IsArray,
77
IsBoolean,
88
IsIn,
9+
IsNotEmpty,
910
IsNumber,
1011
IsOptional,
1112
IsString,
13+
Min,
1214
} from 'class-validator';
1315
import type {
1416
AuthenticatorAttachment,
@@ -19,13 +21,15 @@ import type {
1921
/**
2022
* Configuration for passkey (WebAuthn) functionality.
2123
*
22-
* This configuration is loaded from Convict in auth-server's config/index.ts
23-
* and passed to PasskeyService constructor.
24+
* Class-validator decorators enforce constraints at load time. A dedicated
25+
* config provider is responsible for loading this from Convict and registering
26+
* it with the NestJS DI container.
2427
*/
2528
export class PasskeyConfig {
2629
/**
2730
* Feature flag to enable/disable passkey functionality.
2831
*/
32+
@IsOptional()
2933
@IsBoolean()
3034
public enabled?: boolean;
3135

@@ -34,13 +38,15 @@ export class PasskeyConfig {
3438
* @example 'accounts.firefox.com'
3539
*/
3640
@IsString()
41+
@IsNotEmpty()
3742
public rpId!: string;
3843

3944
/**
4045
* WebAuthn Relying Party display name.
4146
* @example 'Mozilla Accounts'
4247
*/
4348
@IsString()
49+
@IsNotEmpty()
4450
public rpName!: string;
4551

4652
/**
@@ -53,15 +59,20 @@ export class PasskeyConfig {
5359

5460
/**
5561
* Maximum number of passkeys a user can register.
62+
* Must be at least 1; a value of 0 would silently disable registration.
5663
*/
5764
@IsNumber()
58-
public maxPasskeysPerUser?: number;
65+
@Min(1)
66+
public maxPasskeysPerUser!: number;
5967

6068
/**
6169
* Challenge expiration timeout in milliseconds.
70+
* Consumed by ChallengeManager (not yet implemented).
6271
* @example 300000 (5 minutes)
6372
*/
73+
@IsOptional()
6474
@IsNumber()
75+
@Min(1)
6576
public challengeTimeout?: number;
6677

6778
/**

libs/accounts/passkey/src/lib/passkey.errors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class PasskeyError extends BaseError {
2424
/** WebAuthn credential ID (when applicable) */
2525
readonly credentialId?: string;
2626
/** Additional structured context */
27-
readonly context: Record<string, any>;
27+
readonly context: Record<string, unknown>;
2828

2929
/**
3030
* Creates a PasskeyError.
@@ -42,7 +42,7 @@ export class PasskeyError extends BaseError {
4242
message: string,
4343
info: { errno?: number; uid?: string; credentialId?: string } & Record<
4444
string,
45-
any
45+
unknown
4646
>,
4747
cause?: Error
4848
) {

0 commit comments

Comments
 (0)