Home

Documentation

Documentation

Step 2: Assertion → Refresh Token

Exchange the SAML assertion from Step 1 for a Refresh Token at the IdP, using RFC 8693 Token Exchange. This is the first of the two exchanges defined in §4.5 (TE ①).

Why a Refresh Token first?

A SAML assertion proves a single sign-on event and is short-lived by design. Rather than replay it, you trade it once for a durable Refresh Token that represents the user's authenticated session. Step 3 then mints ID-JAGs from that Refresh Token as needed.

Request parameters

ParameterRequiredValue
grant_typeYesurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenYesThe bare <saml:Assertion> from Step 1, base64url-encoded
subject_token_typeYesurn:ietf:params:oauth:token-type:saml2
requested_token_typeYesurn:ietf:params:oauth:token-type:refresh_token
scopeYesMust include openid offline_access email plus your resource scopes
client_idYesYour IdP client ID
client_secretYesYour IdP client secret
subject_token is the bare Assertion, not the Response

Send only the <saml:Assertion> element (not the enclosing <samlp:Response>), base64url-encoded (RFC 7521 §3, RFC 8693 §3). IdenX signs the assertion individually in Step 1, so the bare element is self-verifiable.

No audience or resource yet

Unlike the OIDC token exchange, this call does not take audience or resource. Those belong to Step 3, when you target a specific Authorization Server and API.

Implementation

What the IdP validates

Before issuing the Refresh Token, IdenX verifies the assertion:

  • XML-DSig: the assertion's individual signature, against the IdP's own SAML certificate
  • Issuer: the assertion <saml:Issuer> matches IdenX's entity ID
  • Conditions: NotBefore / NotOnOrAfter timestamps are currently valid
  • NameID: extracts the NameID and its qualifiers, and stores them alongside the Refresh Token so Step 3 can put them in the ID-JAG's sub_id

Response

JSON
FieldDescription
access_tokenThe opaque Refresh Token. Per RFC 8693 §2.2, the issued token is always returned in access_token, whatever its type.
issued_token_typeConfirms this is a refresh_token
token_typeN_A (this token is not used as a bearer token directly)
expires_inRefresh Token lifetime in seconds (90 days)
scopeGranted scopes, carried into the ID-JAG in Step 3
Store this, not the assertion

The Refresh Token, not the SAML assertion, is the durable handle for the user's session. Keep it server-side; it's the input to every Step 3 exchange.

Error handling

ErrorCauseFix
invalid_grantAssertion signature invalid, expired, or Issuer mismatchRe-run Step 1 to get a fresh assertion; confirm you sent the bare, base64url-encoded <saml:Assertion>
unsupported_token_typerequested_token_type is not refresh_token for a saml2 subjectFor a saml2 subject token, the only valid requested_token_type is refresh_token
invalid_clientIdP client credentials wrongUse the Client ID / Secret at IdP from registration, in the POST body

Next step

With the Refresh Token, proceed to Step 3: Refresh Token → ID-JAG.

These materials and any recommendations within are not legal, privacy, security, compliance, or business advice. These materials are intended for general informational purposes only and may not reflect the most current security, privacy, and legal developments nor all relevant issues. You are responsible for obtaining legal, security, privacy, compliance, or business advice from your own lawyer or other professional advisor and should not rely on the recommendations herein.

Presented byOkta Developer

Copyright © 2026 Okta. All rights reserved.