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 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, If a user is logged into a website on Safari, they will not be logged in when that same site is opened by using |
| SSO within the same app |
Instances of This means if you have multiple |
| Other storage |
Instances of |
| 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 This prevents it from sharing any existing cookies, and it discards any new ones after the session. |
| UI and consent |
The 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:
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: Bearerheader, 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.
Exchanging a one-time passcode for a session cookie
- 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
handoffotpcustom parameter. -
The URL of the secondary webapp that the user is attempting to access, in the
gotoparameter.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=serviceandauthIndexValue=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,HttpOnlysession token cookie for the associated user:-
The
Secureattribute so that they’re only sent over HTTPS. -
The
HttpOnlyattribute so that JavaScript can’t access the cookies, which prevents attackers from stealing them using cross-site scripting (XSS).
-
- 9 Redirect to the
gotoURL -
The server returns a 302 redirect to send the in-app browser or WebView to the actual target page. The redirect includes the
Set-Cookieheader 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.