prld:pwd:write session scope — the user acquires the scope by completing a short verification challenge (SMS or email OTP), then calls the reset password endpoint.
This guide shows how to wire that flow using a direct step-up scope configuration: you declare the challenge inline on the scope entry and Prelude serves it without calling any backend hook.
How it works
Because the step-up response is configured inline on the scope entry (mode: "direct"), there is no need to run a delegation hook: no delegation_hook is involved and jwks_url can be left empty as long as every allowed_scopes entry uses direct mode.
Prerequisites
- A Prelude account with access to the Session API
- An Application ID (
appID) — see Applications - Your Management API key for backend calls
- Password login configured on the application — see Password Authentication
- Users with an
email_addressorphone_numberidentifier (used for the OTP step)
Configure the step-up flow
Register the prld:pwd:write scope
Add
prld:pwd:write to the application’s allowed scopes so it can be requested from the frontend:Create a direct step-up configuration
Create a step-up configuration that resolves
You can list multiple identifier types in a single entry when the same decision applies to all of them (e.g. a custom step that is identifier-agnostic). Here we need two entries because the step differs:
prld:pwd:write directly. The jwks_url can stay empty because no delegated scope is configured.Because the step differs depending on the user’s identifier (verify_email for email, verify_sms for phone), we register two allowed_scopes entries for the same scope, each scoped to a different identifier type. The first entry that matches one of the user’s identifiers is used.| Field | Description |
|---|---|
allowed_scopes[].scope | Scope this entry resolves. |
allowed_scopes[].mode | direct to serve a static decision, delegated to call a delegation hook. |
allowed_scopes[].direct.identifier_types | Identifier types the user must hold for this entry to match. Valid values: email_address, phone_number. |
allowed_scopes[].direct.status / grant_mode / granted_for / steps | The decision fields, flattened. Same shape as the step-up hook response, inline on the scope entry. |
Entries are matched in declaration order. The first
direct entry whose scope matches and whose identifier_types overlaps with the user’s identifiers wins. Put the preferred challenge at the top.verify_email for email users, verify_sms for phone users. If you only support one identifier type, provide a single entry and list just that type.If no
direct entry’s identifier_types match the user, Prelude falls back to a delegated entry for the same scope when one exists. Add one — pointing at your delegation hook — if you want to keep the flow alive for users whose identifier type isn’t covered by any direct entry.Relationship with OTP login grant_change_password
The step-up path described above is for users who are already logged in and want to change their password from the account area. It is complementary to the grant_change_password flag on OTP login configurations, which covers a different entry point:
| Entry point | Mechanism | When to use |
|---|---|---|
| Unlogged reset / account creation | OTP login with grant_change_password: true attaches prld:pwd:write to the session at login time | The user proves possession of the identifier via the login OTP itself, so requiring another step-up right after would be redundant |
| Logged-in reset | Static step-up on prld:pwd:write | The user is already authenticated and we want a fresh proof of possession before changing the password |
Trigger the flow from the frontend
Once the configuration above is in place, wire the flow into your frontend. See the Web SDK Password Reset guide for the full code example and a runnable Try it sandbox.What happens server-side
POST /v1/session/stepup/requestwithscope=prld:pwd:writelooks up the app’s step-up configuration.- Because
prld:pwd:writehas direct entries, Prelude picks the first entry whoseidentifier_typesmatches the user and returns its inline decision — no hook is invoked. - The user completes the OTP step. On completion Prelude mints a step-up token which the SDK uses to refresh the session; the new access token carries
prld:pwd:write. POST /v1/session/me/password/resetvalidates the scope, writes the new password, and atomically removes the scope from the session so it cannot be reused.
mode from direct to delegated and point it at a delegation hook. Direct and delegated scopes can coexist in the same configuration.
Constraints
| Rule | Limit |
|---|---|
jwks_url | Required when at least one allowed_scopes entry uses delegated mode. Can be empty when every entry uses direct mode. |
direct.identifier_types | Must not be empty. Allowed values: email_address, phone_number. |
(scope, identifier_type) pairs (direct) | Must be unique across all direct entries. |
| Delegated entries per scope | At most one. It is used as a fallback when no direct entry matches. |
Response granted_for | 0 to 86400 seconds. For a password reset, keep it short (60–300 seconds is typical). |
Response grant_mode | Use single-use so the scope is attached to a single access token only. |
What’s next?
Step-Up Authentication
Full overview of step-up, including dynamic decisions via the delegation hook.
Step-Up Hook
Response shape used by delegated scopes. The same fields are inlined on direct scope entries.