Configure and Implement Native to Web SSO

Configure Native to Web SSO

To use Native to Web Single Sign-On (SSO), configure your native and web (Single Page App or Regular Web App) to create and manage sessions with the Auth0 Management API. You need an access token to use the Management API.

To configure Native to Web SSO, you need to create and manage session_transfer_tokens and configure your native and web applications.

Create and manage Session Transfer Tokens

The first Management API call allows your native and web Applications to:

  • Create and manage session_transfer_tokens

  • Create sessions in a web browser via cookies or a URL parameter

  • Bind the session to a user's device through an IP address or ASN

For existing applications, make a PATCH call to the Update a Client endpoint. To create a new application, make a POST call to the Create a Client endpoint:

{
  "session_transfer": {
    "can_create_session_transfer_token": false,
    "allowed_authentication_methods": ["cookie", "query"],
    "enforce_device_binding": "ip", // also "none" or "asn"
  }
}

Was this helpful?

/

Parameter Type Value Description
can_create_session_transfer_token Boolean, default = false N/A Specifies whether the application can use the Token Exchange endpoint to create a session_transfer_token.
allowed_authentication_methods Array of enum, default =[] cookie, query Determines the methods allowed for a web application to create a session using a session_transfer_token:
  • query: Allows your application to create a session by passing the session_transfer_token as a URL parameter in the call to the /authorize endpoint.
  • cookie: Allows your application to create a session by sending the session_transfer_token in a cookie during the call to the /authorize endpoint.
cookie takes precedence if both methods are allowed.
enforce_device_binding Enum, default = ip none, ip, asn Configures the level of device binding enforced when a session_transfer_token is consumed:
  • none: Does not enforces device binding.
  • ip: Enforces device binding when session_transfer_token is consumed from the same IP address it was created
  • asn:Enforces device binding by matching the Autonomous System Number (ASN) of the requestor

Configure native applications

Once a user is authenticated, Auth0 returns an Access token, and ID token, and (optionally) a Refresh token.

You can configure your native application to exchange a refresh token for a Session Transfer Token. If your web application does not support cookie injection, your native application also needs to configure your web application’s Login URI to inject the Session Transfer Token as a URI parameter.

You can update your native application using the Update a Client endpoint:


curl --request PATCH \
  --url 'https://{yourDomain}/api/v2/clients//{yourClientId}' \
  --header 'authorization: Bearer {yourMgmtApiAccessToken}' \
  --header 'content-type: application/json' \
  --data '{
  "session_transfer": {
    "can_create_session_transfer_token": true,
    "enforce_device_binding": "ip"
  }
}'

Was this helpful?

/
var client = new RestClient("https://{yourDomain}/api/v2/clients//{yourClientId}");
var request = new RestRequest(Method.PATCH);
request.AddHeader("authorization", "Bearer {yourMgmtApiAccessToken}");
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);

Was this helpful?

