Use Case: Configure mobile-to-web payment flows using Native to Web SSO
Before you start
1. Create an Auth0 tenant to register and configure your native and web applications.
2. Install and configure Auth0 CLI for your Auth0 tenant.
3. Add Auth0 Authentication to your native application using the appropriate Quickstarts for your platform:
4. Add refresh_token support to your native application.
5. Add Auth0 Authentication to your web application using the React Single Page App Quickstart.
Native to Web Single Sign-On (SSO) allows you to create a seamless, secure user experience between your native application and a web-based paid membership flow. Native to Web SSO enables the native application to send the user’s authentication context to the web application using a short-lived, secure session_transfer_token
.
The sections below outline how you can add:
To your native application, a Subscribe Now button that lets authenticated users sign up for a paid membership plan using a secure web checkout experience.
To your web application, a Subscription page that allows user to select membership subscriptions without the user logging-in again.
Configure Auth0 CLI
Follow these steps to authenticate your Auth0 tenant using Auth0 CLI:
1. Initialize the Auth0 CLI
auth0 login
Was this helpful?
2. Select As a user and follow the login flow.
How would you like to authenticate?
> As a user
As a machine
Was this helpful?
3. Select the Auth0 tenant where you want to enable Native to Web SSO.
Configure Auth0
Enable Native to Web SSO in your native application
Native to Web SSO uses a session_transfer_token
to establish SSO from a native to a web application. The session_transfer_token
allows Auth0 to identify the user, the origin native application, and additional context securely. To learn more, read Native to Web SSO.
Enable Native to Web SSO using Auth0 CLI:
auth0 apps session-transfer update {yourClientId} --can-create-token=true --enforce-device-binding=asn
Was this helpful?
Enable Native to Web SSO in your web application
Enable the web application to accept the session_transfer_token
for authentication through a cookie or URL parameter using Auth0 CLI:
auth0 apps session-transfer update {yourClientId} --allowed-auth-methods=cookie,query
Was this helpful?
If the session_transfer_token
is injected into the browser with a cookie no additional changes to your web application are required. The only requirement is that the browser navigates to your application's Login URI to handle the redirect of the user to your Auth0 tenant’s /authorize
endpoint.
Create a subscription page in the web application
Add a new /src/views/
file to create a subscription page:
1: Add a new view file
Create a file at src/views/JoinMembership.js
. This file will prompt the users to complete a paid subscription.
import React, { useEffect } from "react";
import { useAuth0 } from "@auth0/auth0-react";
import { useLocation } from "react-router-dom";
import { Container, Button } from "reactstrap";
import Loading from "../components/Loading";
const JoinMembership = () => {
const { isAuthenticated, isLoading, loginWithRedirect } = useAuth0();
const location = useLocation();
useEffect(() => {
if (isLoading || isAuthenticated) return;
const params = new URLSearchParams(location.search);
const token = params.get("session_transfer_token");
const redirectOptions = {
appState: { returnTo: "/join-membership" },
authorizationParams: {},
};
if (token) {
redirectOptions.authorizationParams.session_transfer_token = token;
}
loginWithRedirect(redirectOptions);
}, [isAuthenticated, isLoading, loginWithRedirect, location.search]);
if (isLoading) {
return <Loading />;
}
if (!isAuthenticated) {
return <p>Redirecting to login...</p>;
}
return (
<Container className="mt-5">
<h1>Choose a Subscription Plan</h1>
<Button color="primary" className="mb-3" onClick={() => alert("Subscribed to Basic!")}>
Basic – $5/month
</Button>
<Button color="secondary" className="mb-3" onClick={() => alert("Subscribed to Pro!")}>
Pro – $10/month
</Button>
<Button color="success" className="mb-3" onClick={() => alert("Subscribed to Premium!")}>
Premium – $20/month
</Button>
</Container>
);
};
export default JoinMembership;
Was this helpful?
2: Add a new route
Edit the file src/App.js
and add a /join-membership
route to the new subscription page:
import JoinMembership from "./views/JoinMembership";
{/* Redirect to the join membership page */}
<Route path="/join-membership" component={JoinMembership} />
Was this helpful?
The new /join-membership
route:
Determines if the user is authenticated. If not, it will redirect the user to the Universal Login page.
If a
session_transfer_token
is appended as a URL parameter, the token is passed in the authentication request.Once authenticated, the user will be presented with buttons to subscribe to different membership plans.
Configure the native application
Your native application needs to exchange the refresh_token
for a session_transfer_token
immediately before launching the web application. To do so, add the session transfer exchange and the web application launch logic inside the same event handler, an example is the button’s onClick
method.
iOS
The following steps outline how to add mobile-to-web payment to iOS native applications:
1: Add a Subscribe to Membership button
To launch the web-based subscription flow from your iOS native app, add a Subscribe to Membership
button to the ProfileView.swift
file.
Edit the body of the ProfileView.swift
file to include a button below the user information:
import SwiftUI
struct ProfileView: View {
let user: User
var onSubscribe: () -> Void = {}
var body: some View {
List {
Section(header: ProfileHeader(picture: user.picture)) {
ProfileCell(key: "ID", value: user.id)
ProfileCell(key: "Name", value: user.name)
ProfileCell(key: "Email", value: user.email)
ProfileCell(key: "Email verified?", value: user.emailVerified)
ProfileCell(key: "Updated at", value: user.updatedAt)
}
Section {
Button("Subscribe to Membership", action: onSubscribe)
}
}
}
}
Was this helpful?
The ProfileView.swift
file adds a Subscribe to Membership
button and uses an onSubscribe
closure to determine the behavior when selected.
2: Implement the subscription flow using Native to Web SSO
Edit the MainView.swift
file to define the behavior of the Subscribe to Membership
button:
import SwiftUI
import Auth0
import WebKit
struct MainView: View {
@State var user: User?
var body: some View {
if let user = self.user {
VStack {
ProfileView(user: user, onSubscribe: launchSubscription)
Button("Logout", action: self.logout)
}
} else {
VStack {
HeroView()
Button("Login", action: self.login)
}
}
}
func login() {
Auth0
.webAuth()
.audience("https://sample.api.com")
.scope("profile email offline_access openid")
//.useHTTPS() // Use a Universal Link callback URL on iOS 17.4+ / macOS 14.4+
.start { result in
switch result {
case .success(let credentials):
self.user = User(from: credentials.idToken)
let manager = CredentialsManager(authentication: Auth0.authentication())
let success = manager.store(credentials: credentials)
print("Credentials stored? \(credentials.refreshToken)")
case .failure(let error):
print("Failed with: \(error)")
}
}
}
func logout() {
Auth0
.webAuth()
.useHTTPS()
.clearSession { result in
switch result {
case .success:
self.user = nil
case .failure(let error):
print("Failed with: \(error)")
}
}
}
func launchSubscription() {
let credentialsManager = CredentialsManager(authentication: Auth0.authentication())
credentialsManager.credentials { result in
switch result {
case .success(let credentials):
let refreshToken = credentials.refreshToken ?? ""
Auth0
.authentication()
.ssoExchange(withRefreshToken: refreshToken)
.start { result in
switch result {
case .success(let ssoCredentials):
DispatchQueue.main.async {
let cookie = HTTPCookie(properties: [
.domain: "{yourDomain}", // Replace with your actual Auth0 tenant domain
.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: "http://localhost:3000/join-membership")!
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)")
}
}
case .failure(let error):
print("Error loading credentials: \(error)")
}
}
}
}
Was this helpful?
This allows the user to select the Subscribe to Membership
in the native app and immediately start the web application subscription process without logging in again.
Android
The following steps outline how to add mobile-to-web payment to Android native applications:
1: Add a Subscribe button to the main page
To launch the web-based subscription flow from your Android native application, add a Subscribe to Membership
button to the UI.
Edit the MainActivity.kt
file and add the following code to the onCreate()
method:
binding.buttonSubscribe.setOnClickListener { launchSubscriptionFlow() }
Was this helpful?
Edit the activity_main.xml
file to include the code below after `@+id/button_patch_metadata` button:
<Button
android:id="@+id/buttonSubscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Subscribe to Membership" />
Was this helpful?
2: Implement subscription flow using Native to Web SSO
Edit the MainActivity.kt
file to define the behavior of the Subscribe to Membership
button:
Extend the login flow and handle the subscription action:
// Required imports
import com.auth0.android.authentication.storage.CredentialsManager
import com.auth0.android.authentication.storage.SharedPreferencesStorage
import com.auth0.android.result.SSOCredentials
Was this helpful?
2. Update the onCreate()
method to include the credentialsManager:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set up the account object with the Auth0 application details
account = Auth0.getInstance(
getString(R.string.com_auth0_client_id),
getString(R.string.com_auth0_domain)
)
// Bind the button click with the login action
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.buttonLogin.setOnClickListener { loginWithBrowser() }
binding.buttonLogout.setOnClickListener { logout() }
binding.buttonGetMetadata.setOnClickListener { getUserMetadata() }
binding.buttonPatchMetadata.setOnClickListener { patchUserMetadata() }
binding.buttonSubscribe.setOnClickListener { launchSubscriptionFlow() }
credentialsManager = CredentialsManager(
AuthenticationAPIClient(account),
SharedPreferencesStorage(this)
)
}
Was this helpful?
3. Update the loginwithBrowser()
method to store credentials using the credentialsManager:
private fun loginWithBrowser() {
WebAuthProvider.login(account)
.withScheme(getString(R.string.com_auth0_scheme))
.withScope("openid profile email offline_access")
.withAudience("https://example.api.com")
.start(this, object : Callback<Credentials, AuthenticationException> {
override fun onSuccess(credentials: Credentials) {
credentialsManager.saveCredentials(credentials)
cachedCredentials = credentials
showSnackBar("Success: ${credentials.accessToken}")
updateUI()
showUserProfile()
}
override fun onFailure(exception: AuthenticationException) {
showSnackBar("Failure: ${exception.getCode()}")
}
})
}
Was this helpful?
4. Add the launchSubscriptionFlow()
method to open the web application:
private fun launchSubscriptionFlow() {
credentialsManager.getCredentials(object : Callback<Credentials, CredentialsManagerException> {
override fun onSuccess(credentials: Credentials) {
val refreshToken = credentials.refreshToken ?: return
AuthenticationAPIClient(account)
.ssoExchange(refreshToken)
.start(object : Callback<SSOCredentials, AuthenticationException> {
override fun onSuccess(result: SSOCredentials) {
val sessionToken = result.sessionTransferToken
val cookieValue =
"auth0_session_transfer_token=$sessionToken; Path=/; Secure; HttpOnly; SameSite=None"
val cookieManager = android.webkit.CookieManager.getInstance()
cookieManager.setAcceptCookie(true)
cookieManager.setCookie("https://${getString(R.string.com_auth0_domain)}", cookieValue)
val webView = android.webkit.WebView(this@MainActivity)
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : android.webkit.WebViewClient() {
override fun shouldOverrideUrlLoading(view: android.webkit.WebView?, url: String?) = false
}
webView.loadUrl("http://localhost:3000/join-membership")
setContentView(webView)
}
override fun onFailure(error: AuthenticationException) {
showSnackBar("Failed to get session transfer token: ${error.getDescription()}")
}
})
}
override fun onFailure(error: CredentialsManagerException) {
showSnackBar("Failed to load stored credentials: ${error.message}")
}
})
}
Was this helpful?
This allows the user to select Subscribe to Membership
in the native application and immediately start the web application subscription process without logging in again.
Test your Native to Web SSO implementation
Once everything is configured, run your iOS or Android native application to log in, go to the Profile or to the Main screen and select the Subscribe to Membership button.
The following takes place:
The stored
refresh_token
is used to request a securesession_transfer_token
The
session_transfer_token
is injected into a cookie for your Auth0 domainA
WKWebView
is used to load your web application’s/join-membership
routeThe web application receives the
session_transfer_token
and completes login using Native to Web SSOThe user sees the subscription options immediately in the web application
You have created a seamless experience where a mobile native application user can launch a secure, authenticated flow in a web application without being prompted to log in again.
Next Steps
Explore more configuration options for Native to Web SSO: Dive deeper into session lifetime, refresh token rotation, device binding, and cascade revocation in the Native to Web SSO documentation.
Customize the post-login experience with Progressive Profiling: Use Auth0’s Progressive Profile Form to collect additional user data after login — such as plan preferences, address, or payment intent — before showing subscription options.