aws-amplify / amplify-ui

Amplify UI is a collection of accessible, themeable, performant React (and more!) components that can connect directly to the cloud.
https://ui.docs.amplify.aws
Apache License 2.0
797 stars 271 forks source link

@aws-amplify/ui-react-native withAuthenticator and <Authenticator.Provider> works on web but fails on mobile when using expo-router #5175

Open drewboardman opened 4 months ago

drewboardman commented 4 months ago

Before creating a new issue, please confirm:

On which framework/platform are you having an issue?

React Native, iOS, Android, Other

Which UI component?

Authenticator

How is your app built?

expo

What browsers are you seeing the problem on?

iOS (React Native), Android (React Native)

Which region are you seeing the problem in?

local development

Please describe your bug.

Here is a repository I created that contains a reproducible minified example of the issue. There is also a stack overflow thread describing a similar issue, however that issue has logs.

This seems to explicitly be an issue with expo-router's interaction with '@aws-amplify/ui-react-native'. The issue persists with both <Authenticator.Provider> and withAuthenticator.

Issue

To Reproduce

What's the expected behaviour?

The expected behavior is to display the login and authentication screens. I'd also like to see error logs somehow. There are no logs whatsoever.

Help us reproduce the bug!

There are detailed instructions in the README of the linked repo. I also included them at the top of this bug report.

Code Snippet

import FontAwesome from '@expo/vector-icons/FontAwesome';
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect, useState } from 'react';
import { fetchAuthSession } from 'aws-amplify/auth'
import {
  withAuthenticator,
  Authenticator
} from '@aws-amplify/ui-react-native';
import { config } from './amplifyconfigs/main';
import { Amplify } from 'aws-amplify';

import { useColorScheme } from '@/components/useColorScheme';
// import { AuthProvider } from './contexts/AuthContext';
Amplify.configure(config);

export {
  // Catch any errors thrown by the Layout component.
  ErrorBoundary,
} from 'expo-router';

export const unstable_settings = {
  // Ensure that reloading on `/modal` keeps a back button present.
  initialRouteName: '(tabs)',
};

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

function App() {
  const [loaded, error] = useFonts({
    SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
    ...FontAwesome.font,
  });

  // Expo Router uses Error Boundaries to catch errors in the navigation tree.
  useEffect(() => {
    if (error) throw error;
  }, [error]);

  useEffect(() => {
    if (loaded) {
      SplashScreen.hideAsync();
    }
  }, [loaded]);

  if (!loaded) {
    return null;
  }

  return (
    <Authenticator.Provider>
      <Authenticator>
        <RootLayoutNav />
      </Authenticator>
    </Authenticator.Provider>
  );
}

function RootLayoutNav() {
  const colorScheme = useColorScheme();

  return (
    // <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
      </Stack>
    // </ThemeProvider>
  );
}

// export default withAuthenticator(App);
export default App;

Console log output

no logs

Additional information and screenshots

https://imgur.com/a/FtJzuoo

esauerbo commented 4 months ago

Hi @drewboardman thanks for creating issue and for providing a reproduction. We'll look into this and follow up with you.

esauerbo commented 4 months ago

@drewboardman the aws-amplify@6 dependency of the React Native Authenticator unfortunately doesn't support using Expo Go. Documentation for that (under Expo tab). You can still use Expo itself, and build/run the project locally using

// Android
npx expo run:android 

// iOS
npx expo run:ios

I was able to get your sample app running using those commands. Let us know if this resolves your issue.

drewboardman commented 4 months ago

@drewboardman the aws-amplify@6 dependency of the React Native Authenticator unfortunately doesn't support using Expo Go. Documentation for that (under Expo tab). You can still use Expo itself, and build/run the project locally using

// Android
npx expo run:android 

// iOS
npx expo run:ios

I was able to get your sample app running using those commands. Let us know if this resolves your issue.

Interesting 🤔 . Any idea why this seems to only manifest with expo-router? I added the same authenticator to your "sample tutorial" application (the one with the images and emoji) and everything works as expected (with expo go).

Here is the working example: https://github.com/drewboardman/tutorial-with-amplify-working

Also, any idea on where I would find error logs for this issue?

drewboardman commented 4 months ago

@esauerbo I ran npx expo run:ios and I'm still seeing the same issue. Images here: https://imgur.com/a/z7ovBP1

Can you provide more details of how you got this working?

esauerbo commented 4 months ago

Any idea why this seems to only manifest with expo-router?

Because Expo Go isn't officially supported, it may happen to work in some cases but isn't required to work. The "actual" reason would be dependent to the implementation of aws-amplify. Here's a little more information on the decision to drop support. You might also be able to get better answers to specific questions in the Amplify JS repo.