/
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://{yourDomain}/api/v2/clients//{yourClientId}"

	payload := strings.NewReader("{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("authorization", "Bearer {yourMgmtApiAccessToken}")
	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

Was this helpful?

/
HttpResponse<String> response = Unirest.patch("https://{yourDomain}/api/v2/clients//{yourClientId}")
  .header("authorization", "Bearer {yourMgmtApiAccessToken}")
  .header("content-type", "application/json")
  .body("{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}")
  .asString();

Was this helpful?

/
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://{yourDomain}/api/v2/clients//{yourClientId}',
  headers: {
    authorization: 'Bearer {yourMgmtApiAccessToken}',
    'content-type': 'application/json'
  },
  data: {
    session_transfer: {can_create_session_transfer_token: true, enforce_device_binding: 'ip'}
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Was this helpful?

/
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer {yourMgmtApiAccessToken}",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"session_transfer": @{ @"can_create_session_transfer_token": @YES, @"enforce_device_binding": @"ip" } };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/api/v2/clients//{yourClientId}"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];

Was this helpful?

/
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://{yourDomain}/api/v2/clients//{yourClientId}",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PATCH",
  CURLOPT_POSTFIELDS => "{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}",
  CURLOPT_HTTPHEADER => [
    "authorization: Bearer {yourMgmtApiAccessToken}",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Was this helpful?

/
import http.client

conn = http.client.HTTPSConnection("")

payload = "{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}"

headers = {
    'authorization': "Bearer {yourMgmtApiAccessToken}",
    'content-type': "application/json"
    }

conn.request("PATCH", "/{yourDomain}/api/v2/clients//{yourClientId}", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Was this helpful?

/
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://{yourDomain}/api/v2/clients//{yourClientId}")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Patch.new(url)
request["authorization"] = 'Bearer {yourMgmtApiAccessToken}'
request["content-type"] = 'application/json'
request.body = "{\n  \"session_transfer\": {\n    \"can_create_session_transfer_token\": true,\n    \"enforce_device_binding\": \"ip\"\n  }\n}"

response = http.request(request)
puts response.read_body

Was this helpful?

/
import Foundation

let headers = [
  "authorization": "Bearer {yourMgmtApiAccessToken}",
  "content-type": "application/json"
]
let parameters = ["session_transfer": [
    "can_create_session_transfer_token": true,
    "enforce_device_binding": "ip"
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/api/v2/clients//{yourClientId}")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

Was this helpful?

/

Configure web applications

Before you enable Session Transfer Token, make sure you have configured your web application’s Application Login URI to handle extra parameters. To learn more about URIs, read Application Settings.

You can update the web application using the Update a Client endpoint:


curl --request PATCH \
  --url 'https://{yourDomain}/api/v2/clients//{yourClientId}' \
  --header 'authorization: Bearer {yourMgmtApiAccessToken}' \
  --header 'content-type: application/json' \
  --data '{
  "session_transfer": {
    "allowed_authentication_methods": ["cookie", "query"]
  }
}'

Was this helpful?

/
var client = new RestClient("https://{yourDomain}/api/v2/clients//{yourClientId}");
var request = new RestRequest(Method.PATCH);
request.AddHeader("authorization", "Bearer {yourMgmtApiAccessToken}");
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);

Was this helpful?

/
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://{yourDomain}/api/v2/clients//{yourClientId}"

	payload := strings.NewReader("{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("authorization", "Bearer {yourMgmtApiAccessToken}")
	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}

Was this helpful?

/
HttpResponse<String> response = Unirest.patch("https://{yourDomain}/api/v2/clients//{yourClientId}")
  .header("authorization", "Bearer {yourMgmtApiAccessToken}")
  .header("content-type", "application/json")
  .body("{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}")
  .asString();

Was this helpful?

/
var axios = require("axios").default;

var options = {
  method: 'PATCH',
  url: 'https://{yourDomain}/api/v2/clients//{yourClientId}',
  headers: {
    authorization: 'Bearer {yourMgmtApiAccessToken}',
    'content-type': 'application/json'
  },
  data: {session_transfer: {allowed_authentication_methods: ['cookie', 'query']}}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Was this helpful?

/
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"authorization": @"Bearer {yourMgmtApiAccessToken}",
                           @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"session_transfer": @{ @"allowed_authentication_methods": @[ @"cookie", @"query" ] } };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/api/v2/clients//{yourClientId}"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];

Was this helpful?

/
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://{yourDomain}/api/v2/clients//{yourClientId}",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PATCH",
  CURLOPT_POSTFIELDS => "{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}",
  CURLOPT_HTTPHEADER => [
    "authorization: Bearer {yourMgmtApiAccessToken}",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

Was this helpful?

/
import http.client

conn = http.client.HTTPSConnection("")

payload = "{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}"

headers = {
    'authorization': "Bearer {yourMgmtApiAccessToken}",
    'content-type': "application/json"
    }

conn.request("PATCH", "/{yourDomain}/api/v2/clients//{yourClientId}", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Was this helpful?

/
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://{yourDomain}/api/v2/clients//{yourClientId}")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Patch.new(url)
request["authorization"] = 'Bearer {yourMgmtApiAccessToken}'
request["content-type"] = 'application/json'
request.body = "{\n  \"session_transfer\": {\n    \"allowed_authentication_methods\": [\"cookie\", \"query\"]\n  }\n}"

response = http.request(request)
puts response.read_body

Was this helpful?

/
import Foundation

let headers = [
  "authorization": "Bearer {yourMgmtApiAccessToken}",
  "content-type": "application/json"
]
let parameters = ["session_transfer": ["allowed_authentication_methods": ["cookie", "query"]]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/api/v2/clients//{yourClientId}")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

Was this helpful?

/

Implement Native to Web SSO

Native to Web Single SSO provides a seamless user experience transitioning authenticated users from your native application to your web application.

To facilitate this, your native application needs to exchange a refresh token for a Session Transfer Token and send the Session Transfer Token, through a URL or cookie, to your web application to authorize the session.

In your native application

Step 1: Exchanging a Refresh Token for a Session Transfer Token

Use the /token endpoint with your native application to exchange the refresh token for a Session Transfer Token.

import SwiftUI
import Auth0
import WebKit

struct MainView: View {
    var body: some View {
        Button("Open Web Session", action: self.launchWebSSO)
    }

    func launchWebSSO() {
        Auth0
            .authentication()
            .ssoExchange(withRefreshToken: refreshToken)
            .start { result in
                switch result {
                case .success(let ssoCredentials):
                    DispatchQueue.main.async {
                        let cookie = HTTPCookie(properties: [
                            .domain: "{yourDomain}",
                            .path: "/",
                            .name: "auth0_session_transfer_token",
                            .value: ssoCredentials.sessionTransferToken,
                            .expires: ssoCredentials.expiresIn,
                            .secure: true
                        ])!

                        let webView = WKWebView()
                        let store = webView.configuration.websiteDataStore.httpCookieStore
                        store.setCookie(cookie) {
                            let url = URL(string: "https://yourWebApplicationLoginURI")!
                            let request = URLRequest(url: url)
                            webView.load(request)

                            let vc = UIViewController()
                            vc.view = webView
                            UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: true)
                        }
                    }
                case .failure(let error):
                    print("Failed to get SSO token: \(error)")
                }
            }
    }
}

Was this helpful?

/

The Auth0 tenant returns a single-use and short-lived (1-minute lifetime) session_transfer_token.

{
    "access_token": "{session_transfer_token}",
    "issued_token_type": "urn:auth0:params:oauth:token-type:session_transfer_token",
    "token_type": "N_A",
    "expires_in": 60
}

Was this helpful?

/

There are two options to send the  session_transfer_token to your web application based on the configured allowed_authentication_methods.

If your web application using WebView or browser supports cookie injection, you can configure your native application to:

  • Add the session_transfer_token into a cookie. 

  • Open the web application using WebView or browser.

  • Log the web application to your Auth0 tenant or Custom Domain. As the session_transfer_token is included in the cookie, the user is not prompted for first-factor authentication.

{
  "method": "GET",
  "url": "https://YOUR_WEB_APPLICATION_LOGIN_URL/",
  "cookie": [
    {
      "name": "auth0_session_transfer_token",
      "value": "YOUR_SESSION_TRANSFER_TOKEN; Path=/; Secure; HttpOnly; SameSite=None"
    }
  ]
}

Was this helpful?

/

Option 2: Send the session_transfer_token as a URL parameter

If your web application does not support cookie injection, you can configure your native application using URL parameters to:

  • Add the session_transfer_token as a URL parameter.

  • Open the web application using  WebView or browser.

  • Log the web application appending the session_transfer_token as a URL parameter to the /authorize endpoint. The Auth0 tenant authenticates the user without requiring first-factor authentication, as the session_transfer_token is valid and trusted

{
  "method": "GET",
  "url": "https://YOUR_WEB_APP_LOGIN_URL?session_transfer_token=YOUR_SESSION_TRANSFER_TOKEN",
  "headers": []
}

Was this helpful?

/

In your web application

Implement Native to Web Web Single SSO in your web application using URL parameters by:

Option 1: Add the Session Transfer Token in your web application request

Use the /authorize endpoint with your web application when the session_transfer_token is sent as a URL parameter.

{
  "method": "GET",
  "url": "https://YOUR_WEB_APP_LOGIN_URL?session_transfer_token=YOUR_SESSION_TRANSFER_TOKEN",
  "headers": []
}

Was this helpful?

/

Option 2: Add the Session Transfer Token to web applications using Auth0 SDKs

Auth0 SDKs do not support Native to Web Single SSO automatically and they will not include the session_transfer_token in the /authorize endpoint request.

Below are examples of web applications using Auth0 SDKs to redirect the session_transfer_token in the /authorize endpoint request:

Node (Express.js)

If your web application uses Express.js or the Auth0 Express SDK, you can use the code below to add middleware support for session_transfer_token.

const config = {
  authRequired: false,
  auth0Logout: true
};

// Default Middleware with no customizations
// app.use(auth(config));

// Extending the middleware to auto detect session_transfer_token
app.use((req, res, next) => {
  const { session_transfer_token } = req.query;

  if (session_transfer_token) {
    config.authorizationParams = {
      session_transfer_token,
    }
  }

  auth(config)(req, res, next);
});

Was this helpful?

/

SAML and WS-Federation

If your web application uses SAML or WS-Fed service provider and Auth0 as the IdP, you can send the session_transfer_token as an URL parameter to the Auth0 /authorize endpoint and the redirect_uri is the SAML or WS-Fed sign-in URL.

{
  "method": "GET",
  "url": "https://{yourDomain}/samlp/{yourClientId}",
  "headers": [
    {
      "name": "Cookie",
      "value": "session_transfer_token=YOUR_SESSION_TRANSFER_TOKEN"
    }
  ],
  "queryString": [
    {
      "name": "session_transfer_token",
      "value": "YOUR_SESSION_TRANSFER_TOKEN"
    }
  ]
}

Was this helpful?

/

Session Transfer Token with Actions

Using session_transfer_token with Actions allows you to configure post-authentication risk detection and response capabilities to enhance user protection.

To facilitate this, the post-login Action object event.session_transfer_token provides relevant information including unique client_id, scope, request information such as ip, asn, user_agent and geoip information such as, cityName, countryCode among others. To learn more, read Actions Triggers: post-login - Event Object.

The Action code below allows you to dynamically reject a transaction based on geolocation information:

/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
   if(
     event.session_transfer_token &&
     event.session_transfer_token.request.geoip.countryCode !== event.request.geoip.countryCode
     ) {
     api.access.deny("Network mismatch detected")
   }
};

Was this helpful?

/

Monitoring

You can monitor the Native to Web SSO activity by reviewing the Tenant logs.

  • sertft : Successful Refresh Token exchange. This log will correspond to a Native to Web SSO exchange when the audience field is "audience":"urn:$auth0Domain:session_transfer"

  • fertft: Failed Refresh Token exchange. This log will correspond to a Native to Web SSO exchange when the audience field is "audience": "urn:$auth0Domain:session_transfer"