aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.45k stars 2.13k forks source link

WebView call to federatedSignIn results in unsupported_code_challenge_method #10526

Closed SaileshKumar closed 2 years ago

SaileshKumar commented 2 years ago

Before opening, please confirm:

JavaScript Framework

React, React Native

Amplify APIs

Authentication

Amplify Categories

auth

Environment information

Native: # Put output below this line ``` npx envinfo --system --binaries --browsers --npmPackages --duplicates --npmGlobalPackages System: OS: macOS 12.5.1 CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Memory: 999.65 MB / 32.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.8.0 - /usr/local/bin/node Yarn: 1.22.17 - /usr/local/bin/yarn npm: 8.18.0 - /usr/local/bin/npm Browsers: Chrome: 106.0.5249.119 Firefox: 101.0.1 Safari: 15.6.1 npmPackages: @babel/core: ^7.12.9 => 7.19.1 HelloWorld: 0.0.1 expo: ~46.0.9 => 46.0.10 expo-status-bar: ~1.4.0 => 1.4.0 hermes-inspector-msggen: 1.0.0 react: 18.0.0 => 18.0.0 react-native: 0.69.5 => 0.69.5 react-native-webview: ^11.23.1 => 11.23.1 npmGlobalPackages: @aws-amplify/cli: 8.0.3 expo-cli: 6.0.5 npm: 8.18.0 react-native-cli: 2.0.1 ``` Web: ``` npx envinfo --system --binaries --browsers --npmPackages --duplicates --npmGlobalPackages System: OS: macOS 12.5.1 CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Memory: 983.36 MB / 32.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.8.0 - /usr/local/bin/node Yarn: 1.22.17 - /usr/local/bin/yarn npm: 8.18.0 - /usr/local/bin/npm Browsers: Chrome: 106.0.5249.119 Firefox: 101.0.1 Safari: 15.6.1 npmPackages: @apollo/client: ^3.5.10 => 3.6.9 @apollo/client/cache: undefined () @apollo/client/core: undefined () @apollo/client/errors: undefined () @apollo/client/link/batch: undefined () @apollo/client/link/batch-http: undefined () @apollo/client/link/context: undefined () @apollo/client/link/core: undefined () @apollo/client/link/error: undefined () @apollo/client/link/http: undefined () @apollo/client/link/persisted-queries: undefined () @apollo/client/link/retry: undefined () @apollo/client/link/schema: undefined () @apollo/client/link/subscriptions: undefined () @apollo/client/link/utils: undefined () @apollo/client/link/ws: undefined () @apollo/client/react: undefined () @apollo/client/react/components: undefined () @apollo/client/react/context: undefined () @apollo/client/react/hoc: undefined () @apollo/client/react/hooks: undefined () @apollo/client/react/parser: undefined () @apollo/client/react/ssr: undefined () @apollo/client/testing: undefined () @apollo/client/testing/core: undefined () @apollo/client/utilities: undefined () @apollo/client/utilities/globals: undefined () @aws-amplify/cli: ^7.6.8 => 7.6.26 @cypress/angular: 0.0.0-development @cypress/mount-utils: 0.0.0-development @cypress/react: 0.0.0-development @cypress/react18: 0.0.0-development @cypress/vue: 0.0.0-development @cypress/vue2: 0.0.0-development @daily-co/daily-js: ^0.29.0 => 0.29.0 @daily-co/daily-react-hooks: ^0.3.0 => 0.3.0 @date-io/date-fns: ^1.3.13 => 1.3.13 @emotion/styled: ^11.9.3 => 11.10.4 @hookform/resolvers: ^2.9.7 => 2.9.7 @hookform/resolvers/ajv: 1.0.0 @hookform/resolvers/class-validator: 1.0.0 @hookform/resolvers/computed-types: 1.0.0 @hookform/resolvers/io-ts: 1.0.0 @hookform/resolvers/joi: 1.0.0 @hookform/resolvers/nope: 1.0.0 @hookform/resolvers/superstruct: 1.0.0 @hookform/resolvers/typanion: 1.0.0 @hookform/resolvers/vest: 1.0.0 @hookform/resolvers/yup: 1.0.0 @hookform/resolvers/zod: 1.0.0 @material-ui/core: ^4.11.4 => 4.12.4 @material-ui/icons: ^4.11.2 => 4.11.3 @material-ui/pickers: ^3.3.10 => 3.3.10 @material-ui/styles: ^4.11.4 => 4.11.5 @mui/material: ^5.8.5 => 5.10.4 @mux/videojs-kit: ^0.11.1 => 0.11.1 @reduxjs/toolkit: ^1.7.1 => 1.8.5 @reduxjs/toolkit-query: 1.0.0 @reduxjs/toolkit-query-react: 1.0.0 @supercharge/promise-pool: ^2.1.0 => 2.3.2 @testing-library/cypress: ^8.0.3 => 8.0.3 @testing-library/jest-dom: ^5.11.4 => 5.16.5 @testing-library/react: ^11.1.0 => 11.2.7 @testing-library/user-event: ^14.2.0 => 14.4.3 HelloWorld: 0.0.1 antd: ^4.18.3 => 4.23.0 apollo-link-error: ^1.1.13 => 1.1.13 appsync-client: ^2.13.0 => 2.15.0 aws-amplify: ^4.3.12 => 4.3.35 aws-appsync: ^4.1.5 => 4.1.7 aws-sdk: ^2.1173.0 => 2.1212.0 (2.1084.0) chalk: ^4.1.2 => 4.1.2 (2.4.2, 3.0.0) copy-to-clipboard: ^3.3.1 => 3.3.2 create-polyfill-service-url: ^2.2.6 => 2.2.6 cypress: 10.6.0 => 10.6.0 date-fns: ^2.28.0 => 2.28.0 (2.27.0) es6-promise: ^4.2.8 => 4.2.8 flow-bin: ^0.148.0 => 0.148.0 graphql: ^16.3.0 => 16.6.0 (14.7.0, 15.8.0) graphql-tag: ^2.12.6 => 2.12.6 (2.10.1) hermes-inspector-msggen: 1.0.0 hls.js: ^1.1.3 => 1.2.1 husky: >=6 => 8.0.1 jest-styled-components: ^7.1.1 => 7.1.1 lint-staged: >=10 => 13.0.3 mixpanel-browser: ^2.45.0 => 2.45.0 moment: ^2.29.1 => 2.29.4 moment-timezone: ^0.5.33 => 0.5.37 (0.5.27) mux-embed: ^4.4.4 => 4.13.3 new-plugin-package: 1.0.0 paga-collect: ^1.1.6 => 1.1.6 prettier: ^2.6.2 => 2.7.1 (2.3.2, 1.19.1) react: ^17.0.2 => 17.0.2 react-currency-format: ^1.1.0 => 1.1.0 react-datepicker: ^4.8.0 => 4.8.0 react-device-detect: ^2.2.2 => 2.2.2 react-dom: ^17.0.2 => 17.0.2 react-easy-crop: ^3.5.2 => 3.5.3 react-error-boundary: ^3.1.4 => 3.1.4 react-error-overlay: 6.0.9 => 6.0.9 (6.0.11) react-feather: ^2.0.10 => 2.0.10 react-file-drop: ^3.1.3 => 3.1.5 react-google-button: ^0.7.2 => 0.7.2 react-google-calendar-api: ^1.5.1 => 1.5.2 react-helmet: ^6.1.0 => 6.1.0 react-helmet-async: ^1.3.0 => 1.3.0 react-hook-form: ^7.34.2 => 7.34.2 react-hotjar: ^5.1.0 => 5.1.0 react-json-view: ^1.21.3 => 1.21.3 react-linkify: ^1.0.0-alpha => 1.0.0-alpha react-loading-skeleton: ^3.1.0 => 3.1.0 react-native: ^0.64.0 => 0.64.3 react-player: ^2.9.0 => 2.10.1 react-redux: ^7.2.4 => 7.2.8 react-router-dom: ^6.4.1 => 6.4.1 react-scripts: ^4.0.3 => 4.0.3 react-select: ^5.3.2 => 5.4.0 react-select-event: ^5.5.0 => 5.5.1 react-social-icons: ^5.7.0 => 5.14.0 recoil: ^0.7.5 => 0.7.5 redux-devtools-extension: ^2.13.9 => 2.13.9 request: ^2.88.2 => 2.88.2 stream-chat: ^4.2.0 => 4.4.3 stream-chat-react: ^6.12.0 => 6.13.0 stripe: ^8.198.0 => 8.222.0 styled-components: ^5.3.1 => 5.3.5 styled-components/macro: undefined () styled-components/native: undefined () styled-components/primitives: undefined () url-loader: ^4.1.1 => 4.1.1 web-vitals: ^1.0.1 => 1.1.2 yup: ^0.32.11 => 0.32.11 npmGlobalPackages: @aws-amplify/cli: 8.0.3 expo-cli: 6.0.5 npm: 8.18.0 react-native-cli: 2.0.1 ```

