Guides and TutorialsMFA and Step-Up

Step-up Authentication

Step-up authentication is a security mechanism that allows you to add additional authentication requirements for sensitive operations in your application. It works by requiring users to re-authenticate when accessing high-risk features, even if they're already logged in.

How Step-up Authentication Works

When a user needs to perform a sensitive operation (like making a purchase or accessing personal data), you can trigger step-up authentication. This requires the user to verify their identity again, typically using a stronger authentication method than their initial login.

After successful step-up authentication, the user's session token is updated with a su (step-up) claim set to true. This allows your application to verify that the user has completed the additional authentication step. This stepped-up token will be valid for the duration of the Step Up Token Timeout defined in Project Settings.

{
  "amr": ["xxxx"],
  "drn": "xx",
  "exp": xxxxx,
  "iat": xxxxx,
  "iss": "xxxxx",
  "rexp": "xxxxx",
  "su": true,
  "sub": "xxxxx"
}

Use Cases

Step-up authentication is particularly useful for:

  • Financial transactions (e.g., making purchases, transferring money)
  • Accessing sensitive personal information
  • Administrative operations
  • Changing account settings
  • Any high-risk operation that requires additional verification

Implementation Options

You can implement step-up authentication using our Flows, Client SDKs, or Backend SDKs.

Option 1: Using Descope Flows

Descope provides a pre-built step-up flow that you can easily integrate into your application. This flow:

  1. Loads the user from their refresh token
  2. Marks the flow as a step-up authentication
  3. Presents authentication options (magic link, passkeys, social login)
  4. Updates the session token upon successful authentication

Default step-up flow

You can integrate a step-up flow in your application in the same way you would integrate a regular authentication flow. After the step-up flow succeeds, the user's JWT will be updated with the su claim.

The authentication options in a step-up flow aren't limited to native Descope methods. If a user is authenticated through a homegrown or third-party IdP instead, a Generate JWT action placed after the Step Up action also issues a token with the su claim. See Step-Up Authentication for Third-Party IdP Sign-Ins for details.

Option 2: Using Client SDKs

To perform step-up authentication using our Client SDKs, you use the same "Sign In" or "Sign Up Or In" functions as you would for regular authentication. You just have to specify that you are performing step-up using the Login Options parameter.

The below example implements step-up authentication via OTP Sign-In after the user has already authenticated using another authentication method. In loginOptions, stepup is set to true, indicating that this is a step-up authentication action. On success of the sign in function, the user's JWT will include the su claim.

// Args:
//    loginId: email or phone - becomes the loginId for the user from here on and also used for delivery
const loginId = "email@company.com"
//    deliveryMethod: Delivery method to use to send OTP. Supported values include "email", "voice, or "sms"
const deliveryMethod = "email"
//    loginOptions: login options for MFA, stepup, or custom claims. Ex: {stepup: true, mfa: false, customClaims: {}}
const loginOptions = {stepup: true}
//    token: refresh token from the successful sign-in of the user
const token = "xxxxxx"

const resp = await descopeClient.otp.signIn[deliveryMethod](loginId, loginOptions, token);
if (!resp.ok) {
  console.log("Failed to initialize step-up flow")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully initialized step-up flow")
}

Option 3: Using Backend SDKs

To perform step-up authentication using our Backend SDKs, you use the same "Sign In" or "Sign Up Or In" functions as you would for regular authentication. You just have to specify that you are performing step-up using the Login Options parameter.

The below example implements step-up authentication via OTP Sign In after the user has already authenticated using another authentication method. In loginOptions, stepup is set to true, indicating that this is a step-up authentication action. On success of the sign in function, the user's JWT will include the su claim.

// Args:
//    deliveryMethod: Delivery method to use to send OTP. Supported values include "email", "voice, or "sms"
const deliveryMethod = "email"
//    loginId: email or phone - email or phone - the loginId for the user
const loginId = "email@company.com"
//    loginOptions: login options for MFA, stepup, or custom claims. Ex: {stepup: true, mfa: false, customClaims: {}}
const loginOptions = {stepup: true}
//    token: refresh token from the successful sign-in of the user
const token = "xxxx"

