Amplify-js: App Store rejects binary because AWS Amplify launches Safari for Facebook Login

Created on 30 Mar 2020  路  11Comments  路  Source: aws-amplify/amplify-js

Which Category is your question related to?
Amplify Facebook Login Integration

* What AWS Services are you utilizing? *
Amplify, Auth

** Environment:
React Native 0.61

* App Store Rejection message *
`Guideline 4.0 - Design

We noticed that the user is taken to Safari to sign in or register for an account, which provides a poor user experience.

Specifically, your app exit to Safari in order to sign in with Facebook.

Next Steps

To resolve this issue, please revise your app to enable users to sign in or register for an account in the app.

We recommend implementing the Safari View Controller API to display web content within your app. The Safari View Controller allows the display of a URL and inspection of the certificate from an embedded browser in an app so that customers can verify the webpage URL and SSL certificate to confirm they are entering their sign in credentials into a legitimate page.
`

* Code snippets with App *
const facebookResult = await Auth.federatedSignIn({provider: 'Facebook'});

* Code snippet within Amplify - urlOpener.native.ts*
`import { Linking } from 'react-native';

export const launchUri = url => Linking.openURL(url);`

The urlOpener code above launches Safari on iOS causing App Store rejection of the binary. One possible solution is proposed here https://stackoverflow.com/questions/58503905/implement-safari-view-controller-in-react-native.

Feedback React Native bug

Most helpful comment

Following this issue. Any update on this ?
We can not deploy our app in production for ios because of this and using react-native-inappbrowser-reborn does not seem like a viable solution to us, especially considering the different hacks that need to be implemented.

We're seriously considering moving away from amplify as we have been facing several unresolved issues lately.

All 11 comments

Seems related to #4950

*Partial workaround to this issue *

Utilizes the https://github.com/proyecto26/react-native-inappbrowser library.

`import Amplify from 'aws-amplify';

import config from './aws-exports.js';

import InAppBrowser from 'react-native-inappbrowser-reborn'`

`const urlOpener = async (url, redirectUrl) => {

//const deepLink = getDeepLink("");

if (await InAppBrowser.isAvailable()) {  

    return InAppBrowser.open(url);

} else {

    return Linking.openURL(url);

}

}`

config.oauth.urlOpener = Platform.OS === 'ios' ? urlOpener : null; Amplify.configure(config)
`

The urlOpener function above will open a browser in SFSafariViewController on iOS as a modal and pass the Cognito user but will not close the modal. To close the model, one could listen on the Hub events e.g

`authListener(data) {

    switch (data.payload.event) {

        case 'signIn':

            if (this.isFacebookLogin(data.payload.data)) {
                Auth.currentAuthenticatedUser({
                    bypassCache: false  // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
                }).then(user => { 
                    this.setState({ loggedUser: user.attributes, loggedIn: true });
                }).catch(err => console.log("Facebook login error mode: %j", err));

                // Dismiss Facebook Modal
                if (Platform.OS === "ios") {
                    InAppBrowser.close();
                }

            } else {
                const { attributes } = data.payload.data;
                this.setState({ loggedIn: true, loggedUser: attributes });
            }
            break;
        case 'signUp':
            //logger.error('user signed up');
            //this.setState({ loggedIn: true });
            break;
        case 'signOut':
            //console.log('user signed out: %j', data.payload.data);
            this.setState({ loggedIn: false, loggedUser: null });

            break;

        case 'oAuthSignOut':
            console.log('default auth: %j', data.payload.data);
            // Dismiss Facebook Modal
            if (Platform.OS === "ios") {
                InAppBrowser.close();
            }
            this.setState({ loggedIn: false, loggedUser: null });
            break;
        default:
            break;
    }
}`

However signOut does not close the inApp Browser Modal hence this is a partial workaround. Look forward to feedback from the Amplify team on how to improve and complete this workaround as they hopefully work on a more complete solution

Setting a timeout handles the signOut issue above but this definitely a hack...but at least gives a workable solution.

`
case 'oAuthSignOut':

            // console.log('default auth: %j', data.payload.data);
            // Dismiss Facebook Modal

            if (Platform.OS === "ios") {

                setTimeout(function() {

                    InAppBrowser.close();

                }, 2000);

            }

            this.setState({ loggedIn: false, loggedUser: null });

            break;