Describe the bug

We have a setup like this:

React Web app hosted by Amplify React Native mobile app that's just a webview wrapper around the web app.

The mobile app is basically a "dummy renderer", and the web app handles all the business logic and UI to make it feel mobile. This was basically our quickest path to getting a mobile beta app working.

So far everything has worked except for specifically Auth.federatedSignIn. Logging in with email + password works fine, but initiating a sign in flow with Google or Facebook results in an error unsupported_code_challenge_method.

Is there a way to get federatedSignIn to work from webview?

Expected behavior

  1. Initiating SSO flow via federatedSignIn should also be able to work, like the regular Auth.signIn

Reproduction steps

  1. Set up web app with working Auth.federatedSignIn
  2. Access web app from React Native WebView
  3. From RN, initiate federatedSignIn flow
  4. See error

Code Snippet

// Put your code below this line.

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

tannerabread commented 2 years ago

@SaileshKumar What platform are you experiencing this on? Android or iOS? And on emulators or physical devices? Or all of the above

Does the federatedSignIn with webview leave the app to open the browser or does it open an in-app browser?

Can you share how you are calling the method

SaileshKumar commented 2 years ago

It's any device (physical or emulator), and currently it's not opening any browser at all (we don't have any in-app browser built btw, it's basically just the WebView).

