Skip to content

Commit 3cf7ee1

Browse files
authored
fix(core): add display name to webauthn registration options (#7439)
1 parent f265ea9 commit 3cf7ee1

File tree

4 files changed

+19
-3
lines changed

4 files changed

+19
-3
lines changed

.changeset/pink-rules-compare.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@logto/integration-tests': patch
3+
'@logto/core': patch
4+
---
5+
6+
fix potential WebAuthn registration errors by specifying the displayName
7+
8+
This is an optional field, but it's actually required by some browsers. For example, when using Chrome on Windows 11 with the "Use other devices" option (scanning QR code), an empty displayName will cause the registration to fail.

packages/core/src/routes/interaction/additional.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export default function additionalRoutes<T extends IRouterParamContext>(
237237
user: {
238238
id: newAccountId,
239239
username: newUserProfile.username ?? newAccountId,
240+
name: newUserProfile.name ?? null,
240241
primaryEmail: newUserProfile.primaryEmail ?? null,
241242
primaryPhone: newUserProfile.primaryPhone ?? null,
242243
mfaVerifications: [],
@@ -260,12 +261,13 @@ export default function additionalRoutes<T extends IRouterParamContext>(
260261

261262
if (isSignInInteractionResult(profileVerifiedInteraction)) {
262263
const { accountId } = profileVerifiedInteraction;
263-
const { id, username, primaryEmail, primaryPhone, mfaVerifications } =
264+
const { id, name, username, primaryEmail, primaryPhone, mfaVerifications } =
264265
await findUserById(accountId);
265266
const options = await generateWebAuthnRegistrationOptions({
266267
rpId: ctx.URL.hostname,
267268
user: {
268269
id,
270+
name,
269271
username,
270272
primaryEmail,
271273
primaryPhone,

packages/core/src/routes/interaction/utils/webauthn.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,25 @@ import RequestError from '#src/errors/RequestError/index.js';
2525

2626
type GenerateWebAuthnRegistrationOptionsParameters = {
2727
rpId: string;
28-
user: Pick<User, 'id' | 'username' | 'primaryEmail' | 'primaryPhone' | 'mfaVerifications'>;
28+
user: Pick<
29+
User,
30+
'id' | 'name' | 'username' | 'primaryEmail' | 'primaryPhone' | 'mfaVerifications'
31+
>;
2932
};
3033

3134
export const generateWebAuthnRegistrationOptions = async ({
3235
rpId,
3336
user,
3437
}: GenerateWebAuthnRegistrationOptionsParameters): Promise<WebAuthnRegistrationOptions> => {
35-
const { username, primaryEmail, primaryPhone, id, mfaVerifications } = user;
38+
const { username, name, primaryEmail, primaryPhone, id, mfaVerifications } = user;
3639

3740
const options: GenerateRegistrationOptionsOpts = {
3841
rpName: rpId,
3942
rpID: rpId,
4043
userID: Uint8Array.from(Buffer.from(id)),
4144
userName: getUserDisplayName({ username, primaryEmail, primaryPhone }) ?? 'Unnamed User',
45+
userDisplayName:
46+
getUserDisplayName({ name, username, primaryEmail, primaryPhone }) ?? 'Unnamed User',
4247
timeout: 60_000,
4348
attestationType: 'none',
4449
excludeCredentials: mfaVerifications

packages/integration-tests/src/tests/api/account/mfa.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('my-account (mfa)', () => {
4444

4545
expect(verificationRecordId).toBeTruthy();
4646
expect(registrationOptions.rp.name).toBe('localhost');
47+
expect(registrationOptions.user.displayName).toBe(user.username);
4748

4849
await deleteDefaultTenantUser(user.id);
4950
});

0 commit comments

Comments
 (0)