Skip to content

Commit e4f2a6c

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 - Adds name validation (empty/whitespace/too long) with PasskeyInvalidNameError (errno 229) - Tightens updatePasskeyName to match on both uid and credentialId, preventing cross-user renames - Guards maxPasskeysPerUser config with @min(1) to prevent accidentally disabling registration - Adds unit and integration tests for the manager Closes #FXA-13064
1 parent f4c4f28 commit e4f2a6c

File tree

7 files changed

+1056
-25
lines changed

7 files changed

+1056
-25
lines changed

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

Lines changed: 14 additions & 2 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,21 @@ 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
*/
64+
@IsOptional()
5765
@IsNumber()
66+
@Min(1)
5867
public maxPasskeysPerUser?: number;
5968

6069
/**
6170
* Challenge expiration timeout in milliseconds.
71+
* Consumed by ChallengeManager (not yet implemented).
6272
* @example 300000 (5 minutes)
6373
*/
74+
@IsOptional()
6475
@IsNumber()
76+
@Min(1)
6577
public challengeTimeout?: number;
6678

6779
/**

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)