`

a question, if i use https://github.com/facebook/react-native-fbsdk answer with

Auth.federatedSignIn('facebook', { token: accessToken, expires_at }, user)
                .then(credentials => {
                    console.log(credentials);
                });

Could it work to replace react-native fb web login?

Ok I've tried it and it does not work

Following this issue. Any update on this ?
We can not deploy our app in production for ios because of this and using react-native-inappbrowser-reborn does not seem like a viable solution to us, especially considering the different hacks that need to be implemented.

We're seriously considering moving away from amplify as we have been facing several unresolved issues lately.

@ThomasCarca @Manzia

Thanks for your patience. We are looking into the issue this week and evaluating the use of in-app browser libraries. We will provide an update soon once we have validated an approach.

@Manzia @ThomasCarca

The PR: aws-amplify/docs#2015 will update our documentation with details of how to set up the urlOpener to use an in-app browser for federated signIn both for Expo and RN CLI apps. It is currently being reviewed and will be merged today.

By default, Amplify will opened the Cognito Hosted UI in Safari/Chrome, but you can override that behavior by providing a custom urlOpener:

import React, { useEffect, useState } from 'react';
import { Button, Linking, Text, View } from 'react-native';
import Amplify, { Auth, Hub } from 'aws-amplify';
import InAppBrowser from 'react-native-inappbrowser-reborn';
import awsconfig from './aws-exports';

async function urlOpener(url, redirectUrl) {
  await InAppBrowser.isAvailable();
  const { type, url: newUrl } = await InAppBrowser.openAuth(url, redirectUrl, {
    showTitle: false,
    enableUrlBarHiding: true,
    enableDefaultShare: false,
    ephemeralWebSession: false,
  });

  if (type === 'success') {
    Linking.openURL(newUrl);
  }
}

Amplify.configure({
  ...awsconfig,
  oauth: {
    ...awsconfig.oauth,
    urlOpener,
  },
});

function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    Hub.listen('auth', ({ payload: { event, data } }) => {
      switch (event) {
        case 'signIn':
        case 'cognitoHostedUI':
          getUser().then(userData => setUser(userData));
          break;
        case 'signOut':
          setUser(null);
          break;
        case 'signIn_failure':
        case 'cognitoHostedUI_failure':
          console.log('Sign in failure', data);
          break;
      }
    });

    getUser().then(userData => setUser(userData));
  }, []);

  function getUser() {
    return Auth.currentAuthenticatedUser()
      .then(userData => userData)
      .catch(() => console.log('Not signed in'));
  }

  return (
    <View>
      <Text>User: {user ? JSON.stringify(user.attributes) : 'None'}</Text>
      {user ? (
        <Button title="Sign Out" onPress={() => Auth.signOut()} />
      ) : (
        <Button title="Federated Sign In" onPress={() => Auth.federatedSignIn()} />
      )}
    </View>
  );
}

export default App;

Please let us know if this resolves your issue.

Hello @Ashish-Nanda ,

Thanks for your post. It was helpful for me.
I'm now able to social signIn on React Native with Android and iOS, following this tutorial of Nader Dabit.

For future user, be sure to enable deep linking on iOS by adding the code to your appDelegate.m, here.

@Jobel91 Glad to know that the post was helpful and you were able to get it working.

Closing this issue as the solution above has been validated, and the docs have been updated with the relevant examples: https://docs.amplify.aws/lib/auth/social/q/platform/js#full-samples

Hi @Ashish-Nanda

I'm trying to implement this and am following your post and the one in the official amplify docs but the login still opens in Safari.

The only thing that's different is that I run amplify.configure in my root file and not where I run Auth.federatedSignIn
I'm also running this on the iOS simulator if it makes any difference.

Thank you very much! I also got rejected for opening the authentication in Safari and in a tight deadline so any help is greatly appreciated. Thank you very much!

Edit: Tried a bunch of random things and this seems to work.
Based my solution off of your post and this article.

index.js (root .js file)

import { AppRegistry } from 'react-native'
import App from './src'
import { name as appName } from './app.json'

import { Linking } from 'react-native'
import InAppBrowser from 'react-native-inappbrowser-reborn'

import Amplify from 'aws-amplify'
import config from 'aws-exports'

Amplify.configure({
    ...config,
    oauth: {
        ...config.oauth,
        urlOpener: async (url, redirectUrl) => {
            await InAppBrowser.isAvailable();
            const { type, url: newUrl } = await InAppBrowser.openAuth(url, redirectUrl, {
                showTitle: false,
                enableUrlBarHiding: true,
                enableDefaultShare: false,
                ephemeralWebSession: false,
            });

            if (type === 'success') {
                Linking.openURL(newUrl);
            }
        }
    },
});


AppRegistry.registerComponent(appName, () => App)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

DougWoodCDS picture DougWoodCDS  路  3Comments

guanzo picture guanzo  路  3Comments

ldgarcia picture ldgarcia  路  3Comments

callmekatootie picture callmekatootie  路  3Comments

cosmosof picture cosmosof  路  3Comments