ASWebAuthenticationSession opens a browser view to handle OAuth and single-sign-on flows. When prefersEphemeralWebBrowserSession is false (the default), Safari’s full cookie jar is shared - the same session the user is logged into across every website. An app that opens an OAuth flow without ephemeral mode is silently importing the entire browser state, and if a malicious OAuth server can redirect back to the app’s custom scheme, it carries a token minted against the user’s persistent session.
The shape
Highlight 1 is callbackURLScheme: "vulnlaboauth" - custom URL scheme used for the OAuth redirect; any other app that registers vulnlaboauth:// in its Info.plist can receive the callback URL containing the authorization code.
Highlight 2 is prefersEphemeralWebBrowserSession not set - defaults to false; Safari’s full cookie jar is shared with the auth session, so if the user is already logged in at the IdP, the OAuth flow completes silently without any user interaction or visible prompt.
When prefersEphemeralWebBrowserSession is false, the authentication web page opens inside Safari’s shared session. Any cookies set by the IdP during a prior login (in Safari or another app) are present, and the IdP may skip re-authentication and issue an authorization code immediately. The code arrives at vulnlaboauth://callback and the app exchanges it for an access token - all without the user re-entering credentials.
Why this matters
Three attack scenarios:
Silent token issuance. The attacker loads a phishing page that silently opens ASWebAuthenticationSession toward the target IdP with the victim app’s client_id and a redirect URI the attacker controls. If the victim is already logged into the IdP in Safari, the IdP immediately redirects to the attacker’s URI with an authorization code. Ephemeral mode forces a fresh login prompt; non-ephemeral doesn’t.
Session confusion in shared-device scenarios. On a device used by multiple people (family members, shared work iPad), the Safari session belongs to whoever last logged in. A non-ephemeral OAuth flow authenticates as that user, silently.
Cross-app cookie reuse. The cookie jar includes SSO cookies from other apps (any app that also uses non-ephemeral ASWebAuthenticationSession). An attacker-controlled app that shares the SSO domain can ride those cookies to authenticate as the user.
Spotting it
In the binary or decompiled source:
1
2
3
nm Payload/VulnLabAppiOS.app/VulnLabAppiOS | grep -E 'ASWebAuthenticationSession|prefersEphemeral'
# debug build — code is in the dylib:
nm Payload/VulnLabAppiOS.app/*.dylib | grep -E 'ASWebAuthenticationSession|prefersEphemeral'
If ASWebAuthenticationSession is present but prefersEphemeral is not, the finding is confirmed. The property defaults to false.
Runtime hook to confirm:
1
2
3
4
5
6
7
8
const cls = ObjC.classes.ASWebAuthenticationSession;
Interceptor.attach(cls['- setPrefersEphemeralWebBrowserSession:'].implementation, {
onEnter: function (args) {
// args[2] is the BOOL
const ephemeral = args[2].toInt32();
console.log('[ASWebAuthSession] prefersEphemeral=' + ephemeral);
}
});
If the hook never fires, prefersEphemeralWebBrowserSession was never set - it remains false.
Trigger the OAuth flow, watch the hook. A legitimate app that locks in ephemeral mode logs prefersEphemeral=1.
The fix
1
2
3
4
5
6
7
8
9
10
11
let session = ASWebAuthenticationSession(
url: authURL,
callbackURLScheme: "vulnlaboauth"
) { callbackURL, error in
// handle callback
}
// FIX: force a fresh, isolated browser session for each OAuth flow
session.prefersEphemeralWebBrowserSession = true
session.presentationContextProvider = self
session.start()
With prefersEphemeralWebBrowserSession = true:
- No cookies from Safari or other apps are available to the IdP.
- The user must authenticate every time (no silent re-auth from an existing session).
- The session is torn down after the redirect - no residual cookies remain.
The UX tradeoff is the user sees a login form every time. For security-sensitive apps (banking, healthcare, anything with privileged data), this is correct. Apps that want SSO convenience should at minimum validate the resulting token’s sub claim against the logged-in user and reject mismatches.
The callback scheme side of the same bug
VulnLabAppiOS’s callback scheme (vulnlaboauth://) is a custom URL scheme. Any other app can register the same scheme and intercept the authorization code. This is the scheme-hijacking bug (covered here) compounded with the non-ephemeral session issue - silent authentication feeds directly into a stealable callback.
The correct fix for production:
prefersEphemeralWebBrowserSession = true- Migrate the callback to a Universal Link (HTTPS associated domain), not a custom scheme
Closing
prefersEphemeralWebBrowserSession is a one-line fix. The default is the wrong value for any app where re-authentication should require user action. Check every ASWebAuthenticationSession instantiation in the codebase for the property - if it is absent or false, the IdP’s session handling decides whether authentication is silent, and that is not a decision the app should be leaving to the server.
Happy Hacking !!
