> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

> Seamlessly transition users from your iOS or Android app into a secure, authenticated subscription flow in your web app using Native to Web Single Sign-On (SSO) and session transfer tokens.

# Use Case: Configure mobile-to-web payment flows using Native to Web SSO

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

<Card title="Before you start">
  1. Create an Auth0 tenant to register and configure your native and web applications.

  2. Install and configure [Auth0 CLI](https://github.com/auth0/auth0-cli) for your Auth0 tenant.

  3. Add Auth0 Authentication to your native application using the appropriate Quickstarts for your platform:

  * [iOS Swift Quickstart](/docs/quickstart/native/ios-swift/interactive)
  * [Android Quickstart](/docs/quickstart/native/android/interactive)

  4. Add [refresh\_token](/docs/secure/tokens/refresh-tokens/get-refresh-tokens) support to your native application.

  5. Add Auth0 Authentication to your web application using the [React Single Page App Quickstart](/docs/quickstart/spa/react/interactive).
</Card>

[Native to Web Single Sign-On (SSO)](/docs/authenticate/single-sign-on/native-to-web) allows you to create a seamless, secure user experience between your native application and a web-based paid membership flow. Native to Web <Tooltip tip="Single Sign-On (SSO): Service that, after a user logs into one applicaton, automatically logs that user in to other applications." cta="View Glossary" href="/docs/glossary?term=SSO">SSO</Tooltip> 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.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  This use case focuses on React-based web applications using the Auth0 React SDK.

  If you are using a different framework, such as Node or Express, the logic for managing `session_transfer_token` whether through a URL parameter or cookie can be adapted accordingly.
</Callout>

## Configure Auth0 CLI

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  Use the [Auth0 CLI](https://github.com/auth0/auth0-cli) to configure your Auth0 tenant. You can also use the [Auth0 Management API](https://auth0.com/docs/api/management/v2); to learn more read [Configure Native to Web SSO](/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web#configure-native-applications).
</Callout>

Follow these steps to authenticate your Auth0 tenant using [Auth0 CLI](https://github.com/auth0/auth0-cli):

1. Initialize the Auth0 CLI

```bash lines theme={null}
auth0 login
```

2. Select **As a user** and follow the login flow.

```bash lines theme={null}
How would you like to authenticate?
> As a user
  As a machine
```

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](/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web).

Enable Native to Web SSO using Auth0 CLI:

export const codeExample1 = `auth0 apps session-transfer update {yourClientId} --can-create-token=true --enforce-device-binding=asn`;

<AuthCodeBlock children={codeExample1} language="bash" />

### 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:

export const codeExample2 = `auth0 apps session-transfer update {yourClientId}  --allowed-auth-methods=cookie,query`;

<AuthCodeBlock children={codeExample2} language="bash" />

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](https://sus.auth0.com/docs/get-started/applications/application-settings#application-uris) to handle the redirect of the user to your Auth0 tenant’s `/authorize` endpoint.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  You can configure the Application Login URI in your application's settings within the Auth0 Dashboard. This is the route Auth0 will redirect users to when initiating login from external sources.
</Callout>

## Create a subscription page in the web application

Add a new `/src/views/` file to create a subscription page:

### Step 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.

```js lines expandable theme={null}
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;
```

### Step 2: Add a new route

Edit the file `src/App.js` and add a `/join-membership` route to the new subscription page:

```js lines theme={null}
import JoinMembership from "./views/JoinMembership";

 {/* Redirect to the join membership page */}
<Route path="/join-membership" component={JoinMembership} />
```

The new `/join-membership` route:

* Determines if the user is authenticated. If not, it will redirect the user to the <Tooltip tip="Universal Login: Your application redirects to Universal Login, hosted on Auth0's Authorization Server, to verify a user's identity." cta="View Glossary" href="/docs/glossary?term=Universal+Login">Universal Login</Tooltip> 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.

<Warning>
  Run your React app and go to `http://localhost:3000/join-membership`

  * If the user is authenticated, the user will see the subscription options.
  * If the user is not authenticated, they are automatically redirected to the Auth0 Universal Login page.

    * If the URL includes a `session_transfer_token`, it will be included in the login request to Auth0.
    * If not, the login will proceed using the standard web authentication.

  When the user logs in, the user is returned to the `/join-membership` page to select the subscription options.
</Warning>

## 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:

#### Step 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:

```swift lines theme={null}
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)
            }
        }
    }
}
```

The `ProfileView.swift` file adds a `Subscribe to Membership` button and uses an `onSubscribe` closure to determine the behavior when selected.

#### Step 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:

export const codeExample3 = `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)")
            }
        }
    }
}`;

<AuthCodeBlock children={codeExample3} language="swift" />

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  This sample uses the audience `https://sample.api.com` for demonstration purposes. You can create an API with this identifier in your Auth0 tenant or replace it with your own API identifier.

  To learn more, read [Set Up APIs](/docs/get-started/auth0-overview/set-up-apis).
</Callout>

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:

#### Step 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:

```kotlin wrap lines theme={null}
binding.buttonSubscribe.setOnClickListener { launchSubscriptionFlow() }
```

Edit the `activity_main.xml` file to include the code below after `@+id/button_patch_metadata` button:

```xml lines theme={null}
<Button
    android:id="@+id/buttonSubscribe"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Subscribe to Membership" />
```

#### Step 2: Implement subscription flow using Native to Web SSO

Edit the `MainActivity.kt` file to define the behavior of the `Subscribe to Membership` button:

1. Extend the login flow and handle the subscription action:

```kotlin lines theme={null}
// Required imports
import com.auth0.android.authentication.storage.SecureCredentialsManager
import com.auth0.android.authentication.storage.SharedPreferencesStorage
import com.auth0.android.result.SSOCredentials
```

2. Update the `onCreate()`method to include the `credentialsManager:`

```kotlin lines theme={null}
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() }

        secureCredentialsManager = SecureCredentialsManager(
            this,
            AuthenticationAPIClient(account),
            SharedPreferencesStorage(this)
        )

    }
```

3. Update the `loginwithBrowser()` method to store credentials using the `credentialsManager:`

```kotlin lines theme={null}
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) {
                secureCredentialsManager.saveCredentials(credentials)
                cachedCredentials = credentials
                showSnackBar("Success: ${credentials.accessToken}")
                updateUI()
                showUserProfile()
            }

            override fun onFailure(exception: AuthenticationException) {
                showSnackBar("Failure: ${exception.getCode()}")
            }
        })
}
```

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  This sample uses the audience `https://sample.api.com` for demonstration purposes. You can create an API with this identifier in your Auth0 tenant or replace it with your own API identifier.

  To learn more, read [Set Up APIs](/docs/get-started/auth0-overview/set-up-apis).
</Callout>

4. Add the `launchSubscriptionFlow()` method to open the web application:

```kotlin lines expandable theme={null}
private fun launchSubscriptionFlow() {
    secureCredentialsManager.getSsoCredentials(
        mapOf(),  // optional parameters
        object : Callback<SSOCredentials, CredentialsManagerException> {
            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: CredentialsManagerException) {
                showSnackBar("Failed to get session transfer token: ${error.message}")
            }
        }
    )
}
```

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 Mainscreen and select the **Subscribe to Membership** button.

The following takes place:

* The stored `refresh_token` is used to request a secure `session_transfer_token`
* The `session_transfer_token` is injected into a cookie for your Auth0 domain
* A `WKWebView` is used to load your web application’s `/join-membership` route
* The web application receives the `session_transfer_token` and completes login using Native to Web SSO
* The 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, <Tooltip tip="Refresh Token: Token used to obtain a renewed Access Token without forcing users to log in again." cta="View Glossary" href="/docs/glossary?term=refresh+token">refresh token</Tooltip> rotation, device binding, and cascade revocation in the [Native to Web SSO documentation](/docs/authenticate/single-sign-on/native-to-web).
* Customize the post-login experience with Progressive Profiling:Use Auth0’s [Progressive Profile Form](/docs/customize/forms/configure-progressive-profile-form) to collect additional user data after login — such as plan preferences, address, or payment intent — before showing subscription options.
