Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-firefox-passkeys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/ui': patch
---

Prevent passkey autofill from displaying an RP ID or domain error when a browser rejects a background credential request.
28 changes: 27 additions & 1 deletion packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClerkAPIResponseError } from '@clerk/shared/error';
import { ClerkAPIResponseError, ClerkWebAuthnError } from '@clerk/shared/error';
import { CAPTCHA_ELEMENT_ID } from '@clerk/shared/internal/clerk-js/constants';
import { OAUTH_PROVIDERS } from '@clerk/shared/oauth';
import type { SignInResource } from '@clerk/shared/types';
Expand Down Expand Up @@ -154,6 +154,32 @@ describe('SignInStart', () => {
});
});

it('does not display related-origin errors from passkey autofill', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withEmailAddress();
f.withPasskey();
f.withPasskeySettings({
allow_autofill: true,
show_sign_in_button: true,
});
});

fixtures.signIn.authenticateWithPasskey.mockRejectedValue(
new ClerkWebAuthnError('The operation is insecure.', {
code: 'passkey_invalid_rpID_or_domain',
}),
);
render(<SignInStart />, { wrapper });

await waitFor(() => {
expect(fixtures.signIn.authenticateWithPasskey).toHaveBeenCalledWith({
flow: 'autofill',
});
});
expect(screen.queryByText(/operation is insecure/i)).not.toBeInTheDocument();
screen.getByText('Use passkey instead');
});

it('skips autofill when the host reports no autofill support', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withEmailAddress();
Expand Down
7 changes: 5 additions & 2 deletions packages/ui/src/components/SignIn/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,11 @@ function useHandleAuthenticateWithPasskey(
if (err.code === 'passkey_operation_aborted') {
return;
}
// In case of autofill, if retrieval of credentials is cancelled by the user avoid showing errors as it results to pour UX.
if (flow === 'autofill' && err.code === 'passkey_retrieval_cancelled') {
// Autofill runs in the background, so browser rejections must not surface as form errors.
if (
flow === 'autofill' &&
(err.code === 'passkey_retrieval_cancelled' || err.code === 'passkey_invalid_rpID_or_domain')
) {
return;
}
}
Expand Down
Loading