Skip to content
Slicekit

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.

View .md
On this page

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/options and POST /auth/reauth/passkey run a WebAuthn assertion for the current user (the same machinery as the passkey 2FA flow) and mint a proof.
  • GET /auth/reauth/{provider}/start and POST /auth/reauth/{provider}/callback run 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.

Live demo

Explore the running app

No sign-up. Pick an account to open the demo with its credentials prefilled, or open it and sign in yourself.

Open without signing in

Credentials are prefilled. Shared password Slicekit-Demo-2026!.