Please provide the following:
- SDK Version: 45
- Platforms: Android 11
I am trying to implemnt an example expo app with react navigation, mobx and authentication. I am using expo’s AuthSession to do the authentication. I start the application on my phone using expo start
. However, every time I start the application I have to log in again. How can I prevent this from happening. Am I doing something wrong or is it because the app is running inside Expo Go?
Code:
My mobx store for sharing the user info and controling authentication. ClientId and auth domain have been removed:
import { action, computed, makeObservable, observable } from "mobx";
import React from "react";
import * as AuthSession from "expo-auth-session";
import { Platform } from "react-native";
import jwtDecode, { JwtPayload } from "jwt-decode";
import { openAuthSessionAsync } from "expo-web-browser";
export class UserStore {
auth0ClientId = "clientId";
authorizationEndpoint = "endpoint";
discoveryDoc: AuthSession.DiscoveryDocument = { authorizationEndpoint: this.authorizationEndpoint };
useProxy: boolean = false;
request: AuthSession.AuthRequest | null = null;
userName: string | null = null;
constructor() {
this.useProxy = Platform.select({ web: false, default: false });
makeObservable(this, {
preperaLogInRequest: action.bound,
logIn: action.bound,
userName: observable,
isLoading: computed,
logOut: action.bound
});
}
get isLoading() {
return this.request === null;
}
async preperaLogInRequest() {
let redirectUri = AuthSession.makeRedirectUri({ useProxy: this.useProxy });
let tmp = await AuthSession.startAsync({authUrl: this.authorizationEndpoint, returnUrl: redirectUri});
console.log("tmp is: " + JSON.stringify(tmp));
this.request = await AuthSession.loadAsync({
redirectUri,
clientId: this.auth0ClientId,
responseType: "id_token",
prompt: AuthSession.Prompt.Login,
scopes: ["openid", "profile"],
extraParams: {
nonce: "nonce",
},
}, this.discoveryDoc);
}
async logIn() {
if (this.request) {
let result = await this.request.promptAsync(this.discoveryDoc, { useProxy: this.useProxy });
if (result) {
if (result.type === "error") {
console.log(
"Authentication error: " +
(result.params.error_description || "something went wrong")
);
return;
}
if (result.type === "success") {
console.log("success");
const jwtToken = result.params.id_token;
const decoded = jwtDecode<JwtPayload & { name: string }>(jwtToken);
const { name } = decoded;
this.userName = name;
}
}
else {
console.log("Something went wrong during log in");
}
}
}
async logOut() {
try {
let res = await openAuthSessionAsync(`https://dev-j3dkt6a3.us.auth0.com/logout?client_id=1wMZHHb2znirLmNv1GHlqDKz5JT9FN7d`, 'redirectUrl');
console.log(JSON.stringify(res))
this.userName = null;
}
catch (error) {
console.log("Failed to log out: " + error);
}
}
}
const userStore = new UserStore();
export const UserStoreContext = React.createContext(userStore);
export const useUserStore = () => React.useContext(UserStoreContext)
My index.tsx:
/**
* If you are not familiar with React Navigation, refer to the "Fundamentals" guide:
* https://reactnavigation.org/docs/getting-started
*
*/
import { FontAwesome } from '@expo/vector-icons';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackScreenProps } from '@react-navigation/native-stack';
import * as React from 'react';
import { ColorSchemeName, Pressable, Platform, Alert, View, Button, StyleSheet, Text } from 'react-native';
import * as AuthSession from "expo-auth-session";
import jwtDecode from "jwt-decode";
import { useState, useEffect } from "react";
import Colors from '../constants/Colors';
import useColorScheme from '../hooks/useColorScheme';
import ModalScreen from '../screens/ModalScreen';
import NotFoundScreen from '../screens/NotFoundScreen';
import TabOneScreen from '../screens/TabOneScreen';
import TabTwoScreen from '../screens/TabTwoScreen';
import { RootStackParamList, RootTabParamList, RootTabScreenProps } from '../types';
import LinkingConfiguration from './LinkingConfiguration';
import { useUserStore } from '../store/UserStore';
import { observer } from 'mobx-react-lite';
export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
return (
<NavigationContainer
linking={LinkingConfiguration}
theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<RootNavigator />
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
title: {
fontSize: 20,
textAlign: "center",
marginTop: 40,
},
});
function LogIn() {
const { logIn } = useUserStore();
return (
<View style={styles.container}>
<Button
title="Log in with Auth0"
onPress={() => logIn()}
/>
</View>
);
}
const Stack = createNativeStackNavigator<RootStackParamList>();
const RootNavigator = observer(() => {
const { userName, isLoading, preperaLogInRequest } = useUserStore();
useEffect(()=>{preperaLogInRequest()}, []);
return (
<>
{
isLoading?
<Stack.Navigator>
{userName ?
<>
<Stack.Screen name="Root" component={BottomTabNavigator} options={{ headerShown: false }} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen name="Modal" component={ModalScreen} />
</Stack.Group>
</> :
<>
<Stack.Screen name="LogIn" component={LogIn} options={{ headerShown: false }} />
</>
}
</Stack.Navigator>:
<Text>Loading</Text>
}
</>
);
})
/**
* A bottom tab navigator displays tab buttons on the bottom of the display to switch screens.
* https://reactnavigation.org/docs/bottom-tab-navigator
*/
const BottomTab = createBottomTabNavigator<RootTabParamList>();
function BottomTabNavigator() {
const colorScheme = useColorScheme();
return (
<BottomTab.Navigator
initialRouteName="TabOne"
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme].tint,
}}>
<BottomTab.Screen
name="TabOne"
component={TabOneScreen}
options={({ navigation }: RootTabScreenProps<'TabOne'>) => ({
title: 'Tab One',
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
headerRight: () => (
<Pressable
onPress={() => navigation.navigate('Modal')}
style={({ pressed }) => ({
opacity: pressed ? 0.5 : 1,
})}>
<FontAwesome
name="info-circle"
size={25}
color={Colors[colorScheme].text}
style={{ marginRight: 15 }}
/>
</Pressable>
),
})}
/>
<BottomTab.Screen
name="TabTwo"
component={TabTwoScreen}
options={{
title: 'Tab Two',
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
/**
* You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
*/
function TabBarIcon(props: {
name: React.ComponentProps<typeof FontAwesome>['name'];
color: string;
}) {
return <FontAwesome size={30} style={{ marginBottom: -3 }} {...props} />;
}