React Native Authentication Flow
Authentication proves who a user is. A sign-in screen, a protected home screen, and a logout button form the core of the experience. The app must remember a signed-in user across restarts, hide private screens from visitors, and keep the login credentials safe. A clear flow handles all three jobs.
The Big Picture
App starts
│
▼
Check for saved token
│
┌──┴────────────┐
│ │
Token found No token
│ │
Home screens Login screens
│ │
│ Sign in succeeds → save token → Home screens
│
Logout → delete token → Login screens
A token is a long random string issued by the server after a successful login. Think of it as a wristband at a festival. Show the wristband at each gate, and staff let you through without asking for your ticket again.
Two Navigation Groups
Split screens into a public group and a private group. Show only one group at a time.
function RootNavigator() {
const { token, loading } = useAuth();
if (loading) return <SplashScreen />;
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{token ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</>
)}
</Stack.Navigator>
);
}
The screen list changes the moment the token appears or disappears. React Navigation moves the user automatically, so no manual redirect is needed.
Build an Auth Context
Share the sign-in state across the app with a context provider. It exposes the token plus signIn and signOut functions.
import * as SecureStore from 'expo-secure-store';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [token, setToken] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const restore = async () => {
const saved = await SecureStore.getItemAsync('token');
setToken(saved);
setLoading(false);
};
restore();
}, []);
const signIn = async (email, password) => {
const res = await fetch('https://example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await res.json();
await SecureStore.setItemAsync('token', data.token);
setToken(data.token);
};
const signOut = async () => {
await SecureStore.deleteItemAsync('token');
setToken(null);
};
return (
<AuthContext.Provider value={{ token, loading, signIn, signOut }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);
Store Tokens Securely
AsyncStorage keeps data as plain text, which suits settings and cached lists. Tokens deserve stronger protection. expo-secure-store saves values in the iOS Keychain and the Android Keystore, both encrypted by the operating system.
Data Type Best Storage ────────────────────────────────────────── Theme, language AsyncStorage Cached article list AsyncStorage Login token SecureStore Refresh token SecureStore
The Login Screen
function LoginScreen() {
const { signIn } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleLogin = async () => {
try {
await signIn(email, password);
} catch (e) {
setError('Email or password is incorrect');
}
};
return (
<View style={{ padding: 20 }}>
<TextInput placeholder="Email" value={email} onChangeText={setEmail} autoCapitalize="none" />
<TextInput placeholder="Password" value={password} onChangeText={setPassword} secureTextEntry />
{error ? <Text style={{ color: 'red' }}>{error}</Text> : null}
<Button title="Sign In" onPress={handleLogin} />
</View>
);
}
Send the Token with Requests
Attach the token to the header of every private request. The server reads it and identifies the user.
fetch('https://example.com/profile', {
headers: { Authorization: `Bearer ${token}` },
});
Handle Expired Tokens
Tokens expire after a set time. A server reply with status 401 signals an expired or invalid token. Catch that status, clear the saved token, and return the user to the login screen. Many apps also use a refresh token to request a new token silently.
Security Checklist
- Send credentials over HTTPS only
- Store tokens in SecureStore, never in plain storage
- Never print tokens or passwords to logs
- Keep secret keys on the server, never inside app code
- Show generic error text that does not reveal which field was wrong
- Consider trusted providers such as Firebase Auth or OAuth sign-in for added safety
Summary
An authentication flow checks for a saved token at startup, shows public screens to visitors, and shows private screens to signed-in users. Build an auth context, keep tokens in secure storage, send them in request headers, and handle expiry by returning users to the login screen.
