Ping SDKs

Enabling single sign-on between embedded, in-app browsers, and WebViews

A common use-case for the Ping SDKs for Android and iOS is seamlessly signing on to a second protected webapp after having first signed on to a separate webapp.

For example, your native app has used embedded sign-in to authenticate your user for auth.example.com, but you have a second protected webapp running at checkout.example.com, and you don’t want to reauthenticate the user.

Best practice dictates that the powerful session bearer tokens are short-lived. Instead you should use OAuth 2.0 access tokens, and refresh tokens to keep them current without having to reauthenticate the user.

However, you should also not pass OAuth 2.0 tokens to a web app, as it may make them easier for a malicious actor to obtain and use.

This article outlines one solution for authenticating users to additional webapps, without exposing session or access tokens to a browser, or the app itself.

Opening webapps in Android and iOS

Native Android and iOS applications use two main methods for opening a website or web app:

The Ping SDKs for Android and iOS use an in-app browser when authenticating using OIDC sign-on, rather than a WebView.

Understanding the distinction between WebViews and in-app browsers is critical for security and user experience, and explains why the Ping SDKs use an in-app browser by default.

WebViews

A WebView is a component that renders web pages as an integrated part of your native app’s UI.

Control

Your app has extensive control over a WebView. It can inspect traffic, inject JavaScript, and access content.

You can set headers, URL parameters, and cookies in the requests.

Context

WebViews run in the same process as your app, sharing memory and resources.

The WebView does not share cookies or sessions with the device’s default browser.

Security

As the app can manipulate the WebView, it is considered less secure for handling sensitive authentication redirects.

An attacker who compromises the app could potentially intercept credentials.

Compatibility

WebViews don’t support the entire web specification.

Advanced features like WebAuthn and Passkeys do not work.

In-App Browsers

In-app browsers appear separated from your native app’s user interface, often creating their own window to open the website or web app.

In-app browsers are platform-recommended components such as Chrome Custom Tabs on Android, and SFSafariViewController and ASWebAuthenticationSession on iOS.

Control

Your app has minimal control over an in-app browser window. It can’t inspect the content or inject scripts.

It essentially hosts a sandboxed, secure browser tab.

Context

An in-app browser runs as a separate, secure process.

Critically, certain components like ASWebAuthenticationSession and Chrome Custom Tabs share the cookie jar with the device’s default browser, for example Safari or Chrome.

This sharing of cookies is the key mechanism for enabling Web SSO.

Security

This separation between the app and the in-app browser makes it the most secure and recommended option for user authentication flows.

It prevents the host app from interfering with the login process.

Compatibility

In-App browsers are fully-featured browsers that run on the same engine as Safari and Chrome.

Features like WebAuthn and Passkeys are fully supported.

In-app browser implementation on iOS

On iOS there are two main implementations of in-app browsers; SFSafariViewController and ASWebAuthenticationSession.

This section outlines some of the differences between the two iOS implementations.

SFSafariViewController

The SFSafariViewController implementation is designed to display general web content within your app while maintaining a secure, sandboxed environment.

Cookie storage

Since iOS 11, SFSafariViewController does not share cookies with the native Safari browser.

If a user is logged into a website on Safari, they will not be logged in when that same site is opened by using SFSafariViewController.

SSO within the same app

Instances of SFSafariViewController within the same app do share a cookie store.

This means if you have multiple SFSafariViewController instances in your application, they can maintain a consistent session, but this session is sandboxed to your app only.

Other storage

Instances of SFSafariViewController share their own data store for localStorage and sessionStorage, which is separate from the native Safari browser.

Use case

Ideal for displaying external, non-authentication-related web pages.

ASWebAuthenticationSession

The ASWebAuthenticationSession implementation is a specialized API built for handling authentication flows like OAuth 2.0.

It is the recommended and most secure method for logging a user in to a web app.

Cookie Storage

It shares cookies and website data with the native Safari browser by default.

This cookie sharing enables a seamless web SSO experience.

Ephemeral sessions

You can optionally configure it using the prefersEphemeralWebBrowserSession property to start with a clean slate each time.

This prevents it from sharing any existing cookies, and it discards any new ones after the session.

UI and consent

The ASWebAuthenticationSession implementation presents a clear, recognizable, non-customizable system dialog to the user, informing them that your app is about to use a specific website to sign them in.

This explicit user consent is a critical security feature.

Use case

The only appropriate choice for handling third-party login flows, such as signing in with through your Ping Identity solutions.

Summary of iOS in-app browser implementations

The following table summarizes the key differences between the two iOS implementations of an in-app browser:

Feature SFSafariViewController ASWebAuthenticationSession

Primary Use Case

