Implementing Token-Based Authentication in React Native
Course: React Native Training
Topic: Implementing Token-Based Authentication
Course Link: React Native Training at JustAcademy
Demo Link: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to Token-Based Authentication
Token-based authentication is a security mechanism that allows an application to verify a user's identity using a token issued by an authentication server or trusted identity provider. In React Native applications, tokens are commonly used to authenticate requests to APIs after a user logs in.
Instead of sending the user's email address and password with every API request, the application sends an authentication token. The server validates the token and determines whether the request is allowed. Depending on the system design, tokens may contain claims about the user, reference a server-side session, or provide temporary access to specific resources.
Token-based authentication is commonly used in:
- E-commerce applications that display personal orders and account details.
- Education applications that provide access to courses and student dashboards.
- Social applications that manage profiles, posts, and private messages.
- Booking applications that allow users to manage reservations.
- Business applications that connect mobile interfaces to protected APIs.
React Native provides the mobile application interface and networking logic. A backend or identity provider must securely issue, validate, refresh, and revoke tokens according to the chosen authentication architecture.
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand token-based authentication and its purpose.
- Differentiate authentication tokens, access tokens, and refresh tokens.
- Understand JSON Web Tokens (JWTs) and their structure.
- Implement a login request that receives a token from an authentication server.
- Send access tokens with protected API requests.
- Store tokens using appropriate secure-storage mechanisms.
- Handle token expiration and refresh operations.
- Implement logout and clear local authentication state.
- Protect application screens and private API resources.
- Handle authentication errors and network failures.
- Apply security best practices to React Native applications.
3. What Is a Token?
A token is a value used by a system to represent an authentication session, delegated permission, or other security-related information. After a user successfully authenticates, an identity provider or backend may issue a token that the client can present when requesting protected resources.
Tokens allow the application to avoid repeatedly sending the user's password to the API. The server checks the presented token according to its expected format, issuer, audience, expiration, permissions, and validation rules.
3.1 Example of a Token-Based Workflow
- The user enters their login credentials in the React Native application.
- The application sends the credentials to the authentication endpoint over HTTPS.
- The server verifies the credentials.
- If authentication succeeds, the server issues an access token and may also issue a refresh token.
- The application stores the tokens using the mechanisms recommended for that authentication system.
- The application sends the access token with requests to protected API endpoints.
- The server validates the token before returning protected data.
- When the access token expires, the application may request a new one using the supported refresh mechanism.
- When the user logs out, the application clears its local authentication state and invokes any server-side session or token-revocation process that the system supports.
4. Understanding the Different Types of Tokens
| Token Type |
Purpose |
Typical Usage |
| Access token |
Provides temporary access to protected resources. |
Sent with authorized API requests. |
| Refresh token |
Allows the client to obtain a new access token without repeating the full login process, when supported. |
Submitted to a trusted token endpoint. |
| ID token |
Provides information about the authentication event and user identity in OpenID Connect. |
Consumed by the client or relying party according to the protocol. |
| Session identifier |
References a session maintained by a server. |
Presented as a cookie or another session credential. |
| Verification token |
Supports a limited-purpose action such as email verification or password reset. |
Used only for its intended workflow. |
These tokens are not interchangeable. For example, an OpenID Connect ID token is intended to communicate authentication information to the client; it should not automatically be used as an API access token. The API must receive the token type it is designed to validate.
5. Understanding Access Tokens
An access token is a credential presented to a protected resource server. It generally has a limited lifetime and may represent permissions, scopes, or access granted to a particular application or user.
5.1 Important Access Token Properties
- Limited lifetime: Access tokens should have an expiration policy appropriate to the application's risk level.
- Resource-specific use: A token should only be accepted by the intended API or resource server.
- Authorization: The server must check the token's permissions and the user's access to the requested resource.
- Secure transmission: Send tokens over HTTPS and avoid including them in URLs.
- Limited exposure: Keep tokens out of logs, analytics events, crash reports, and error messages.
5.2 Authorization Header Example
GET /api/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer ACCESS_TOKEN
The text ACCESS_TOKEN is a placeholder, not a working credential. The application should obtain the real token from its configured authentication system and send it only to the intended resource server.
6. Understanding Refresh Tokens
A refresh token is a credential that may be used to obtain a replacement access token after the current access token expires. It is usually more sensitive than a short-lived access token because it can provide access to a continuing session.
6.1 Refresh Token Workflow
- The user signs in successfully.
- The authentication service issues an access token and, if supported, a refresh token.
- The application uses the access token to call protected APIs.
- The access token expires or the API indicates that it is no longer valid.
- The application sends the refresh token to the designated token endpoint using the provider's documented procedure.
- The server validates the refresh token and its session status.
- If valid, the server returns a new access token and may rotate the refresh token.
- If the refresh operation fails, the application clears invalid local authentication state and asks the user to sign in again when appropriate.
6.2 Refresh Token Security
- Never expose refresh tokens in application logs or URLs.
- Store refresh tokens in platform-appropriate secure storage if the client must persist them.
- Follow the identity provider's rules for rotation and reuse detection.
- Revoke the applicable refresh-token session on logout when supported.
- Do not retry indefinitely when a refresh token is expired, revoked, or invalid.
7. What Is a JSON Web Token (JWT)?
JSON Web Token (JWT) is a token format used to represent claims as a JSON object. JWTs are commonly used in authentication systems, but not all access tokens are JWTs. Some providers issue opaque tokens that can only be interpreted by the authorization server or resource server.
A signed JWT commonly contains three parts separated by periods:
header.payload.signature
7.1 JWT Header
The header typically identifies the token type and the signing algorithm. The server must validate the algorithm and key according to a trusted configuration rather than blindly accepting values supplied by the token.
7.2 JWT Payload
The payload contains claims such as the subject, issuer, audience, expiration time, or permissions. A payload may include identifying information, so avoid placing unnecessary personal or sensitive data in it.
7.3 JWT Signature
The signature allows the receiving system to verify that the signed token has not been modified and was signed using an accepted key. Verification must occur on the server or another trusted validation component.
7.4 JWT Example for Learning
{
"sub": "user_123",
"iss": "https://auth.example.com",
"aud": "mobile-api",
"iat": 1790000000,
"exp": 1790000900,
"scope": "profile:read"
}
This JSON object is an illustrative payload, not a complete signed JWT. The values are examples only. The server must validate the issuer, audience, expiration, signature, and relevant authorization claims before accepting a token.
7.5 Important JWT Security Note
JWT payloads are commonly encoded rather than encrypted. Anyone who obtains a normal signed JWT may be able to decode its header and payload. Do not put passwords, private keys, or other secrets in a JWT payload. A valid signature does not automatically mean the token is appropriate for every API.
8. Authentication Architecture in React Native
A clean architecture separates the screen interface, authentication service, secure storage, API client, and application authentication state.
React Native Login Screen
|
v
Authentication Service
|
v
Authentication Server
|
v
Access Token / Refresh Token
|
v
Secure Token Storage
|
v
API Client
|
v
Protected Backend API
|
v
Validated Response
8.1 Main Components
| Component |
Responsibility |
| Login screen |
Collects credentials and displays validation or server errors. |
| Authentication service |
Calls login, refresh, and logout endpoints or SDK methods. |
| Secure storage |
Persists credentials or tokens when necessary using an appropriate platform mechanism. |
| API client |
Sends authenticated requests to the correct backend. |
| Authentication context |
Shares the current authentication state across the application. |
| Backend API |
Validates authentication and enforces authorization. |
9. Setting Up a React Native Project
You can implement token-based authentication in an existing React Native application or a new project. Installation instructions vary depending on whether the application uses Expo, React Native CLI, or a provider-specific SDK.
9.1 Install an HTTP Client
The built-in Fetch API is sufficient for many projects. Axios is an alternative when shared request configuration and interceptors are useful.
npm install axios
Install any secure-storage package using the official instructions for your project type. For Expo applications, consult the current Expo SecureStore documentation. For bare React Native applications, select a maintained library compatible with your platform and React Native version.
9.2 Suggested Project Structure
src/
components/
AuthInput.js
PrimaryButton.js
LoadingScreen.js
screens/
LoginScreen.js
RegisterScreen.js
HomeScreen.js
ProfileScreen.js
services/
authService.js
apiClient.js
storage/
tokenStorage.js
context/
AuthContext.js
navigation/
RootNavigator.js
utils/
validation.js
App.js
Separating these responsibilities makes it easier to test authentication logic, replace a backend, and maintain consistent token handling throughout the application.
10. Creating a Login Screen
The login screen collects the user's credentials and sends them to the authentication service. The screen should not generate an access token itself. Only the trusted identity provider or backend should issue credentials that it will accept.
10.1 Basic Login Screen Example
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
Pressable,
ActivityIndicator
} from 'react-native';
import { login } from '../services/authService';
export default function LoginScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleLogin = async () => {
if (!email.trim() || !password) {
setError('Enter your email address and password.');
return;
}
setLoading(true);
setError('');
try {
await login(email.trim(), password);
// Update authentication state through your app's auth provider.
} catch (err) {
setError('Unable to sign in. Check your details and try again.');
} finally {
setLoading(false);
}
};
return (
<View>
<Text>Login</Text>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email address"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
/>
{!!error && (
<Text accessibilityRole="alert">{error}</Text>
)}
<Pressable
onPress={handleLogin}
disabled={loading}
accessibilityRole="button"
>
{loading
? <ActivityIndicator />
: <Text>Sign In</Text>}
</Pressable>
</View>
);
}
This screen assumes that an authentication service exists and that application-level authentication state is handled separately. A production screen should also provide appropriate password recovery, registration navigation, accessibility labels, and error handling.
11. Connecting to a Token Authentication API
A custom authentication server may expose an endpoint such as POST /auth/login. The endpoint receives credentials over HTTPS and returns a response according to the server's documented contract.
11.1 Example Request
POST /auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "USER_SUPPLIED_PASSWORD"
}
The credentials above are placeholders. Never hardcode a real user's password into source code or sample data that is shipped with the application.
11.2 Example Response
{
"accessToken": "ACCESS_TOKEN_FROM_SERVER",
"refreshToken": "REFRESH_TOKEN_FROM_SERVER",
"expiresIn": 900,
"tokenType": "Bearer"
}
This is an illustrative response. Actual field names, expiration units, refresh-token support, and session behavior depend on the backend. Do not assume every provider returns both tokens.
11.3 Creating the Authentication Service
// services/authService.js
const API_BASE_URL = 'https://api.example.com';
export async function login(email, password) {
const response = await fetch(
`${API_BASE_URL}/auth/login`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password })
}
);
if (!response.ok) {
throw new Error('Authentication failed');
}
return response.json();
}
Replace the example domain with your trusted backend's HTTPS endpoint. This function returns the response data but does not persist tokens or update authentication state. Those responsibilities should be implemented separately using the backend's documented contract.
12. Storing Tokens Securely
Once a token is received, the application must decide whether it needs to persist it and how to protect it. Authentication tokens are credentials. If an attacker obtains a usable token, they may be able to access resources until the token expires, is revoked, or otherwise becomes invalid.
12.1 Storage Options
| Storage Method |
Characteristics |
Recommendation |
| Secure platform storage |
Uses platform security facilities such as Keychain or Android Keystore-backed mechanisms through a suitable library. |
Use for sensitive tokens when client-side persistence is required. |
| AsyncStorage |
General-purpose persistent key-value storage. |
Do not use as the default storage for sensitive authentication tokens. |
| In-memory state |
Tokens are held in application memory while running. |
Reduces persistent storage exposure but does not survive process restarts. |
| HttpOnly secure cookies |
Can reduce direct JavaScript access to session credentials in supported architectures. |
Consider when the backend and mobile networking architecture support this model correctly. |
Secure storage does not make a token invulnerable. Device compromise, insecure application code, accidental logging, or incorrect server-side validation can still create risks. Choose a storage strategy based on your provider's requirements and the threat model of the application.
12.2 Secure Storage Service Example
The following example illustrates a storage wrapper using Expo SecureStore. Use it only in an Expo environment configured for this package, or adapt the wrapper to a suitable secure-storage library for your project.
// storage/tokenStorage.js
import * as SecureStore from 'expo-secure-store';
const ACCESS_TOKEN_KEY = 'access_token';
const REFRESH_TOKEN_KEY = 'refresh_token';
export async function saveTokens({
accessToken,
refreshToken
}) {
await SecureStore.setItemAsync(
ACCESS_TOKEN_KEY,
accessToken
);
if (refreshToken) {
await SecureStore.setItemAsync(
REFRESH_TOKEN_KEY,
refreshToken
);
} else {
await SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY);
}
}
export async function getAccessToken() {
return SecureStore.getItemAsync(ACCESS_TOKEN_KEY);
}
export async function getRefreshToken() {
return SecureStore.getItemAsync(REFRESH_TOKEN_KEY);
}
export async function clearTokens() {
await SecureStore.deleteItemAsync(ACCESS_TOKEN_KEY);
await SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY);
}
Install and configure the package according to the official documentation. The example writes the access token and optional refresh token separately. If writing either token fails, handle the partial-failure scenario safely rather than assuming both values were stored successfully.
13. Saving Tokens After Login
After receiving a successful authentication response, the application should validate that the response contains the fields required by its contract, save credentials securely when persistence is needed, and update the authenticated application state.
13.1 Login Service with Token Storage
// services/authService.js
import { saveTokens } from '../storage/tokenStorage';
const API_BASE_URL = 'https://api.example.com';
export async function login(email, password) {
const response = await fetch(
`${API_BASE_URL}/auth/login`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password })
}
);
if (!response.ok) {
throw new Error('Unable to sign in');
}
const data = await response.json();
if (
typeof data.accessToken !== 'string' ||
data.accessToken.length === 0
) {
throw new Error('Invalid authentication response');
}
await saveTokens({
accessToken: data.accessToken,
refreshToken: data.refreshToken
});
return data;
}
This example assumes that the server uses the illustrated field names. Update the validation and persistence logic to match your real backend. In a production implementation, also handle partial storage failures, token expiry metadata, server error codes, and session-state updates.
14. Sending an Access Token with API Requests
After a user signs in, protected API calls usually send the access token through the HTTP Authorization header with the Bearer scheme.
14.1 Fetch API Example
import { getAccessToken } from '../storage/tokenStorage';
const API_BASE_URL = 'https://api.example.com';
export async function getProfile() {
const token = await getAccessToken();
if (!token) {
throw new Error('Authentication required');
}
const response = await fetch(
`${API_BASE_URL}/profile`,
{
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
);
if (!response.ok) {
throw new Error('Unable to load profile');
}
return response.json();
}
The API must validate the access token on every protected request. The client should not assume that a token is valid merely because it exists in storage. A token may have expired, been revoked, or lack permission to access the requested resource.
14.2 API Request Best Practices
- Send tokens only to the intended trusted API.
- Use HTTPS for all production authentication and API traffic.
- Never place access tokens in query parameters or URL fragments.
- Avoid logging request headers that contain credentials.
- Handle HTTP 401 and 403 responses appropriately.
- Do not automatically retry every failed request as an authentication failure.
- Keep resource authorization checks on the server.
15. Creating a Reusable API Client with Axios
When many screens call the same backend, a shared API client helps standardize the base URL, request headers, timeouts, and error handling.
15.1 Basic Axios Client
// services/apiClient.js
import axios from 'axios';
import { getAccessToken } from '../storage/tokenStorage';
export const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 15000,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
});
apiClient.interceptors.request.use(async config => {
const token = await getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
This request interceptor attaches the access token to requests made through this client. Do not use the client indiscriminately for third-party domains because it could send the token to an unintended host. Keep the base URL trusted and consider validating request destinations when the client supports configurable URLs.
16. Understanding Token Expiration
Access tokens commonly expire after a limited period. When an expired token is sent to a protected endpoint, the server may return HTTP 401 or another documented error response. The client must follow the API's authentication contract to recover.
16.1 What Is Token Expiration?
Token expiration is a time-based limit that prevents a token from being accepted indefinitely. A JWT may include an exp claim, while an opaque token may have an expiration time managed by the authorization server.
16.2 Recommended Expiration Handling
- Check the API response or provider-supported token metadata.
- If the access token is invalid or expired, use the supported refresh flow if available.
- Validate the refresh response and store replacement credentials securely.
- Retry the original request at most as appropriate for the API contract.
- If refresh fails, clear the invalid local session and prompt the user to sign in again.
Do not decode a JWT in the client and treat its claims as proof of validity. Client-side decoding can help display non-sensitive information or estimate expiry, but trusted validation must happen at the resource server or authorization server.
17. Implementing a Token Refresh Workflow
The refresh implementation depends on the backend or identity provider. The following example demonstrates the general structure for a custom API that accepts a refresh token and returns a replacement access token.
17.1 Example Refresh Function
// services/refreshService.js
import {
getRefreshToken,
saveTokens,
clearTokens
} from '../storage/tokenStorage';
const API_BASE_URL = 'https://api.example.com';
export async function refreshAccessToken() {
const refreshToken = await getRefreshToken();
if (!refreshToken) {
throw new Error('No refresh token available');
}
const response = await fetch(
`${API_BASE_URL}/auth/refresh`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ refreshToken })
}
);
if (!response.ok) {
await clearTokens();
throw new Error('Session expired. Please sign in again.');
}
const data = await response.json();
if (
typeof data.accessToken !== 'string' ||
data.accessToken.length === 0
) {
await clearTokens();
throw new Error('Invalid refresh response');
}
await saveTokens({
accessToken: data.accessToken,
refreshToken: data.refreshToken ?? refreshToken
});
return data.accessToken;
}
This is a teaching example, not a universal refresh implementation. Use the endpoint, request format, rotation policy, and error codes defined by your backend. Some systems rotate refresh tokens on every use; others use provider SDKs or server-managed cookies. Do not retain an old refresh token if the server requires rotation and the replacement was not saved successfully.
17.2 Preventing Concurrent Refresh Requests
If multiple API requests fail at the same time, each request may attempt to refresh the token. This can create race conditions, particularly when refresh tokens are rotated. A production API client should coordinate refresh operations so that concurrent requests share one refresh attempt where appropriate.
A robust refresh manager should:
- Track whether a refresh operation is already in progress.
- Allow waiting requests to use the refreshed token when it becomes available.
- Retry only requests that are safe to retry and only under defined conditions.
- Avoid infinite loops when a refreshed token is rejected.
- Handle logout and account switching while a refresh is in progress.
- Clear session state if refresh fails in a way that invalidates the session.
18. Handling HTTP 401 and 403 Responses
HTTP status codes provide useful information, but their exact meaning should be interpreted according to the API contract.
| Status |
Common Meaning |
Typical Client Response |
| 200 OK |
The request succeeded. |
Process the returned data. |
| 400 Bad Request |
The request is malformed or fails validation. |
Show an appropriate validation message. |
| 401 Unauthorized |
Authentication is missing or invalid. |
Refresh or reauthenticate if appropriate. |
| 403 Forbidden |
The request is not permitted for this user or credential. |
Explain that access is unavailable; do not repeatedly refresh without evidence that authentication is the problem. |
| 429 Too Many Requests |
The client exceeded a rate limit. |
Respect retry guidance and apply backoff when appropriate. |
| 500 Server Error |
The server encountered a problem. |
Display a general error and allow a controlled retry. |
A 401 response does not always mean the access token expired; the token could be missing, malformed, revoked, or issued for the wrong audience. A 403 response usually indicates insufficient permission, although implementations differ. Follow the API's documented behavior.
19. Implementing Logout for Token-Based Authentication
Logout must handle both client-side authentication state and server-side session behavior. Removing a token from local storage prevents the app from using that stored value, but it does not necessarily invalidate a copied token or revoke a server-side session.
19.1 Basic Local Logout
// services/logoutService.js
import { clearTokens } from '../storage/tokenStorage';
export async function clearLocalAuthentication() {
await clearTokens();
// Also clear user-specific in-memory state and caches
// through the application's authentication state manager.
}
This function only clears locally stored token values. If the backend supports logout or refresh-token revocation, invoke that documented endpoint before or as part of the logout process. Plan how the app behaves if the network is unavailable while logging out.
19.2 Server-Supported Logout Example
import {
getRefreshToken,
clearTokens
} from '../storage/tokenStorage';
const API_BASE_URL = 'https://api.example.com';
export async function logout() {
const refreshToken = await getRefreshToken();
try {
if (refreshToken) {
await fetch(`${API_BASE_URL}/auth/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ refreshToken })
});
}
} finally {
await clearTokens();
}
}
This example assumes that the backend provides a logout endpoint accepting a refresh token. Adapt it to your real session architecture. The finally block clears local tokens even if the network request fails; however, a failed server request may mean server-side revocation did not complete. For high-security systems, make that outcome explicit and use the backend's supported recovery procedure.
20. Managing Authentication State with React Context
React Context can provide authentication information to screens without passing the same props through many layers. A central provider may track the current user, initialization status, and login and logout actions.
20.1 Example Authentication Context Structure
import React, {
createContext,
useContext,
useState
} from 'react';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [initializing, setInitializing] = useState(true);
const setAuthenticatedUser = currentUser => {
setUser(currentUser);
};
const clearAuthenticatedUser = () => {
setUser(null);
};
return (
<AuthContext.Provider
value={{
user,
initializing,
setInitializing,
setAuthenticatedUser,
clearAuthenticatedUser
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used inside AuthProvider');
}
return context;
}
This example provides a state container but does not implement startup token restoration, login, refresh, or logout by itself. A complete application must connect these actions to the authentication service and initialize the state based on the actual provider session.
21. Protecting Screens in React Native
Authenticated screens should be displayed only when the application has established an authenticated state. React Navigation supports authentication flows in which the available screens change depending on whether a user is signed in.
21.1 Conditional Navigation Example
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useAuth } from '../context/AuthContext';
import LoginScreen from '../screens/LoginScreen';
import RegisterScreen from '../screens/RegisterScreen';
import HomeScreen from '../screens/HomeScreen';
const Stack = createNativeStackNavigator();
export default function RootNavigator() {
const { user, initializing } = useAuth();
if (initializing) {
return null; // Replace with a loading or splash screen.
}
return (
<NavigationContainer>
<Stack.Navigator>
{user ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
Ensure that the user state is derived from a validated login or restored session rather than merely from the presence of a string in local storage. Hiding screens is a user-interface control, not a substitute for backend authorization.
Documentation: React Navigation Authentication Flows.
22. Handling App Startup and Token Restoration
When the application starts, it may need to restore a persisted session. This process should not blindly trust any token retrieved from storage. The app can use the provider's supported session mechanism, refresh the session when required, or call an appropriate API to confirm the session.
22.1 Startup Workflow
- Display a loading or splash screen.
- Load the session information required by the selected authentication design.
- Check whether the current access token is usable according to trusted server or provider responses.
- Use a refresh flow when supported and necessary.
- Set the central authentication state only after the session has been established according to the application's contract.
- Display the authenticated screens or login screen.
- Handle storage errors, expired sessions, and offline conditions explicitly.
A locally stored access token can be expired or revoked. If the app cannot validate or refresh a session while offline, decide whether to show a limited offline state or require the user to reconnect, based on the sensitivity of the data.
23. Token-Based Authentication with Firebase
Firebase Authentication manages authentication sessions and provides SDK methods for retrieving tokens for Firebase-backed requests or supported integrations. When using Firebase, prefer its documented SDK workflow instead of implementing a separate token protocol without a clear requirement.
23.1 Firebase Token Overview
- Firebase Authentication signs in the user through the configured provider.
- The SDK maintains the authentication state and manages token lifecycle behavior.
- The client can obtain an ID token for a Firebase-authenticated user.
- A backend that trusts Firebase Authentication can verify the ID token using the supported server-side Firebase Admin SDK.
- The backend must verify the token and enforce authorization before returning private data.
23.2 Example: Get a Firebase ID Token
import { getAuth } from 'firebase/auth';
const auth = getAuth();
export async function getFirebaseIdToken() {
const user = auth.currentUser;
if (!user) {
throw new Error('User is not signed in');
}
return user.getIdToken();
}
For the Firebase JavaScript SDK, configure React Native persistence according to the SDK version and environment. Avoid initializing duplicate Auth instances. If using React Native Firebase, use the corresponding SDK methods and native setup instructions instead.
23.3 Send the Token to a Trusted Backend
const token = await getFirebaseIdToken();
const response = await fetch(
'https://api.example.com/profile',
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
The example assumes that the backend is specifically configured to accept and verify Firebase ID tokens. Do not send an ID token to an unrelated API that expects a different access token. Firebase documentation: Verify Firebase ID Tokens.
24. Common Token-Based Authentication Mistakes
| Mistake |
Why It Is a Problem |
Recommended Solution |
| Storing sensitive tokens in general-purpose storage |
Can increase exposure if application data is accessed. |
Use appropriate platform secure storage or provider-managed session handling. |
| Sending tokens in URLs |
URLs can appear in logs, analytics, and browser or network history. |
Use the documented authorization header or secure session mechanism. |
| Trusting a decoded JWT |
Decoding does not validate its signature or claims. |
Validate tokens on the trusted server. |
| Using an ID token as an API access token without validation design |
The token may be intended for a different recipient or purpose. |
Use the token type required by the API. |
| Not handling token expiry |
Protected API requests may fail unexpectedly. |
Implement the documented refresh or reauthentication flow. |
| Refreshing on every error |
Can create infinite retries and unnecessary requests. |
Refresh only for defined authentication failures and limit retries. |
| Clearing only local tokens on logout |
A server-side session or copied refresh token may remain valid. |
Revoke or terminate server-side sessions where supported. |
| Protecting only navigation screens |
Attackers may still call backend endpoints directly. |
Enforce authentication and authorization on every protected resource. |
| Logging tokens during debugging |
Logs may expose reusable credentials. |
Redact authorization headers and sensitive response fields. |
25. Security Best Practices
- Use HTTPS for all authentication and API requests in production.
- Use a trusted identity provider or implement a well-reviewed authentication backend.
- Never store passwords for the purpose of automatic login.
- Store persistent tokens in suitable secure storage when required.
- Use short-lived access tokens where appropriate for the system's risk profile.
- Apply refresh-token rotation and revocation policies when supported.
- Validate token signature, issuer, audience, expiration, and relevant claims on the server.
- Check resource-level permissions on every protected request.
- Never include tokens in URLs, analytics events, crash reports, or ordinary logs.
- Restrict tokens to the intended API and scope.
- Clear user-specific caches when signing out or switching accounts.
- Handle refresh failures and session expiry without endless retries.
- Keep dependencies updated and review platform-specific security guidance.
- Use multi-factor authentication when required by the application's security needs.
- Do not rely on obfuscation or client-side checks as the primary security boundary.
26. Testing Token-Based Authentication
Authentication testing should cover successful operations, invalid credentials, session restoration, token expiry, refresh behavior, logout, and API authorization.
26.1 Login Tests
- Log in with valid credentials and confirm that the server returns the expected token response.
- Log in with invalid credentials and confirm that no authenticated state is created.
- Test missing email or password values.
- Verify that loading indicators and errors are handled correctly.
- Confirm that tokens are never printed in logs.
26.2 API Request Tests
- Send a valid access token and confirm that the authorized request succeeds.
- Send a missing token and confirm that the API rejects protected access.
- Send an expired or invalid token and verify the documented error behavior.
- Use a token with insufficient permissions and verify that access is denied.
- Confirm that the application does not send tokens to untrusted domains.
26.3 Refresh and Logout Tests
- Test refresh using a valid refresh token.
- Test refresh with an expired, invalid, or revoked refresh token.
- Simulate concurrent requests that receive authentication failures.
- Verify that refresh attempts do not enter an infinite loop.
- Log out and confirm that local tokens and private application state are cleared.
- Verify server-side session revocation when supported.
- Restart the application and confirm that it restores or rejects the session appropriately.
27. Mini Project: Token-Based Authentication App
Build a React Native application that authenticates users through a backend API and accesses a protected profile endpoint using an access token.
Project Requirements
- Create login and registration screens.
- Connect the forms to a real authentication service.
- Receive and validate the token response according to the backend contract.
- Store tokens using a suitable secure-storage mechanism when persistence is required.
- Create a shared API client that sends the access token only to the trusted backend.
- Build a protected Profile screen that retrieves private data from the API.
- Handle missing, expired, or rejected tokens.
- Implement the supported refresh-token workflow.
- Add logout with local cleanup and server-side revocation when available.
- Protect authenticated screens through centralized authentication state.
- Test successful login, failed login, session restoration, refresh failure, and logout.
- Document the backend's token format, expiration policy, and security assumptions.
Expected Outcome
The completed project should authenticate users through a trusted service, access protected API endpoints using the correct token type, handle expired sessions, and sign users out safely. It should not rely solely on client-side checks to protect sensitive data.
28. Interview Questions and Answers
Q1. What is token-based authentication?
Answer: It is an authentication approach in which a client presents a token issued by a trusted identity provider or backend to access protected resources.
Q2. What is an access token?
Answer: An access token is a credential used to access protected resources. It typically has a limited lifetime and must be validated by the resource server.
Q3. What is a refresh token?
Answer: A refresh token is a credential that can be used to obtain a new access token when supported by the authentication system. It requires careful storage and lifecycle management.
Q4. What is a JWT?
Answer: A JSON Web Token is a token format that represents claims in a JSON payload and can be signed or protected using a supported cryptographic method.
Q5. Does decoding a JWT verify it?
Answer: No. Decoding only reads the token's encoded contents. Signature and claim validation must be performed by a trusted validator.
Q6. Where should tokens be stored in React Native?
Answer: When persistent storage is needed, use a suitable platform secure-storage solution or the provider's recommended session mechanism. Avoid using general-purpose storage as the default location for sensitive tokens.
Q7. How do you send a token with an API request?
Answer: A common pattern is to send the access token in the HTTP Authorization header using the Bearer scheme, provided that this matches the API's contract.
Q8. What happens when an access token expires?
Answer: The API may reject the request. The client can use a supported refresh flow or ask the user to sign in again if the session cannot be renewed.
Q9. What is the difference between a 401 and a 403 response?
Answer: A 401 commonly indicates missing or invalid authentication, while a 403 commonly indicates that the request is not permitted. Exact behavior depends on the API contract.
Q10. Why is HTTPS important?
Answer: HTTPS protects credentials and tokens in transit against interception and tampering by unauthorized parties on the network.
Q11. Why is deleting a token locally not always enough for logout?
Answer: A server-side session or copied refresh token may remain valid. When supported, the application should invoke the backend's logout or revocation mechanism as well.
Q12. What is the purpose of an authentication interceptor?
Answer: An HTTP client interceptor can attach the access token to outgoing requests and centralize common request handling. It must be carefully scoped to trusted API destinations.
Q13. Should an ID token always be sent to a backend API?
Answer: No. The backend must explicitly be designed to accept and validate that token type. APIs may require a separate access token.
Q14. How do you protect API endpoints?
Answer: Validate the token on the server and enforce authorization for the specific resource and operation. Hiding a screen in React Native is not sufficient.
29. Quick Revision Checklist
- Understand the purpose of token-based authentication.
- Differentiate access tokens, refresh tokens, and ID tokens.
- Understand JWT structure and the difference between decoding and validation.
- Implement login through a trusted authentication endpoint or SDK.
- Validate the token response against the backend contract.
- Store persistent tokens using an appropriate secure mechanism.
- Send access tokens only to trusted APIs.
- Handle token expiry, refresh, and reauthentication.
- Prevent repeated refresh attempts and concurrent refresh races.
- Implement logout and clear private local state.
- Revoke server-side sessions or refresh tokens when supported.
- Protect backend resources with server-side authorization.
- Test valid, invalid, expired, and revoked token scenarios.
- Never log passwords, access tokens, or refresh tokens.
30. Conclusion
Token-based authentication is a widely used approach for connecting React Native applications to protected backend services. It enables users to authenticate once and then present an appropriate token when accessing authorized resources.
A secure implementation requires more than receiving and storing a token. Developers must choose the correct token type, use secure transport and storage, handle expiration and refresh correctly, coordinate authentication state, implement logout, and validate permissions on the server. When these elements are designed together, token-based authentication can provide a reliable foundation for mobile application security.
Course Resource: Explore React Native Training at JustAcademy
Demo Registration: Register for a Course Demo
React Native Security: Official React Native Security Documentation
Secure Storage: Expo SecureStore Documentation
Authentication Standards: OAuth 2.0 Bearer Token Usage | JSON Web Token (JWT) Standard
JustAcademy Website: Visit JustAcademy