feat(auth): forward options.mediation to navigator.credentials.get in signInWithPasskey - #2675
feat(auth): forward options.mediation to navigator.credentials.get in signInWithPasskey#2675itsybitsci wants to merge 2 commits into
Conversation
… signInWithPasskey Add an optional `mediation` field to the signInWithPasskey options and pass it through to navigator.credentials.get(), so apps can opt into WebAuthn Conditional UI (passkey autofill) with `mediation: 'conditional'`. The option is omitted by default, so existing callers keep the modal picker. Closes supabase#2672
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (2)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 SummarySummary by CodeRabbit
WalkthroughThe passkey sign-in API now accepts an optional Sequence Diagram(s)sequenceDiagram
participant App
participant GoTrueClient
participant AuthAPI
participant WebAuthn
App->>GoTrueClient: signInWithPasskey({ options: { mediation } })
GoTrueClient->>AuthAPI: request passkey options
GoTrueClient->>WebAuthn: navigator.credentials.get({ publicKey, signal, mediation })
WebAuthn-->>GoTrueClient: passkey credential
GoTrueClient->>AuthAPI: verify passkey credential
AuthAPI-->>GoTrueClient: session response
Assessment against linked issues
Priority: ⬇️ Low — Defer the optional WebAuthn mediation change because it is a focused auth API enhancement that preserves existing sign-in behavior and has no stated urgent customer or incident impact. Merge Risk: ⚪ Minimal · up to Passkey sign-in now supports optional WebAuthn mediation while preserving the default picker behavior. Conditional mediation forwarding and retry cancellation are covered, with no active merge-blocking risk identified. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
mandarini
left a comment
There was a problem hiding this comment.
Hi @itsybitsci, thank you so much for contributing to Supabase! 💚
The passthrough itself looks correct and nicely minimal. One thing to sort out before merging: the authentication challenge from _startPasskeyAuthentication expires after 5 minutes by default, but conditional UI is designed to sit pending indefinitely until the user picks a passkey from autofill. If they take longer than that, the browser ceremony succeeds but verify will fail with an expired-challenge error.
Could you add a line to the TSDoc calling this out, and let us know your take on the retry path, should the app call signInWithPasskey again on that error, and does the browser handle a second conditional getCredential call while one may still be pending?
Thank you again for contributing, this is a genuinely useful addition.
…ign-in The authentication challenge expires server-side (5 minutes by default) while a conditional UI prompt can stay pending indefinitely, so a late pick fails verification with webauthn_challenge_expired. Document this on signInWithPasskey and the mediation option, explain that calling signInWithPasskey again cancels the pending ceremony and starts a fresh challenge, add the error code to the ErrorCode union, and cover the retry path with a test.
Thanks for the review @mandarini , and good catch on the expiry. Pushed a follow-up:
On the retry path: calling Added a test that runs exactly that sequence: a pending conditional |
@supabase/auth-js
@supabase/functions-js
@supabase/postgrest-js
@supabase/realtime-js
@supabase/storage-js
@supabase/supabase-js
commit: |
🔍 Description
Lets
signInWithPasskey()opt into WebAuthn Conditional UI (passkey autofill) by forwarding amediationoption tonavigator.credentials.get().What changed?
SignInWithPasskeyCredentials.optionsgainsmediation?: CredentialMediationRequirement(the lib.dom type, so no new imports).GoTrueClient.signInWithPasskeypassescredentials?.options?.mediationthrough togetCredential, which already accepts everyCredentialRequestOptionsfield and hands them tonavigator.credentials.get()unchanged. Nothing else in the ceremony moves.signInWithPasskey.passkey.methods.test.ts: one assertingmediation: 'conditional'reachesnavigator.credentials.getand the ceremony still completes, and an assertion in the existing full-ceremony test thatmediationstays undefined when not requested.Why was this change needed?
Today
signInWithPasskeyonly readscaptchaTokenandsignal, so the browser's modal picker is the only path. Conditional UI lets the browser surface passkeys in the autofill prompt on field focus or page load, which removes a tap and, on iOS Safari, shows the native passkey suggestion above the keyboard. The only missing piece in the SDK was themediationpass-through; theautocomplete="username webauthn"input is app-side.Closes #2672
📸 Screenshots/Examples
🔄 Breaking changes
The option is optional and omitted by default, so existing callers keep the modal behavior. The passkey API is still behind
auth.experimental.passkey.📋 Checklist
<type>(<scope>): <description>pnpm nx formatto ensure consistent code formatting📝 Additional notes
Deliberately a plain pass-through. The issue also floats a guard that drops
mediationwhenPublicKeyCredential.isConditionalMediationAvailable()reports no support; I left that out because silently ignoring an explicit option is debatable and browsers already handle unsupported values themselves. Happy to add it if preferred.registerPasskeyis untouched sinceCredentialCreationOptionshas nomediationin lib.dom.