Web app triggers the flow like this:

<SocialLoginButton onClick={() => Auth.federatedSignIn({ provider: "Google" })}>

Mobile app is just a WebView like this:

<WebView 
        allowsBackForwardNavigationGestures
        source={{...currentURL && { uri: currentURL.toString() }}} 
        onNavigationStateChange={(state) => {
          console.log(state)
        }}
      />

In the logs I see that when I click Auth.federatedSignIn, it navigates first to:

https:///oauth2/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F&response_type=code&client_id=&identity_provider=Google&scope=phone%20email%20openid%20profile%20aws.cognito.signin.user.admin&state=sYIxaoiSwQdjGYMauBa7obARYP6nQUdg&code_challenge=-DqDNRqklJDIXmSHY7q0rfAWKm1CGUs7yqxq03DVUQE&code_challenge_method=S256/&ruso-app=0

Then it comes back to

http://localhost:3000/?error_description=unsupported_code_challenge_method&state=sYIxaoiSwQdjGYMauBa7obARYP6nQUdg&error=invalid_request/&ruso-app=0

I think this has to do with that "&ruso-app=0" parameter. This is how our native app is telling the web app that it's from a native context, and maybe it's not playing nicely with the federatedSignIn code?

SaileshKumar commented 2 years ago

EDITED with new findings:

Ok the issue is that the URL constructor in my native code was adding a trailing slash to so the url generated from amplify ended with:

&code_challenge_method=S256/

instead of

&code_challenge_method=S256

Fixing this fixed the problem of the unsupported_code_challenge.

tannerabread commented 2 years ago

@SaileshKumar Did this completely unblock your issue?

SaileshKumar commented 2 years ago

@tannerabread

No not really, but from Amplify's perspective yes.

Currently I'm getting a disallowed user_agent from Google, I think it's not happy that the sign on is coming from a WebView and not the native app's browser. Was trying to see if there was a way to just pass the userAgent through WebView, and this removed the error but didn't result in a successful flow. Happy to provide more info here if you think there's a way to make it work successfully through WebView...

I tried detecting that it was our amplify auth url, and then opening in the native browser. This kinda works, but the problem is then after the user signs in, they're redirected to our site in the browser, not back in the app. Might have to explore building some kind of deeplinking or messaging system to re-open the app with the correct token.

tannerabread commented 2 years ago

I have not tried this yet with webview but I did just confirm that in a vanilla RN app federatedSignIn works with or without the in-app browser so I'm thinking it's something with the redirect. I will try to reproduce with webview soon.

What does your redirectSignIn/redirectSignOut look like in your aws-exports? I couldn't find in the webview docs, but does it allow you to make a custom url from the package or do you have to go the deeplinking route?

tannerabread commented 2 years ago

@SaileshKumar I tried it with webview and got to where you are at with the disallowed_useragent.

To get it working, you just need to add the userAgent property to your WebView component.

<WebView 
  allowsBackForwardNavigationGestures
  source={{ uri: 'http://localhost:3000' }}
  onNavigationStateChange={(state) => console.log(state)}
  userAgent={'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36'}
/>

Also just a note, GitHub copilot actually filled in the details for the userAgent so I'm not too sure if the versions will be compatible for you.

This worked in my testing, let me know if it works for you!

SaileshKumar commented 2 years ago

I had tried this, but maybe it was incompatible versions. It removed the disallowed_user_agent but I got into a weird state where it was doing the amplify redirect flow:

1) Our amplify auth url 2) Google sign in ---- this is where it diverged 3) Went to a youtube link like this: https://accounts.youtube.com/accounts/CheckConnection? 4) Then I got a blank screen with a console log error:

(m=base:107 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('[https://accounts.google.com ](https://accounts.google.com/)') does not match the recipient window's origin ('[https://accounts.youtube.com ](https://accounts.youtube.com/)').

This was from an iOS simulator. I will test it on other devices to see...

tannerabread commented 2 years ago

Mine was also on an iOS simulator. I realize that the userAgent shows an android but it still worked for me on iOS simulator. Check out my React app and the corresponding React Native Webview app to see how I configured these apps.

I'm not positive on this but it looks like a problem with google's MFA redirecting you to youtube to verify your identity and then maybe running into issues with the redirect there.

SaileshKumar commented 2 years ago

Hmm interesting. Yea it's weird if I click the amplify generated link in browser, everything works fine and I redirect back to my web app authenticated. I hit this youtube flow only from WebView

tannerabread commented 2 years ago

It sounds like something with the google MFA to me, and maybe it's picking up that you're on an iOS device and trying to send you to Google Prompts to authenticate. Check out more on google's 2FA here

I think it's safe to say though that this isn't an amplify problem and that it is possible to do a flow like this with Amplify/React -> React Native/WebView

tannerabread commented 2 years ago

I am going to close this issue as I think we are passed the Amplify block. React Native with WebView should be supported so please open another issue if you experience problems elsewhere so that we can better assist you.

We also have a discord server of Amplify developers and enthusiasts if you are interested.

Thank you!