Skip to content

Commit b49047a

Browse files
committed
fix(passkeys): gate rpId/allowedOrigins validation on enabled flag
Because: * buildPasskeyConfig validated rpId and allowedOrigins unconditionally, causing auth-server to crash at startup on any environment where passkeys is disabled but the required fields are left at their Convict defaults (empty string / empty array) This commit: * Adds @ValidateIf((o) => o.enabled) to rpId and allowedOrigins in PasskeyConfig so those constraints are skipped when the feature is off * Wraps the buildPasskeyConfig call in key_server.js with try/catch so that misconfigured-but-enabled passkeys logs via log.error (structured mozlog) before exiting, instead of falling through to console.error * Adds a test asserting that disabled passkeys with empty defaults no longer throws Closes #FXA-13378
1 parent e4f4e48 commit b49047a

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
IsOptional,
1313
IsString,
1414
Matches,
15+
ValidateIf,
1516
} from 'class-validator';
1617
import type {
1718
AuthenticatorAttachment,
@@ -39,6 +40,7 @@ export class PasskeyConfig {
3940
* WebAuthn Relying Party ID (must match the domain).
4041
* @example 'accounts.firefox.com'
4142
*/
43+
@ValidateIf((o) => o.enabled)
4244
@IsString()
4345
@IsNotEmpty()
4446
public rpId!: string;
@@ -48,6 +50,7 @@ export class PasskeyConfig {
4850
* Must include protocol and domain.
4951
* @example ['https://accounts.firefox.com', 'https://accounts.stage.mozaws.net']
5052
*/
53+
@ValidateIf((o) => o.enabled)
5154
@IsArray()
5255
@ArrayMinSize(1)
5356
@IsString({ each: true })

libs/accounts/passkey/src/lib/passkey.provider.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,21 @@ describe('PasskeyConfigProvider', () => {
117117
});
118118
});
119119

120+
describe('when passkeys is disabled', () => {
121+
it('allows empty rpId and allowedOrigins without throwing', async () => {
122+
const { config } = await buildModule({
123+
...VALID_RAW_CONFIG,
124+
enabled: false,
125+
rpId: '',
126+
allowedOrigins: [],
127+
});
128+
expect(config).toBeInstanceOf(PasskeyConfig);
129+
expect(config!.enabled).toBe(false);
130+
expect(config!.rpId).toBe('');
131+
expect(config!.allowedOrigins).toEqual([]);
132+
});
133+
});
134+
120135
describe('when config is invalid', () => {
121136
it('throws', async () => {
122137
await expect(() =>
@@ -138,7 +153,7 @@ describe('PasskeyConfigProvider', () => {
138153
);
139154
});
140155

141-
it('rejects allowedOrigins with trailing path', async () => {
156+
it('rejects allowedOrigins URL with a path suffix', async () => {
142157
await expect(() =>
143158
buildModule({
144159
...VALID_RAW_CONFIG,

packages/fxa-auth-server/bin/key_server.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,13 @@ async function run(config) {
310310
...config.redis,
311311
...config.redis.passkey,
312312
});
313-
const passkeyConfig = buildPasskeyConfig(config.passkeys);
313+
let passkeyConfig;
314+
try {
315+
passkeyConfig = buildPasskeyConfig(config.passkeys);
316+
} catch (err) {
317+
log.error('startup.passkey.configInvalid', { err });
318+
process.exit(8);
319+
}
314320
const passkeyManager = new PasskeyManager(
315321
accountDatabase,
316322
passkeyConfig,

0 commit comments

Comments
 (0)