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
| Parameter | Required | Value |
|---|---|---|
grant_type | Yes | urn:ietf:params:oauth:grant-type:token-exchange |
subject_token | Yes | The bare <saml:Assertion> from Step 1, base64url-encoded |
subject_token_type | Yes | urn:ietf:params:oauth:token-type:saml2 |
requested_token_type | Yes | urn:ietf:params:oauth:token-type:refresh_token |
scope | Yes | Must include openid offline_access email plus your resource scopes |
client_id | Yes | Your IdP client ID |
client_secret | Yes | Your IdP client secret |
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.
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/NotOnOrAftertimestamps 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
| Field | Description |
|---|---|
access_token | The opaque Refresh Token. Per RFC 8693 §2.2, the issued token is always returned in access_token, whatever its type. |
issued_token_type | Confirms this is a refresh_token |
token_type | N_A (this token is not used as a bearer token directly) |
expires_in | Refresh Token lifetime in seconds (90 days) |
scope | Granted scopes, carried into the ID-JAG in Step 3 |
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
| Error | Cause | Fix |
|---|---|---|
invalid_grant | Assertion signature invalid, expired, or Issuer mismatch | Re-run Step 1 to get a fresh assertion; confirm you sent the bare, base64url-encoded <saml:Assertion> |
unsupported_token_type | requested_token_type is not refresh_token for a saml2 subject | For a saml2 subject token, the only valid requested_token_type is refresh_token |
invalid_client | IdP client credentials wrong | Use 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.
On this page