Displaying general web content

User authentication flows (OAuth)

Ephemeral Session Option

No

Yes

Shares Cookies with Safari

No (iOS 11+)

Yes (unless ephemeral)

Shares Other Data with Safari

No (iOS 11+)

Yes (unless ephemeral)

Data Returned to App

Nothing (Custom schemes are possible but not recommended for auth)

Callback URL with tokens and codes

UI Customization

Minimal (tint color)

None (System-provided alert)

Implementing a session handoff flow

The core principle is that the native Android or iOS app contacts the authorization server and exchanges its valid access token with a secure, one-time passcode.

This exchange happens directly in the background between the client app and authorization server using REST and TLS, making it considerably more difficult for an attacker to access the data.

The native app can then pass this one-time passcode to an in-app browser or WebView as part of a URL, which the AS exchanges for a real session cookie for the second webapp.

This solution works however you initially authenticated the user and obtained their OAuth 2.0 tokens, and allows you to open either an in-app browser or WebView to authenticate against additional apps, negating the drawbacks outlined above.

This flow ensures that the powerful access token is never exposed to a browser, and the handoff is secured by a short-lived, single-use credential.

The solution requires a custom journey in Advanced Identity Cloud or PingAM, or a custom endpoint in PingIDM.

This article doesn’t cover the implementation of these requirements.

The sequence diagram below outlines the steps required to enable single sign-on between apps in a native Android or iOS client app:

Implementing a session handoff flow
Figure 1. Implementing a session handoff flow

Obtaining a handoff one-time passcode

1 Native app requests handoff OTP

The native app has already authenticated the user against the first application, and obtained an access token for them. The session cookie is short-lived, and has now expired.

The app makes a secure backend call, using REST endpoints, to launch a custom journey in Advanced Identity Cloud or PingAM, or to call a custom endpoint in PingIDM.

The call includes an Authorization: Bearer header, containing the original access token.

2 Introspect Access Token

The journey or custom endpoint validates the access token using the introspection endpoint to ensure it’s active and valid.

3 Authorization server creates handoff OTP

The authorization server generates a short-lived, one-time passcode. For example, a cryptographically random string.

It maps the OTP to the user’s identity and stores the code temporarily in the user datastore, or an external database.

4 Return handoff OTP

The authorization server returns the short-lived one-time passcode to the native app.

5 Native App Launches in-app browser or WebView

On receipt of the handoff one-time passcode, which has a short time-to-live (TTL), the native app immediately launches the in-app browser or WebView with a specially crafted URL to start a custom journey URL or open a custom IDM endpoint.

The URL includes any required query parameters. For example for a journey the parameters required include the following:

  • The handoff one-time passcode, for example in a handoffotp custom parameter.

  • The URL of the secondary webapp that the user is attempting to access, in the goto parameter.

    The authorization server redirects the in-app browser or WebView to this URL after successful authentication after exchanging the one-time passcode.

  • On Advanced Identity Cloud and PingAM, the name of the custom journey that handles the handoff OTP and issues the additional session token.

    For example, authIndexType=service and authIndexValue=handle-handoff-otp-journey

The resulting URL to launch a journey in the in-app browser or WebView might resemble the following:

https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate?authIndexType=service&authIndexValue=handle-handoff-otp-journey&goto=https://example.com/myAccount&handoffotp=1b4db7eb-4057-5ddf-91e0-36dec72071f5

6 Start auth journey

The in-app browser or WebView makes the GET request to the authorization server to start the custom auth journey, using the URL passed to it from the native app.

7 Verify and delete OTP

The Advanced Identity Cloud journey or PingIDM custom endpoint looks up the one-time passcode from the URL.

If the code is found and valid, the server deletes the one-time code so it can’t be used again.

8 Issue a new session token

After deleting the one-time passcode, the server completes the journey and creates a Secure, HttpOnly session token cookie for the associated user:

  • The Secure attribute so that they’re only sent over HTTPS.

  • The HttpOnly attribute so that JavaScript can’t access the cookies, which prevents attackers from stealing them using cross-site scripting (XSS).

9 Redirect to the goto URL

The server returns a 302 redirect to send the in-app browser or WebView to the actual target page. The redirect includes the Set-Cookie header containing the new session token cookie.

10 Save the session token in a cookie

The in-app browser or WebView saves the new session token cookie within its own context.

11 Authenticated web session

The in-app browser or WebView follows the redirect, automatically attaching the new session token cookie to the request.

The user seamlessly signs in to the web application, without having to interact or authenticate again to access the second webapp.

12 Return protected content

The protected webapp can now return protected content as the user is authenticated with a valid session token. You can introspect the session token against the server as if they had logged in directly to the app.