var resp =  await descopeClient.otp.signIn[delivery_method](loginId, loginOptions, token);
if (!resp.ok) {
  console.log("Failed to initialize step-up flow")
  console.log("Status Code: " + resp.code)
  console.log("Error Code: " + resp.error.errorCode)
  console.log("Error Description: " + resp.error.errorDescription)
  console.log("Error Message: " + resp.error.errorMessage)
}
else {
  console.log("Successfully initialized step-up flow")
  console.log(resp.data)
}

Skipping Step-Up When the User Already Stepped Up

When a user completes several sensitive actions in a row, each flow can check whether the user already stepped up instead of asking for step-up again.

The su claim is on the user's session token, and stays valid until the Step Up Token Timeout set in your project settings. A flow reads the session token's claims through the sessionJwtClaims dynamic key, as long as the session token is sent to the flow. To skip step-up for a user who already stepped up:

  1. Add a condition on sessionJwtClaims.su at the start of the flow.
  2. If sessionJwtClaims.su is true, continue to the sensitive action.
  3. Otherwise, continue to the step-up steps.

The condition checks whether sessionJwtClaims.su is true:

Condition that checks whether sessionJwtClaims.su is true, with Already stepped up and Needs step-up outcomes

In the following example flow, a user who already stepped up goes straight to the end of the flow. A user who has not stepped up goes through the step-up steps: the Add login options to flow action with Step up selected, the step-up screen, and magic link authentication.

Step-up flow that skips the step-up steps when sessionJwtClaims.su is true

Descope validates the session token, including the signature, expiration, and logout status, before the claims are available in sessionJwtClaims. If the session token is missing or invalid, sessionJwtClaims is empty and the flow continues to the step-up steps.

Sending the Session Token to the Flow

How the session token reaches the flow depends on where your application stores the session token:

  • Session token in a cookie: The session cookie is sent with flow requests automatically, including a session cookie with a custom name. No extra configuration is needed.
  • Session token not in a cookie: Set sendSessionToken on the Descope flow component, or the send-session-token attribute on the web component. The option is off by default.
<Descope
    flowId="step-up"
    sendSessionToken={true}
/>

Minimum SDK Version

sendSessionToken requires at least the following SDK versions:

SDKMinimum version
@descope/web-component4.3.0
@descope/react-sdk3.2.0
@descope/angular-sdk0.28.0
@descope/vue-sdk3.1.0
@descope/nextjs-sdk0.15.69

Native Mobile Apps

The iOS, Android, and React Native SDKs pass the current session token to the flow page. The web component on the flow page must have the send-session-token attribute set.

Minimum SDK Version

Passing the session token to the flow page requires at least the following SDK versions:

SDKMinimum version
DescopeKit (iOS)0.11.2
com.descope:descope-kotlin0.21.0
@descope/react-native-sdk0.13.0

Step-Up Validation

To validate that the session was successfully stepped up, you can utilize the Backend SDKs to validate the session and check that the su claim is true.

// Args:
//   sessionToken (str): The session token, which contains the signature that will be validated
const sessionToken="xxxx"

try {
  const authInfo = await descopeClient.validateSession(sessionToken);
  console.log("Successfully validated user session:");
  console.log(authInfo);
  if ("su" in authInfo.token) {
    console.log("Session is stepped up.");
  }
} catch (error) {
  console.log ("Could not validate user session " + error);
}

Resources

The B2C Retail Sample App, Peek-a-Box demonstrates a practical implementation of step-up authentication. Users can browse products and add items to their cart with basic authentication. When the user proceeds to checkout, step-up authentication is required.

Check out our learning center article for more examples, use cases, and guidelines on implementing step-up authentication.

Was this helpful?

On this page