Auth Flows

Authentication Flows and Conditional Navigation

A signed-out user must not reach screens that need an account, and after signing out, Back must not lead into them again. Both libraries solve this by defining different screens for different states rather than by redirecting. In React Navigation 407,918 you render the screens conditionally:

src/nav/RNAuth.tsx: two sets of screens, chosen by sign-in state (excerpt)TSX
  const [user, setUser] = useState<string | null>(null);
  return (
    <NavigationContainer onStateChange={(s) =>
      console.log('screens:', s?.routes.map((r) => r.name).join(' > '))}>
      <Stack.Navigator>
        {user ? (
          <>
            <Stack.Screen name="Catalog">{/* ...a "My account" button */}</Stack.Screen>
            <Stack.Screen name="Account">
              {() => <Button label={`Sign out ${user}`} onPress={() => setUser(null)} />}
            </Stack.Screen>
          </>
        ) : (
          <Stack.Screen name="SignIn" options={{ title: 'Sign in' }}>

Signing in, opening the account and signing out:

Output of 153
 LOG  screens: Catalog
 LOG  screens: Catalog > Account
 LOG  screens: SignIn

Nothing called navigate to leave SignIn: it stopped existing when user changed, and the navigator showed the first screen of the new set. Signing out removed Catalog and Account, history included.

Expo Router 6,418 expresses the same idea with Stack.Protected (Layouts and Nested Navigators): checkout exists only while guard={user !== null} holds. BookNest's cart sends signed-out users to /sign-in, whose button calls setUser('Ana') (Authentication replaces this with a real sign-in) and then router.replace('/checkout'), which swaps the sign-in screen for the checkout so Back skips it. Tapping Checkout, Sign in as Ana, then Sign out on the checkout screen logged these paths:

Output of 153
 LOG  path: /cart
 LOG  path: /sign-in
 LOG  path: /checkout
 LOG  path: /cart

Signing out flipped the guard, and the router dropped /checkout and its history, landing on the cart. Before Checkout linked to /sign-in, a tap while signed out did nothing: navigation to a guarded route is ignored without an error, so give signed-out users a visible way forward. The server must still check every request.