Can you provide more details of how you got this working?

I didn't actually make any changes, just followed your reproduction steps but with npx expo run:ios. It seems like it could be a local configuration issue.

Can you try making a simpler app just using expo router and see if you have the same problem? It seems like the issue is with the app not mounting properly, so I don't believe there would be Authenticator-specific error logs here.

drewboardman commented 4 months ago

It seems like the issue is with the app not mounting properly, so I don't believe there would be Authenticator-specific error logs here.

My main confusion is that the app (even with Expo Go) works correctly when I change the line:

// export default withAuthenticator(App);
export default App;

NOTE: this is in addition to removing the:

    <Authenticator.Provider>
      <Authenticator>
      ...

Is it possibly some versioning issue with our NPM or something? Maybe my xcode or android studio?

Is there some way that I can debug the difference between my development environment and yours? I'm pretty motivated to understand why I'm seeing different results from your npx expo run:ios.

drewboardman commented 4 months ago

@esauerbo I built the project in xcode using the xed ios command from the Expo Documentation. Running the native build provides the same bug, and the following error log output:

Logging Error: Failed to initialize logging system. Log messages may be missing. If this issue persists, try setting IDEPreferLogStreaming=YES in the active scheme actions environment variables.
objc[7457]: Class AKAlertImageURLProvider is implemented in both /Library/Developer/CoreSimulator/Volumes/iOS_21E213/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/AuthKitUI.framework/AuthKitUI (0x132b71798) and /Library/Developer/CoreSimulator/Volumes/iOS_21E213/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit (0x12ef7d508). One of the two will be used. Which one is undefined.
objc[7457]: Class AKBiometricRatchetUtility is implemented in both /Library/Developer/CoreSimulator/Volumes/iOS_21E213/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/AuthKitUI.framework/AuthKitUI (0x132b71810) and /Library/Developer/CoreSimulator/Volumes/iOS_21E213/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit (0x12ef7eb10). One of the two will be used. Which one is undefined.
nw_socket_handle_socket_event [C1.1.1:2] Socket SO_ERROR [61: Connection refused]
nw_socket_handle_socket_event [C1.1.2:2] Socket SO_ERROR [61: Connection refused]
Connection 1: received failure notification
Connection 1: failed to connect 1:61, reason -1
Connection 1: encountered error(1:61)
Task <BDA1763B-128A-44D8-9BFB-221A30E1A097>.<1> HTTP load failed, 0/0 bytes (error code: -1004 [1:61])
Task <BDA1763B-128A-44D8-9BFB-221A30E1A097>.<1> finished with error [-1004] Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo={_kCFStreamErrorCodeKey=61, NSUnderlyingError=0x600000ac82d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1004 "(null)" UserInfo={_NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0[802.11], uses wifi, _kCFStreamErrorCodeKey=61, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <BDA1763B-128A-44D8-9BFB-221A30E1A097>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <BDA1763B-128A-44D8-9BFB-221A30E1A097>.<1>"
), NSLocalizedDescription=Could not connect to the server., NSErrorFailingURLStringKey=http://localhost:8081/status, NSErrorFailingURLKey=http://localhost:8081/status, _kCFStreamErrorDomainKey=1}
No bundle URL present.

Make sure you're running a packager server or have included a .jsbundle file in your application bundle.
reesscot commented 3 months ago

Hi @drewboardman,

Were you able to get this working in an application using regular Expo (not Expo Go)? While you may have gotten things rendering originally with Expo Go, there would likely be a flow that would fail with Go.

One thing you might try would be rendering the Authenticator in a "SignIn" route rather than wrapping the router in the Authenticator. You could use one of the JS api's like getCurrentUser to check if the user is logged in and only render the Authenticator if there is no user.

drewboardman commented 3 months ago

Hi @drewboardman,

Were you able to get this working in an application using regular Expo (not Expo Go)? While you may have gotten things rendering originally with Expo Go, there would likely be a flow that would fail with Go.

One thing you might try would be rendering the Authenticator in a "SignIn" route rather than wrapping the router in the Authenticator. You could use one of the JS api's like getCurrentUser to check if the user is logged in and only render the Authenticator if there is no user.

I'm not able to be at my computer right now to try your suggestion, but I have not been able to reproduce the successful Auth that the other responder mentioned.

reesscot commented 2 months ago

@drewboardman Just following up to see if you've had a chance to try my suggestion using regular Expo.

drewboardman commented 2 months ago

@drewboardman Just following up to see if you've had a chance to try my suggestion using regular Expo.

Hi @reesscot. I've tried regular expo, as well as building the app natively in xcode. All of them produce the same result.

Are you able to run the sample code with regular expo? Can you see the authentication page?