Sthora
Teams and access

Passkeys

Registering a WebAuthn credential, and signing in with no password and no code.

A passkey is a key pair held by your device or security key and unlocked by your fingerprint, face, or PIN. The private half never leaves the authenticator, so there is nothing in this platform's database that could sign in as you — a passkey survives a leak that a password hash only resists.

It is also the one credential that cannot be phished: the browser will only present a passkey to the exact origin it was registered for, so a convincing look-alike domain gets nothing.

A passkey replaces both steps

Signing in with a passkey needs no password and no two-factor code. The ceremony requires user verification, so the assertion already proves you hold the device and unlocked it — that is two factors, in one gesture.

Registering one

Start registrationPOST /auth/passkeys. It re-authenticates with your password first: an unlocked laptop must not be able to add a credential that signs in on its own.

Follow the browser's prompt. Your device creates the key pair and signs the challenge. Nothing is written to the database at this point, so cancelling here leaves no trace.

ConfirmPOST /auth/passkeys/confirm. The server verifies the attestation and stores the public half, the credential id, and the name you gave the device.

Register one per device you actually sign in from. A passkey marked Synced is backed up to your platform account (iCloud Keychain, Google Password Manager, 1Password) and survives losing the device; one marked This device only — most hardware security keys — does not.

Signing in

POST /auth/passkeys/challenge issues a challenge, and POST /auth/passkeys/verify takes the assertion and issues the session. Both are public.

You never type an email. The credential is discoverable, which means the authenticator knows which account it belongs to and tells the server. That is also why this route reveals nothing: there is no address in the request for anyone to test.

Configuration

Both variables default to values derived from PUBLIC_BASE_URL, so an install whose portal is reachable by name needs neither.

VariableMeans
AUTH_PASSKEY_RP_IDThe bare domain a passkey is bound to. No scheme, no port.
AUTH_PASSKEY_ORIGINSComma-separated full origins a ceremony may run from, checked in the signature.

The relying-party id is not a setting you can change later

A passkey registered under one AUTH_PASSKEY_RP_ID is unusable under any other. Changing it invalidates every credential already enrolled. If the portal might ever move between subdomains, set it to the registrable parent domain before the first passkey is registered.

AUTH_PASSKEY_ORIGINS is not AUTH_TRUSTED_ORIGINS. That one decides which origins the API will act on; this one is checked inside the signed authenticator data and is what makes the credential phishing-resistant. Do not collapse them.

What is stored

ColumnContents
publicKeyThe public half of the key pair. Useless to an attacker.
credentialIDA public handle, unique across the install.
counterSignature counter, recorded for cloned-authenticator detection.
deviceType, backedUpWhether the credential is synced or lives on one device.

No secret appears in this table. The counter is recorded rather than enforced: a synced passkey reports zero forever, and refusing one on that basis would lock out exactly the credentials that are working correctly.

If you lose every passkey

Sign in with your password — passkeys are added alongside it, not instead of it — and remove the lost credential from account settings. If the account also has two-factor authentication on, that path still applies.

On this page