Backend guides
Step-up re-authentication
Let passwordless OAuth users confirm sensitive actions with a passkey or a fresh OAuth round-trip instead of a password.
Some actions are sensitive enough to re-check who is at the keyboard: deleting an account, changing the email, disabling two-factor, or setting a password. Slicekit asks the user to re-authenticate for these, and accepts the result in place of the normal password prompt. Users who signed up with a password just type it. Users who signed in with an OAuth provider have no password, so they re-authenticate with a passkey or a fresh OAuth round-trip instead.
The proof
A re-authentication produces a short-lived, single-use proof: a token stored in the cache for five minutes, bound to the account’s Identity security stamp. Password changes, recovery, confirmed email changes and admin second-factor resets invalidate earlier proofs and pending MFA challenges. Issuance and consumption check the live account under the same lock used by those changes.
Re-auth endpoints
All are authenticated, CSRF-protected and rate-limited, and they return { reauthToken }.
POST /auth/reauth/passkey/optionsandPOST /auth/reauth/passkeyrun a WebAuthn assertion for the current user (the same machinery as the passkey 2FA flow) and mint a proof.GET /auth/reauth/{provider}/startandPOST /auth/reauth/{provider}/callbackrun an OAuth round-trip, verify the returned identity is already linked to the current user, and mint a proof. They reuse the login redirect URI, with their own cache namespace and cookie so a re-auth can never be mistaken for a login.
OAuth link and re-authentication flows bind the initiating account, provider, purpose and credential generation. The final transaction checks the account again after provider network requests. Switching accounts or changing credentials while a flow is pending requires starting over.
Password or proof
The sensitive operations (DELETE /users/me, POST /users/me/request-email-change, DELETE /users/me/totp, and
POST /users/me/change-password) accept an optional re-auth token next to the password field. Each handler applies
one rule: if the user has a password, verify the password; if not, verify and consume the proof. Change-password
doubles as set a password for passwordless users, so an OAuth-only account can add password sign-in after proving
itself. GET /users/me exposes a hasPassword flag so the UI knows which path to show.
On the frontend
The reauth feature slice provides a StepUpVerify component that offers “Verify with passkey” (inline) and
“Continue with <provider>”. The OAuth option is a full-page redirect in the same tab (not a popup, which providers
break with their Cross-Origin-Opener-Policy): the pending operation is stashed in sessionStorage, the OAuth
callback completes the re-auth, and the originating form resumes on return. Password-based forms are unchanged; the
step-up UI appears only when hasPassword is false.