Authentication in the UK Travel Recommendation app is built on JSON Web Tokens. When a user logs in, the Django REST backend issues a short-lived access token and a longer-lived refresh token. The frontend stores both tokens inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/viet2811/uk-travel-recommendation/llms.txt
Use this file to discover all available pages before exploring further.
expo-secure-store — an encrypted, hardware-backed keychain — so they survive app restarts but are never exposed to JavaScript’s global scope or written to plain AsyncStorage. An Axios response interceptor silently refreshes the access token on any 401 Unauthorized response, keeping users logged in without interruption, and the AuthContext exposes the full auth lifecycle to every component in the tree.
Overview
JWT Access Token
Short-lived token sent as
Authorization: Bearer <token> on every authenticated request. Stored in expo-secure-store under the key accessToken.JWT Refresh Token
Longer-lived token used exclusively to mint new access tokens. Stored in
expo-secure-store under the key refreshToken. Never sent to any endpoint other than user/token/refresh.Automatic Refresh
The Axios response interceptor intercepts every
401 response, attempts a silent token refresh, and retries the original request — fully transparent to calling code.Session Restoration
On every cold start a
useEffect reads accessToken from SecureStore. If a token exists, isAuthenticated is set to true without requiring the user to log in again.AuthContext API
TheAuthContext is the single source of truth for authentication state. It is provided at the root of the app via <AuthProvider> and consumed anywhere in the tree via the useAuth() hook.
| Property | Type | Description |
|---|---|---|
isAuthenticated | boolean | true when a valid access token is present in SecureStore. |
isLoading | boolean | true while the initial SecureStore check is in progress on app open. Prevents a flash of the login screen for returning users. |
login | (username, password) => Promise<{ success, message? }> | Submits credentials to the token endpoint. Returns { success: true } on success or { success: false, message: '...' } on failure. |
logout | () => Promise<void> | Clears all tokens, AsyncStorage entries, and the React Query cache, then sets isAuthenticated to false. |
Login Flow
User submits credentials
The
LoginScreen collects a username and password and calls login(username, password) from useAuth().POST to the token endpoint
login() sends a POST request to /user/token/ with the credentials in the request body:Tokens stored in SecureStore
On a
200 response the backend returns both tokens. The context stores them immediately:Automatic Token Refresh
The Axios instance defined inapi/axios.ts registers a response interceptor that handles expired access tokens transparently. You never need to call the refresh endpoint manually in feature code.
- The
_retryflag on the original request config is set totruebefore the refresh attempt, preventing an infinite retry loop if the refresh endpoint itself returns401. - If no
refreshTokenis found in SecureStore at the time of a401,logout()is called immediately without attempting a refresh. - If the refresh call throws,
logout()is called to clear all credentials. - On a successful refresh, the new access token is stored in SecureStore and the
Authorizationheader is updated on the original request before it is retried.
Session Restoration
When the app is opened after being closed (cold start or background eviction),AuthProvider runs a useEffect that checks SecureStore before rendering any navigation:
isLoading is true the app renders a splash/loading indicator.
Logout
Callinglogout() performs a full local sign-out without a network round-trip:
Clear AsyncStorage
await AsyncStorage.clear() removes the geo filter preference and any other AsyncStorage keys written by the app, ensuring the next user (or a re-login by the same user) starts from a clean state.Reset the React Query cache
queryClient.clear() removes all cached server state — recommendations, liked history, search results — preventing stale data from a previous session being briefly visible after a new login.Using the Hook
Any component that needs to react to auth state or trigger auth actions should use theuseAuth() hook:
useAuth() must be called inside a component that is a descendant of <AuthProvider>. Calling it outside the provider tree throws a runtime error: useAuth must be used within AuthProvider. Ensure <AuthProvider> wraps the root navigator in App.tsx (or _layout.tsx).