Authentication flow not working well in standalone app

Please provide the following:

  1. SDK Version: 35
  2. Platforms(Android/iOS/web/all): Android

My app works perfectly within the expo client. however when i install the build apk and open the app, I am not redirected to auth route. Instead the app goes directly to the home screen and is unable to fetch data from the server because there is no valid token.

a simplified segment of my code goes like

const token = AsyncStorage.getItem(“token”);
if(token) {
this.props.navigation.navigate(“App”);
}else{
this.props.navigation.navigate(“Auth”);
}

this works perfectly in the Expo client but always navigates to App on the standalone app.

Hi!

Have you debugged this further? For example, it would be helpful to know if the call to AsyncStorage is returning truthy, so that way we know if the problem lies in the navigation or in how you’re storing/retrieving those key value pairs.

Not sure why this would work in the Expo client app though, since it looks like you’re missing the await when calling AsyncStorage.getItem. You might want to give the AsyncStorage docs a quick look - https://docs.expo.io/versions/latest/react-native/asyncstorage/

I hurriedly typed out that question that is why the await was missing.
The app works perfectly in the expo client. I dont mind sharing details to my account so you could run the app yourself.

I have reproduced the problem in a fresh app. I created a new app with expo init. Selected the tab-template so I could get some basic scaffolding. Then proceeded to add an authentication stack to the switchNavigator

AuthNavigator.js

import React from "react";
import { View, Text } from "react-native";
import { createStackNavigator } from "react-navigation-stack";

function loginScreen(props){
    return (
        <View style={{ flex:1 }}>
            <Text>Login Screen</Text>
        </View>
    )
}
const AuthNavigator = createStackNavigator({
   login: loginScreen
},{
initialRouteName: "login"});


export default AuthNavigator;

AppNavigator.js

import React from 'react';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';

import MainTabNavigator from './MainTabNavigator';
import AuthNavigator from "./AuthNavigator";

export default createAppContainer(
  createSwitchNavigator({
    // You could add another route here for authentication.
    // Read more at https://reactnavigation.org/docs/en/auth-flow.html
    Auth: AuthNavigator,
    Main: MainTabNavigator,
  },{
    initialRouteName: "Auth"
  })
);

In expo client, this works as it should. I get just the login screen when the app loads. However after building and installing the .apk, the app totally ignores the initialRouteName property of the switchNavigator and loads directly to main,

Please see if you can reproduce this. Desperate for some answers

I tried to reproduce this with this minimal example from the react-navigation docs:

import * as React from "react";
import {
  ActivityIndicator,
  Button,
  StatusBar,
  StyleSheet,
  View,
  AsyncStorage
} from "react-native";

import { createSwitchNavigator, createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";

class SignInScreen extends React.Component {
  static navigationOptions = {
    title: "Please sign in"
  };

  render() {
    return (
      <View style={styles.container}>
        <Button title="Sign in!" onPress={this._signInAsync} />
      </View>
    );
  }

  _signInAsync = async () => {
    await AsyncStorage.setItem("userToken", "abc");
    this.props.navigation.navigate("App");
  };
}

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: "Welcome to the app!"
  };

  render() {
    return (
      <View style={styles.container}>
        <Button title="Show me more of the app" onPress={this._showMoreApp} />
        <Button title="Actually, sign me out :)" onPress={this._signOutAsync} />
      </View>
    );
  }

  _showMoreApp = () => {
    this.props.navigation.navigate("Other");
  };

  _signOutAsync = async () => {
    await AsyncStorage.clear();
    this.props.navigation.navigate("Auth");
  };
}

class OtherScreen extends React.Component {
  static navigationOptions = {
    title: "Lots of features here"
  };

  render() {
    return (
      <View style={styles.container}>
        <Button title="I'm done, sign me out" onPress={this._signOutAsync} />
        <StatusBar barStyle="default" />
      </View>
    );
  }

  _signOutAsync = async () => {
    await AsyncStorage.clear();
    this.props.navigation.navigate("Auth");
  };
}

class AuthLoadingScreen extends React.Component {
  constructor() {
    super();
    this._bootstrapAsync();
  }

  // Fetch the token from storage then navigate to our appropriate place
  _bootstrapAsync = async () => {
    const userToken = await AsyncStorage.getItem("userToken");

    // This will switch to the App screen or Auth screen and this loading
    // screen will be unmounted and thrown away.
    this.props.navigation.navigate(userToken ? "App" : "Auth");
  };

  // Render any loading content that you like here
  render() {
    return (
      <View style={styles.container}>
        <ActivityIndicator />
        <StatusBar barStyle="default" />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center"
  }
});

const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });

export default createAppContainer(
  createSwitchNavigator(
    {
      AuthLoading: AuthLoadingScreen,
      App: AppStack,
      Auth: AuthStack
    },
    {
      initialRouteName: "AuthLoading"
    }
  )
);

And it works in both the Expo client and as a standalone app. Hopefully that working example helps to debug your specific implementation. Also, just in case it’s a versioning issue, here’s my package.json dependencies:

@react-native-community/masked-view”: “0.1.5”,
“expo”: “~36.0.0”,
“react”: “~16.9.0”,
“react-dom”: “~16.9.0”,
“react-native”: “https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz”,
“react-native-gesture-handler”: “~1.5.0”,
“react-native-safe-area-context”: “0.6.0”,
“react-native-screens”: “2.0.0-alpha.12”,
“react-native-web”: “~0.11.7”,
“react-navigation”: “^4.1.1”,
“react-navigation-stack”: “^2.1.1”

2 Likes

My react-navigation is 4.0.10. I tried this same code you used. Had the same issue.

"@expo/samples": "~36.0.0",
"@expo/vector-icons": "~10.0.0",
"@react-navigation/web": "~1.0.0-alpha.9",
"expo": "~36.0.0",
"react": "~16.9.0",
"react-dom": "~16.9.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
"react-native-gesture-handler": "~1.5.0",
"react-native-reanimated": "~1.4.0",
"react-native-screens": "2.0.0-alpha.12",
"react-native-web": "~0.11.7",
"react-navigation": "4.0.10",
"react-navigation-stack": "~1.10.3",
"react-navigation-tabs": "~2.6.2"

I will move to a different computer and match same versions of dependencies as yours and see if its any good. Thanks for your help. Really appreciated.

Built a new app using the same code snippets as you used. Matched the version of dependencies in your app. Everything works perfectly in the expo client and when published. However when i build the android standalone app with expo build:android, then download and install the apk, the switch navigator does not work. The main app gets rendered all the time irrespective of the login status.
In fact, even when i set the initialRouteName property to the login stack, the main app still gets loaded.
I have now tried the process on a different computer and it made no difference (didn’t expect this to work as I know the build process is independent of the computer that i use). Just tried it out of desperation

I eventually got this sorted. The problem was with the app.json. Stripped it down to the required values only and it worked.

Hey, I have the same problem but in iOS standalone app. How did you solve it, can you share it? Is it because of react-navigation? Thanks

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.