|
| 1 | +--- |
| 2 | +title: Configure Native to Web SSO |
| 3 | +excerpt: Learn what Native to Web SSO is and how to use it |
| 4 | +layout: Guides |
| 5 | +--- |
| 6 | + |
| 7 | +<ApiLifecycle access="ea" /> |
| 8 | + |
| 9 | +Learn what Native to Web SSO is, why it matters, and how it actually connects your OpenID Connect (OIDC) apps to your web-based services. |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +#### Learning outcomes |
| 14 | + |
| 15 | +* Understand the native to web SSO flow. |
| 16 | +* Set up your allowlist for token exchange. |
| 17 | +* Manage your allowlist. |
| 18 | + |
| 19 | +#### What you need |
| 20 | + |
| 21 | +* [Okta Integrator Free Plan org](https://developer.okta.com/signup) |
| 22 | +* An OIDC app that you want to use as the origin app. Okta supports web, SPA, and native OIDC apps. |
| 23 | +* An OIDC or SAML app that you want to use as the target web app |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## Overview |
| 28 | + |
| 29 | +Native to Web SSO creates a seamless, unified authentication experience when a user transitions from an OIDC origin app (like a native app) to a web app (either OIDC or SAML). It’s a one-way trust from the origin app to the target. |
| 30 | + |
| 31 | +Native to Web SSO achieves this by exchanging a token with the `interclient_access` scope for a one-time token. This token is used to bootstrap an authentication into the target OIDC or SAML app using authentication information such as previous factor verifications from a session created when obtaining the original token. This eliminates repeated sign-in prompts and simplifies development by reducing authentication complexity. |
| 32 | + |
| 33 | +### Handle different assurance levels and remediation |
| 34 | + |
| 35 | +Sometimes the target web app has stricter security needs than the requesting app. For instance, the original app may have only required a username and password, but the target web app requires a second factor, like a one-time passcode (OTP). |
| 36 | + |
| 37 | +Okta handles that during the transition by prompting the user to satisfy the missing requirement. The user sees a single prompt for the complimentary factor (OTP), not a full authentication restart. |
| 38 | + |
| 39 | +### Common use cases |
| 40 | + |
| 41 | +* **Incorporating SaaS**: You have an app, and you want to seamlessly incorporate a third-party SaaS app into it. |
| 42 | +* **The application dashboard**: Your app acts as an "application dashboard" with links to multiple web apps. |
| 43 | +* **Modernization**: You’re going through a large modernization project. This pattern lets your legacy apps continue to operate unmodified while you update the new parts of the system. This is especially helpful when you want to move towards a modern native app and incorporate the legacy web (hybrid native/web) app. |
| 44 | +* **Integration limitations**: It’s critical when you can’t modify the target app integration to accommodate a special connection. The target source code isn’t accessible, but it already supports federation. |
| 45 | + |
| 46 | +In all these cases, the Identity and Access Management (IAM) platform becomes the secure fabric that seamlessly weaves all of your apps together into one low-friction experience. |
| 47 | + |
| 48 | +## Native to Web SSO flow |
| 49 | + |
| 50 | +<div class="three-quarter"> |
| 51 | + |
| 52 | +  |
| 53 | + |
| 54 | +</div> |
| 55 | + |
| 56 | +<!-- Generated using http://www.plantuml.com/plantuml/uml/ |
| 57 | +
|
| 58 | +@startuml |
| 59 | +skinparam monochrome true |
| 60 | +actor "User" as user |
| 61 | +participant "Origin OIDC App" as oidcapp |
| 62 | +participant "Authorization Server (Okta)" as okta |
| 63 | +participant "Target Web App" as webapp |
| 64 | +
|
| 65 | +autonumber "<b>#." |
| 66 | +user -> oidcapp: Signs in to OIDC app |
| 67 | +oidcapp -> okta: Sends direct auth request for tokens with interclient_access scope |
| 68 | +okta -> okta: Creates backend-only session |
| 69 | +okta -> oidcapp: Returns tokens |
| 70 | +user -> oidcapp: Requests to access resource from target app |
| 71 | +oidcapp -> okta: Requests access token refresh |
| 72 | +okta -> oidcapp: Sends new tokens back |
| 73 | +oidcapp -> okta: Sends request to exchange access/ID tokens for interclient token |
| 74 | +okta -> oidcapp: Validates trust relationship, user assigned to app, returns interclient token |
| 75 | +oidcapp -> webapp: Redirects to web app |
| 76 | +webapp -> okta: Receives token, sends authentication request |
| 77 | +okta -> okta: Validates, loads the state, bootstraps state token, evaluates target app policy |
| 78 | +okta -> webapp: Policy conditions are met, user is signed in |
| 79 | +@enduml |
| 80 | +
|
| 81 | +--> |
| 82 | + |
| 83 | +The flow steps: |
| 84 | + |
| 85 | +1. The user signs in to an OpenID Connect (OIDC) origin app. |
| 86 | +2. The app obtains tokens through a request to the `/token` endpoint. The request includes the `interclient_access` and `offline_access` (optional) scopes. |
| 87 | +3. Okta checks that the app is an admin-configured trusted app that’s allowed to use this flow. Then, it creates a special backend-only session. This session tracks the user’s authentication level and security assurances, such as, "This user verified their identity with a strong factor on a trusted device." |
| 88 | +4. Okta sends back the access token, the session-bound ID token, and a refresh token if `offline_access` was requested. |
| 89 | +5. The user requests access to a resource from the target web app (client app). |
| 90 | +6. The OIDC origin app now needs to launch the other app. It makes a request to [refresh the access token](/docs/guides/refresh-tokens/main/#use-a-refresh-token), if necessary. |
| 91 | +7. Okta sends back the new tokens, and the refresh token (if requested). |
| 92 | +8. The app then makes a request to the `/token` endpoint to exchange the access and ID tokens for a single-use `interclient_token`. This token is requested using the `requested_token_type` (`urn:okta:params:oauth:token-type:interclient_token`) and the Token Exchange grant type (`urn:ietf:params:oauth:grant-type:token-exchange`). |
| 93 | +9. Okta validates the trust relationship and user assignment of the target web app, and returns the single-use `interclient_token` that’s bound to the assurance of the `id_token` and target app. This single-use token is the user’s ticket to the target web app. |
| 94 | +10. The OIDC app launches the authorization server URI with intent for the target web app, securely passing the `interclient_token` to it. |
| 95 | +11. The web app receives the token and sends it to Okta in an authentication request (`/authorize` or `/sso/saml`). |
| 96 | +12. Okta validates the `interclient_token`, `audience`, target app ID, issuer, and so on. |
| 97 | + |
| 98 | + * **Look up context**: Okta looks up the associated backend session. |
| 99 | + * **Loads the state**: Okta then reconstructs the user’s state. This tells Okta who the user is and what security assurances (MFA, Device Trust, and so on) were already satisfied when they signed in to the OIDC origin app. |
| 100 | + * **Bootstraps the state token to the flow**: Rather than making the user start from a blank sign-in page, Okta uses this loaded context to bootstrap a new state token for the web app's policy evaluation. |
| 101 | + |
| 102 | +13. Okta checks if the user has satisfied all of the target web app’s policy requirements. If all checks pass, the user is immediately signed in, otherwise the user is prompted to satisfy all policy requirements. |
| 103 | + |
| 104 | + > **Note**: The user isn’t allowed to cancel and switch users. |
| 105 | +
|
| 106 | +## Configure the app |
| 107 | + |
| 108 | +The OIDC origin app exchanges access and ID tokens for a single-use interclient token from the target web app. To do this, use the Token Exchange grant type in the exchange request. |
| 109 | + |
| 110 | +To update the app that you want to request single-use interclient tokens, use the [Replace an app](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Application/#tag/Application/operation/replaceApplication!path=4/settings/oauthClient&t=request) method. |
| 111 | + |
| 112 | +In the `oauthClient` object of your PUT request, add the `urn:ietf:params:oauth:grant-type:token-exchange` value to the `grant_types` array. |
| 113 | + |
| 114 | +**Request example** |
| 115 | + |
| 116 | +This example request is truncated for brevity. |
| 117 | + |
| 118 | +```JSON |
| 119 | +{ |
| 120 | + "oauthClient": { |
| 121 | + . . . |
| 122 | + "grant_types": [ |
| 123 | + "authorization_code," |
| 124 | + "urn:ietf:params:oauth:grant-type:token-exchange" |
| 125 | + ], |
| 126 | + . . . |
| 127 | + } |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +## Configure the trust map |
| 132 | + |
| 133 | +Define a list of apps that are allowed to request the single-use interclient token on the target web app. This allowlist is a trust map between the origin app and the target web app. It ensures that the SSO flow only happens between apps that you explicitly trust. You can define up to five trusted apps per target web app. |
| 134 | + |
| 135 | +Okta checks this trusted relationship at two critical points in the flow: |
| 136 | + |
| 137 | +* **During authentication**: The OIDC origin app can only request the special `interclient_access` scope if it has a trust relationship set up. |
| 138 | + |
| 139 | +* **During token exchange**: When the OIDC origin app asks for the `interclient_token`, Okta explicitly checks to ensure that the app is on the target app's allowed list. If it's not there, the token exchange is denied. |
| 140 | + |
| 141 | +To create an allowlist of apps, use the [Create an allowed app mapping for a target app](http://localhost:3000/openapi/okta-management/management/tag/ApplicationInterclientTrustMappings/#tag/ApplicationInterclientTrustMappings/operation/createInterclientTrustMapping) method (`POST /api/v1/apps/{appId}/interclient-allowed-apps`). The `appID` is the target app’s ID. |
| 142 | + |
| 143 | +> **Note**: You can also use the [Admin Console](https://help.okta.com/okta_help.htm?type=oie&id=apps-native-to-web) to configure the trust mapping. |
| 144 | +
|
| 145 | +In the body of the request, include the [app ID](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Application/#tag/Application/operation/listApplications!c=200&path=4/id&t=response) of the app that you want to add to the allowlist: |
| 146 | + |
| 147 | +```JSON |
| 148 | +{ |
| 149 | + "id": "{appId}" |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +**Response example** |
| 154 | + |
| 155 | +```JSON |
| 156 | +{ |
| 157 | + "id": "itm8vef5ul1nLwiJe0g7", |
| 158 | + "orgId": "00o47ijbqfgnq5gj00g7", |
| 159 | + "appInstanceId": "{targetwebappID}", |
| 160 | + "trustedAppInstanceId": "{OIDCappID}", |
| 161 | + "created": "2025-11-12T23:17:09.000Z", |
| 162 | + "lastUpdated": "2025-11-12T23:17:09.000Z", |
| 163 | + "lastUpdatedBy": "00u47ijy7sRLaeSdC0g7" |
| 164 | +} |
| 165 | +``` |
| 166 | + |
| 167 | +### Application Interclient Trust Mappings API |
| 168 | + |
| 169 | +See the following new [Application Interclient Trust Mappings API](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/ApplicationInterclientTrustMappings/) endpoints to perform other Native to Web SSO tasks: |
| 170 | + |
| 171 | +* To get a list of allowed apps for a target app, use the [List all allowed apps for a target app](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/ApplicationInterclientTrustMappings/#tag/ApplicationInterclientTrustMappings/operation/listInterclientAllowedApplications) method (`GET https://{yourOktaDomain}/api/v1/apps/{appId}/interclient-allowed-apps`). The `appId` is the ID of the target web app (the one allowing the SSO). |
| 172 | + |
| 173 | +* To get a list of target apps that allow an app to SSO, use the [List all target apps for an allowed app](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/ApplicationInterclientTrustMappings/#tag/ApplicationInterclientTrustMappings/operation/listInterclientTargetApplications) method (`GET https://{yourOktaDomain}/api/v1/apps/{appId}/interclient-target-apps`). The `appId` is the ID of the requesting OIDC origin app (the one allowed to request the SSO). |
| 174 | + |
| 175 | +* To delete a trust mapping, use the [Delete an interclient trust mapping](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/ApplicationInterclientTrustMappings/#tag/ApplicationInterclientTrustMappings/operation/deleteInterclientTrustMapping) method. |
| 176 | + |
| 177 | +### Scopes |
| 178 | + |
| 179 | +There are two new OAuth 2.0 scopes available for the interclient endpoints: |
| 180 | + |
| 181 | +* `okta.apps.interclientTrust.manage`: Use to create a resource, manage a resource, or delete a resource |
| 182 | +* `okta.apps.interclientTrust.read`: Use to read information about a resource |
| 183 | + |
| 184 | +## Flow specifics |
| 185 | + |
| 186 | +The following section outlines the requests required to perform Native to Web SSO. These example requests use a native app as the origin app. You can obtain a token with the `interclient_access` scope using your preferred [authentication method](/docs/guides/implement-grant-type/authcode/main/). The examples below use Resource Owner Password as the authentication flow for simplicity. |
| 187 | + |
| 188 | +### Request for initial tokens |
| 189 | + |
| 190 | +Before you can begin this flow, collect the required credentials from the user in a manner of your choosing. Then, make a single API call to the Okta authorization server `/token` endpoint. Your request should look something like the following example: |
| 191 | + |
| 192 | +```BASH |
| 193 | +curl --request POST \ |
| 194 | + --url https://{yourOktaDomain}/oauth2/v1/token \ |
| 195 | + --header 'accept: application/json' \ |
| 196 | + --header 'authorization: Basic MG9hYn...' \ |
| 197 | + --header 'content-type: application/x-www-form-urlencoded' \ |
| 198 | + --data 'grant_type=password |
| 199 | + &client_id={client_id} |
| 200 | + &username=testuser1%40example.com |
| 201 | + &password=%7CmCovrlnU9oZU4qWGrhQSM%3Dyd |
| 202 | + &scope=openid%20offline_access%20interclient_access' |
| 203 | +``` |
| 204 | + |
| 205 | +Note the parameters that are passed: |
| 206 | + |
| 207 | +* `client_id`: Matches the client ID of the OIDC origin app. You can find it at the top of your app's **General** tab |
| 208 | +* `scope`: Must be `openid`, `interclient_access`, and optionally `offline_access` |
| 209 | +* `grant_type`: `password`, which indicates that you're using the Resource Owner Password grant type. |
| 210 | +* `username`: The username of a user registered with Okta. |
| 211 | +* `password`: The password of a user registered with Okta. |
| 212 | + |
| 213 | +> **Note**: For more information on these parameters, see the `/token` [endpoint](https://developer.okta.com/docs/api/openapi/okta-oauth/oauth/tag/OrgAS/#tag/OrgAS/operation/token). |
| 214 | +
|
| 215 | +**Response example** |
| 216 | + |
| 217 | +If the credentials are valid, Okta responds with the required tokens. This example response is truncated for brevity. |
| 218 | + |
| 219 | +```JSON |
| 220 | +{ |
| 221 | + "token_type": "Bearer", |
| 222 | + "expires_in": 3600, |
| 223 | + "access_token": "eyJraWQiOiItbkk . . . aIRQ", |
| 224 | + "scope": "openid offline_access interclient_access", |
| 225 | + "refresh_token": "DnZ77s5coFFJsj7CBO5NJU_wlZs0SZ0euCeiKXrN8T4", |
| 226 | + "id_token": "eyJraWQi . . . AHlwNmdw" |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +### Request to initialize Native to Web SSO |
| 231 | + |
| 232 | +When the user requests access to a resource from the target web app, the native app needs to launch a trusted target web app. It makes a request to refresh the access token, if necessary, and gets back refreshed tokens from Okta. Then, the OIDC origin app needs to exchange the tokens for a single-use interclient token. Your request should look something like this example. The tokens are truncated for brevity. |
| 233 | + |
| 234 | +**Request example** |
| 235 | + |
| 236 | +```BASH |
| 237 | +curl --request POST |
| 238 | + --url https://{yourOktaDomain}/oauth2/v1/token \ |
| 239 | + --header 'content-type: application/x-www-form-urlencoded' \ |
| 240 | + --header 'accept: application/json' \ |
| 241 | + --data 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange |
| 242 | + &client_id={client_id} |
| 243 | + &actor_token=eyJra. . . tSXL-HCA |
| 244 | + &actor_token_type=urn:ietf:params:oauth:token-type:access_token |
| 245 | + &subject_token=eyJr. . .cbYGw |
| 246 | + &subject_token_type=urn:ietf:params:oauth:token-type:id_token |
| 247 | + &requested_token_type=urn:okta:params:oauth:token-type:interclient_token |
| 248 | + &audience=urn:okta:apps:0oa8vcy7h1eyj7wLL0g7' |
| 249 | +``` |
| 250 | + |
| 251 | +Note the parameters that are passed: |
| 252 | + |
| 253 | +* `client_id`: Matches the client ID of the OIDC origin app. You can find it at the top of your app's **General** tab |
| 254 | +* `actor_token`: The value of the access token that you obtained in the last request |
| 255 | +* `actor_token_type`: `urn:ietf:params:oauth:token-type:access_token` |
| 256 | +* `subject_token`: A security token that represents the identity of the party on behalf of whom the request is being made, which is the value of the ID token that you obtained in the last request. |
| 257 | +* `subject_token_type`: `urn:ietf:params:oauth:token-type:id_token` |
| 258 | +* `requested_token_type`: The type of token that you’re requesting in exchange for the `actor_token` and `subject_token`. (`urn:okta:params:oauth:token-type:interclient_token`) |
| 259 | +* `audience`: The target audience for the requested token bound to the target app using the IdP/Okta application/client ID (`urn:okta:apps:{target web app client_id}`) |
| 260 | +* `grant_type`: `urn:ietf:params:oauth:grant-type:token-exchange` |
| 261 | + |
| 262 | +**Response example** |
| 263 | + |
| 264 | +This example response is truncated for brevity. |
| 265 | + |
| 266 | +```JSON |
| 267 | +{ |
| 268 | + "token_type": "N_A", |
| 269 | + "expires_in": 300, |
| 270 | + "access_token": "eyJraWQiOiJNTG. . .GubhhEsg", |
| 271 | + "issued_token_type": "urn:okta:params:oauth:token-type:interclient_token" |
| 272 | +} |
| 273 | +``` |
0 commit comments