Login Experience
The hosted login screen is not a fixed form. What it shows — which methods, in what order, whether passkeys are on, whether users are invited to create one — is configuration on your auth account, and any client can override it for its own login screen.
Everything here lives under Login Experience in the dashboard, and under login_methods on the Account and Client objects of the Management API.
What the login screen shows
The screen offers a method when three things are true: the connection is enabled, it is allowed for the client that started the login (set per connection under Enabled clients), and it has not been removed from this login experience.
The order is yours. Drag methods in the dashboard, or set login_methods.order to a list of connection ids (and the literal passkey). Methods you do not list keep the built-in order after the ones you do: passwordless first, then password, then social.
Removing a method here is enforced, not cosmetic. A connection taken off a client’s login screen is also refused at /authorize?connection=<id> for that client — the screen and the server always agree about what a client may use.
Sign in with a passkey
A passkey verified with a biometric or a device PIN proves possession and knowledge in one gesture, which is why a passkey login is never asked for a second factor afterwards. Turn it on under Login Experience → Passkeys.
Plan requirement: turning on passkeys as a sign-in method is available on the Pro plan. A passkey as a second factor is part of two-step verification, available from Hobby. See Auth pricing.
Two things happen on the login screen. A Continue with a passkey button appears under the password form, behind an or sign in with rule, next to the social buttons (you can move it with the method order). And, in browsers that support it, the passkey is also offered from inside the email field — returning users pick it from the autofill suggestions and never touch the button.
The button names what the passkey is on that device — Touch ID on a Mac, Face ID on an iPhone, Windows Hello on Windows — with the matching icon, because that is the word the user recognises. The screen asks the browser whether a platform authenticator is present; a machine without one gets the generic label. Underneath it is the same WebAuthn ceremony either way. The same naming appears on the second-factor screen and on the passkey offer.
Registration runs entirely on your auth domain: a passkey is bound to the origin that created it, so the ceremony cannot be moved into your own application. Your app only ever sees the resulting session.
Inviting users to create a passkey
Turning passkey sign-in on does not, on its own, get anyone a passkey. Left to a settings page, enrolment stays in the single digits; most passkeys people actually have were created from a prompt shown right after a login, at the moment the user has just typed a password and can see why they would rather not.
That prompt is the passkey offer. With it on, a user who signs in with a password, an email code or a social login — and has no passkey or authenticator app yet — is taken to a hosted screen before returning to your application:
It never blocks the login. The user can say Not now; a browser that cannot create passkeys skips the screen without showing it; a failed registration offers to continue without one; and a user who simply closes the tab is signed in normally on their next visit. The login is complete before the screen appears — the offer is a question, not a gate.
Turn it on under Login Experience → Passkeys, next to passkey sign-in. It needs passkey sign-in on: there is no point creating a passkey that cannot be used to sign in. Two knobs keep it from becoming a nag:
| Setting | Default | What it does |
|---|---|---|
| Ask again after | 30 days | How long to wait before offering again to a user who said Not now. |
| Maximum times asked | 3 | How many times a user is offered a passkey over their lifetime. 0 switches the offer off for this tenant. |
Repeating the prompt measurably raises abandonment. Keep it rare.
From the API
The same settings on the account, for scripts and infrastructure-as-code. login_methods is replaced as a whole on write, so send the full object:
curl -X POST https://<your-auth-domain>/account/account_xxx \
-H "Authorization: Bearer $MANAGEMENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"login_methods": {
"order": ["connection_abc", "passkey"],
"passkey_login_enabled": true,
"passkey_promotion": "offer",
"passkey_promotion_snooze_days": 30,
"passkey_promotion_max_prompts": 3
}
}'Nothing changes in your application. The same redirect_uri receives the same authorization code — a few seconds later when the user creates a passkey, immediately when they decline:
// Your callback handler is untouched. Whether the user created a passkey on
// the way here is visible in the id token's `amr` claim on their NEXT login.
const session = await auth.handleRedirectCallback()Measuring adoption
Every offer writes an audit row: user.passkey_offer.shown when the screen appears, user.passkey_offer.accepted when a passkey is created from it, user.passkey_offer.dismissed when the user declines (with the reason: dismissed, unsupported or failed). Each row carries the prompt number, so acceptance can be read per attempt under Logs or pulled through the API with FaableQL:
type IN ("user.passkey_offer.shown", "user.passkey_offer.accepted")On a user’s detail page the Passkey offer row shows how many times they were asked and when they last declined. Ask again resets it — the answer both to “why does it keep asking me” and “I want to be asked again”. Over the API: POST /user/:id/passkey-prompt/reset.
Ask for the email first
Under Login Experience → Screen, Ask for the email first turns the login into two steps: a single email field, then the methods that apply. On the second screen the email is shown, not asked again — the password form has only the password field, with a Change link back to the first step — and the emailed-code form comes pre-filled. It is the shape Stripe and Google use, and it is the natural place for the passkey suggestion to appear.
Nothing is looked up between the two steps. The address is carried on the login, not resolved to a user, so the first screen cannot be used to find out which addresses have an account.
Remember the last method
Remember the last method moves the method a returning browser used last time to the top of the screen, with a Last used tag. The hosted screen remembers it in a first-party cookie on your auth domain; whether to act on it is decided by this setting on the server, so turning it off is immediate and complete.
Remember me on this device
Offer “Remember me on this device” adds a checkbox to the password and emailed-code forms (a passkey login honours it too). Unticked, the session ends when the browser closes; ticked, it lasts the number of days you set (30 by default, up to 365). With the option off there is no box and every session lasts 30 days, as it always has.
The checkbox only shortens or extends the session cookie on your auth domain. It is separate from Remember this device under two-step verification, which decides how long a browser is excused from a second factor.
Over the API the setting is login_methods.remember_me (off or optional) and remember_me_days; the forms send remember_me: true|false when the box is shown.
Light and dark
The hosted screens come in light and dark. By default (System) they follow the visitor’s device, and a small switch in the footer lets the visitor change it — the choice is remembered on that browser, for your auth domain.
If your application’s own pages are only light or only dark, the login should match them. Under Login Experience → Theme of the login screens, pick Light or Dark to force it: the screens always use that theme and the switch is hidden, since it could not change anything. Over the API it is login_methods.theme: system (the default), light or dark.
A single login can force it too, with theme=light or theme=dark on /authorize — see Forcing light or dark. When more than one says something, the strongest wins:
themeon/authorize, for that login;- the client’s
login_methods.theme, else the account’s; - the visitor’s choice with the switch;
- the visitor’s device.
In dark, your logo is shown on a white pill, because most logos are drawn for a white background.
Per-client overrides
Every setting on this page is a tenant default that a client can override for its own login screen — the method order, whether passkeys are offered, whether users are invited to create one. Open the client under Clients → Login experience; a client that sets nothing inherits what the account says, field by field.
That is enough for the common split: the consumer app offers passwords and Google with a passkey invitation; the admin app offers only the corporate connection and requires two-step verification. For anything beyond flipping settings — different screens, conditions on who sees what — see Login Flows.
To show fewer methods for one particular login without changing the client — only the emailed code on a checkout page, say — pass login_methods to /authorize. See Showing only some methods.
Related
- Two-Step Verification — authenticator apps, security keys and passkeys as a second factor.
- Passkeys in Next.js — the end-to-end guide.
- Clients — where per-client overrides live.
Last updated on