expo: [expo-web-browser] Popup being blocked by Desktop Safari when using WebBrowser.openAuthSessionSync
π Bug Report
Summary of Issue
On Expo Web, popups by WebBrowser are being blocked by Desktop Safari. This is likely because of some async calls which prevent Safari from recognizing that the popup was requested via a user interaction.
Environment - output of expo diagnostics & the platform(s) youβre targeting
Expo CLI 3.27.4 environment info:
System:
OS: macOS Mojave 10.14.6
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 12.14.0 - ~/.nvm/versions/v12.14.0/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.13.4 - ~/.nvm/versions/v12.14.0/bin/npm
Watchman: 4.7.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.5.2 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
IDEs:
Android Studio: 3.6 AI-192.7142.36.36.6241897
Xcode: 11.3/11C29 - /usr/bin/xcodebuild
npmPackages:
@expo/webpack-config: ^0.12.29 => 0.12.29
expo: ^38.0.0 => 38.0.10
react: 16.11.0 => 16.11.0
react-dom: 16.11.0 => 16.11.0
react-native: https://github.com/expo/react-native/archive/sdk-38.0.2.tar.gz => 0.62.2
react-native-web: ~0.11.7 => 0.11.7
npmGlobalPackages:
expo-cli: 3.27.4
Expo Workflow: managed
Reproducible Demo
<TouchableWithoutFeedback onPress={() => {
WebBrowser.openAuthSessionAsync('https://www.hackaday.com', 'https://www.itsame.com');
}}>
<View style={{width: 50, height: 50, backgroundColor: 'red'}} />
</TouchableWithoutFeedback>
Steps to Reproduce
Use the snippet above and press the Touchable.
Expected Behavior vs Actual Behavior
Window should open in Desktop Safari just like it does in Chrome. Instead Safari blocks the popup and asks the user to manually confirm.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 2
- Comments: 15 (9 by maintainers)
Commits related to this issue
- [web-browser][web] Create state before open popup Based on https://github.com/expo/expo/issues/10459#issuecomment-720446251 suggestion, this prevents Desktop Safari from blocking opening the popup, a... — committed to sreuter/expo by sreuter 2 years ago
- [web-browser][web] Create state before open popup Based on https://github.com/expo/expo/issues/10459#issuecomment-720446251 suggestion, this prevents Desktop Safari from blocking opening the popup, a... — committed to sreuter/expo by sreuter 2 years ago
- [web-browser][web] Create state before open popup Based on https://github.com/expo/expo/issues/10459#issuecomment-720446251 suggestion, this prevents Desktop Safari from blocking opening the popup, a... — committed to sreuter/expo by sreuter 2 years ago
Iβve found where is the problem: https://github.com/expo/expo/blob/a228d5016823b51289c51046b55985bd3f6822f2/packages/expo-web-browser/src/ExpoWebBrowser.web.ts#L124
Function getStateFromUrlOrGenerateAsync is creating new crypto state if state param is not passed by url param. This is causing blocking popup on desktop Safari. The case why is working with AuthSession.useAuthRequest(β¦) is because it creates internally crypto state and pass it as a url param.
As a fix I would propose moving code responsible for getting/creating crypto state right after window.open call. Iβve test it locally and it works.
For example:
What do you think ?
@brentvatne Basically in order to not being blocked by the browser, we need to use the new secure API called Credential Management: https://developers.google.com/web/fundamentals/security/credential-management/
There is a web-component already implemented this and has a nice demo: https://github.com/pwa-builder/pwa-auth
Is it okay that I start the implementation of this fix?
The fix just landed in expo-web-browser@10.2.1, in case anybody wants to remove their manual workarounds. Thanks again to @scyrych for suggesting the fix in the first place π
@jarvisluong - yup! that would be great.
After digging for a while, I can share some more findings:
AuthSession.startAsync()suffers from the same issueWebBrowser.openBrowserAsync()works perfectly finepromptAsync()internally makes use of the very sameWebBrowser.openAuthSessionAsync():