Hey team! <@U0AJQMED3FY> and I are trying to find ...
# ask-a-descoper
f
Hey team! @Steffen Hanikel and I are trying to find a correct approach for handling step ups with custom claims with Descope. Afaik step ups update the session token claims automatically. Does it mean that when a user opens up few tabs with step up flows for different actions and completes them - is the user getting a session token with all of these custom claims combined? How do we make sure that our fine grained auth approach with step-up required for specific actions (for example X EUR transaction to Y Iban) is secure and can be used only for one specific action and the claims are not tampered?
✅ 1
d
Hi, I'm Descope's AI assistant. I've put together an initial response below based on your question. A member of the support team will take over and follow up shortly. How Step-Up Claims Work When you complete step-up authentication, Descope adds the
su: true
claim to your session JWT. If you're adding custom claims during step-up flows, these get added to the token as well. However, multiple concurrent step-up flows do not automatically merge their custom claims; each successful step-up updates the session token. Securing Fine-Grained Actions For your specific use case (X EUR transaction to Y IBAN), consider this approach: 1. Action-Specific Custom Claims Use custom claims that are specific to the intended action:
Copy code
// During step-up flow for specific transaction
const loginOptions = {
  stepup: true,
  customClaims: {
    "approved_transaction_id": "txn_12345",
    "approved_amount": "1000_EUR", 
    "approved_iban": "DE89370400440532013000",
    "expires_at": "2024-01-01T12:00:00Z"
  }
}
2. Backend Validation Your backend should: • Verify the
su: true
claim exists • Check that custom claims match the exact transaction being attempted • Validate claims haven't expired • Consume/invalidate claims after use 3. Secure vs Non-Secure Claims Custom claims added through Flows are marked as secure (not in the
nsec
claim), meaning they're trusted by your backend. Claims added by client SDKs would be in the
nsec
claim and shouldn't be trusted for authorization. Best Practices • Set short expiration times for transaction-specific claims • Include unique transaction identifiers in claims • Validate all claim details match the attempted action exactly • Consider using single-use tokens for high-value transactions The key is treating step-up custom claims as temporary, action-specific permissions rather than persistent authorizations.
s
Specifically, can we use the react SDK to start a flow to get a custom and very narrow token to use for a specific request only and not override the global session token
a
Hi @Filip Kajetaniak On concurrent tabs + claim merging: Step-up flows don't merge claims — each completed step-up overwrites the session token. So if a user completes step-up A in tab 1 and then step-up B in tab 2, the final session token will only carry B's claims. This is exactly the race condition you're worried about, and it's a real concern for fine-grained auth. On getting a narrow token that doesn't override the global session: There's no native "isolated step-up token" that bypasses the global session update — step-up always updates the session token. But here's the pattern that works securely for your use case: When a step-up flow completes, capture the returned session token at that moment and pass it directly to the backend API for that specific action, instead of relying on whatever the globally stored session token is at call time. This way, each tab holds its own step-up token locally and uses it for its own action — the global session being overwritten doesn't break anything. For securing the claims themselves (e.g., 1000 EUR → DE89... IBAN): Embed the transaction specifics as custom claims via the Flow (not the client SDK — those land in
nsec
and shouldn't be trusted for authz):
Copy code
{
  "approved_transaction_id": "txn_abc123",
  "approved_amount_eur": 1000,
  "approved_iban": "DE89370400440532013000"
}
Your backend then validates that these claims exactly match the action being requested. Even if the session token gets overwritten, the action-specific token you captured locally still has the right claims — and the backend rejects anything that doesn't match exactly.
👍 1