Bug Report
Capacitor Version
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 8.5.1
@capacitor/core: 8.5.1
@capacitor/android: 8.5.1
@capacitor/ios: 8.5.1
Installed Dependencies:
@capacitor/cli: 8.5.1
@capacitor/core: 8.5.1
@capacitor/android: 8.5.1
@capacitor/ios: 8.5.1
[success] iOS looking great! 👌
[success] Android looking great! 👌
Plugin Version
App Name: Expresso
App ID: one.expresso.app
App Version: 1.0.0
Web Dir: dist
OS: darwin Darwin Kernel Version 25.6.0
Installed Dependencies:
@capgo/cli: 8.50.3
@capgo/capacitor-inappbrowser: 8.16.6
@capgo/capacitor-native-navigation: 8.3.1
@capgo/capacitor-navigation-bar: 8.2.7
Latest Dependencies:
@capgo/cli: 8.50.3
@capgo/capacitor-inappbrowser: 8.16.6
@capgo/capacitor-native-navigation: 8.3.1
@capgo/capacitor-navigation-bar: 8.2.7
✅ All dependencies are up to date
context(s)
ManualModel: false
AutoMode: false
CapgoCloud: false
OnPremise: false
Platform(s)
iOS (real device, iPhone 17 Pro, iOS 26). Not applicable to Android.
Current Behavior
Calling InAppBrowser.openSecureWindow(...) on iOS intermittently completes almost immediately with a rejection carrying ASWebAuthenticationSessionErrorCode.canceledLogin (error 1) or presentationContextInvalid (error 3), even though the user never touched or dismissed anything. This is most reliably reproduced right after a cold app launch, before the native view/window hierarchy has fully settled: session.start() returns true, the presentation window is valid, but the completion handler fires with an error roughly 100ms later — every single time on some launches.
Root cause: in openSecureWindow (InAppBrowserPlugin.swift), the ASWebAuthenticationSession instance is only referenced by a local let inside the closure that constructs it:
DispatchQueue.main.async {
let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) {
callbackURL, error in
// ...
}
session.prefersEphemeralWebBrowserSession = prefersEphemeral
session.presentationContextProvider = self
session.start()
}
session.start() is asynchronous and returns immediately. Nothing on self (the plugin instance) retains session after the enclosing closure returns, so ARC is free to deallocate it right away — sometimes before the sheet is even fully presented to the user.
Expected Behavior
openSecureWindow should only report a cancellation when the user actually dismisses the sheet or the request is genuinely invalid — not as a side effect of the session object being deallocated by ARC while still in use.
Retaining the session for the duration of its use fixes this. Add a class property, set it before start() is called, and clear it in the completion handler:
private var currentAuthSession: ASWebAuthenticationSession?
// ...
DispatchQueue.main.async {
let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) {
[weak self] callbackURL, error in
self?.currentAuthSession = nil
// ... existing handling
}
session.prefersEphemeralWebBrowserSession = prefersEphemeral
session.presentationContextProvider = self
self.currentAuthSession = session // must be set before start()
session.start()
}
We applied exactly this fix locally via a pnpm patch on 8.16.6 and it fully resolved the spurious-cancel behavior on the real device — confirmed the same code path with NSLog instrumentation before and after: without the retain, the completion handler fires with an error ~100ms after start() returns on a cold launch; with it, the sheet presents normally and the completion handler only fires on a real user action or a real redirect. Checked the current main branch source on GitHub — the retain is still absent there as of this writing, so this is not yet fixed upstream.
Code Reproduction
Minimal repro shape (this is the exact call our app makes):
import { InAppBrowser } from '@capgo/capacitor-inappbrowser';
async function login() {
const { redirectedUri } = await InAppBrowser.openSecureWindow({
authEndpoint: 'https://your-oidc-provider.example/authorize?...',
redirectUri: 'yourapp://callback',
});
}
Call login() as early as possible after a cold app launch (e.g. from an app-level auto-login effect that fires on mount, before any user interaction) on a real iOS device. It is timing-dependent — it does not reproduce on every single launch, but reproduces often enough in practice that it broke our login flow's cold-boot path outright until we patched it.
Other Technical Details
npm --version output: 11.17.0
node --version output: v24.13.0
pod --version output (iOS issues only): 1.17.0
Additional Context
We are happy to open a PR with the fix above (already validated against 8.16.6 via pnpm patch) if that is useful — just let us know.
Bug Report
Capacitor Version
Plugin Version
context(s)
Platform(s)
iOS (real device, iPhone 17 Pro, iOS 26). Not applicable to Android.
Current Behavior
Calling
InAppBrowser.openSecureWindow(...)on iOS intermittently completes almost immediately with a rejection carryingASWebAuthenticationSessionErrorCode.canceledLogin(error 1) orpresentationContextInvalid(error 3), even though the user never touched or dismissed anything. This is most reliably reproduced right after a cold app launch, before the native view/window hierarchy has fully settled:session.start()returnstrue, the presentation window is valid, but the completion handler fires with an error roughly 100ms later — every single time on some launches.Root cause: in
openSecureWindow(InAppBrowserPlugin.swift), theASWebAuthenticationSessioninstance is only referenced by a localletinside the closure that constructs it:session.start()is asynchronous and returns immediately. Nothing onself(the plugin instance) retainssessionafter the enclosing closure returns, so ARC is free to deallocate it right away — sometimes before the sheet is even fully presented to the user.Expected Behavior
openSecureWindowshould only report a cancellation when the user actually dismisses the sheet or the request is genuinely invalid — not as a side effect of the session object being deallocated by ARC while still in use.Retaining the session for the duration of its use fixes this. Add a class property, set it before
start()is called, and clear it in the completion handler:We applied exactly this fix locally via a
pnpm patchon 8.16.6 and it fully resolved the spurious-cancel behavior on the real device — confirmed the same code path withNSLoginstrumentation before and after: without the retain, the completion handler fires with an error ~100ms afterstart()returns on a cold launch; with it, the sheet presents normally and the completion handler only fires on a real user action or a real redirect. Checked the currentmainbranch source on GitHub — the retain is still absent there as of this writing, so this is not yet fixed upstream.Code Reproduction
Minimal repro shape (this is the exact call our app makes):
Call
login()as early as possible after a cold app launch (e.g. from an app-level auto-login effect that fires on mount, before any user interaction) on a real iOS device. It is timing-dependent — it does not reproduce on every single launch, but reproduces often enough in practice that it broke our login flow's cold-boot path outright until we patched it.Other Technical Details
npm --versionoutput:11.17.0node --versionoutput:v24.13.0pod --versionoutput (iOS issues only):1.17.0Additional Context
We are happy to open a PR with the fix above (already validated against 8.16.6 via
pnpm patch) if that is useful — just let us know.