After a successful login, a React Native application needs a way to remember authentication information when appropriate. AsyncStorage can store persistent application data on the device. In this lesson, we will learn how to use AsyncStorage with JWT authentication.
AsyncStorage is a persistent key-value storage system commonly used in React Native applications.
Data stored in AsyncStorage can remain available after the application is closed and opened again.
Key → Value
Install the AsyncStorage package in a React Native project.
npm install @react-native-async-storage/async-storage
With Expo, the package can also be installed using:
npx expo install @react-native-async-storage/async-storage
import AsyncStorage
from "@react-native-async-storage/async-storage";
The imported object provides asynchronous methods for storing and retrieving values.
Use setItem() to store a value.
await AsyncStorage.setItem(
"authToken",
token
);
The token is stored using the key authToken.
Use getItem() to retrieve a stored value.
const token =
await AsyncStorage.getItem(
"authToken"
);
console.log(token);
Use removeItem() when the application needs to remove
the locally stored token.
await AsyncStorage.removeItem(
"authToken"
);
This is commonly used during logout.
AsyncStorage methods return promises, so await can be used
inside an asynchronous function.
const loadToken = async () => {
const token =
await AsyncStorage.getItem(
"authToken"
);
console.log(token);
};
After a successful login, the API can return a JWT token.
const result =
await login({
email,
password
});
if (result.success) {
await AsyncStorage.setItem(
"authToken",
result.token
);
}
If needed, non-sensitive user information can also be stored.
await AsyncStorage.setItem(
"userId",
String(result.user.id)
);
When storing objects or structured data, use JSON serialization.
AsyncStorage stores strings. Therefore, an object must be converted to JSON before storing it.
const user = {
id: 101,
name: "Rahul",
email: "rahul@example.com"
};
await AsyncStorage.setItem(
"user",
JSON.stringify(user)
);
const storedUser =
await AsyncStorage.getItem(
"user"
);
if (storedUser) {
const user =
JSON.parse(storedUser);
console.log(user.name);
}
A token retrieved from AsyncStorage can be either a string or null.
const token: string | null =
await AsyncStorage.getItem(
"authToken"
);
The value is null when the key does not exist.
interface AuthState {
token: string | null;
user: User | null;
isAuthenticated: boolean;
}
The application can use this state to represent the current authentication status.
const loadToken = async () => {
const token =
await AsyncStorage.getItem(
"authToken"
);
if (token) {
console.log(
"Token found"
);
} else {
console.log(
"No token found"
);
}
};
useEffect(() => {
const initializeAuth =
async () => {
const token =
await AsyncStorage.getItem(
"authToken"
);
console.log(token);
};
initializeAuth();
}, []);
const initializeAuth = async () => {
const token =
await AsyncStorage.getItem(
"authToken"
);
if (token) {
setAuth({
token,
user: null,
isAuthenticated: true
});
} else {
setAuth({
token: null,
user: null,
isAuthenticated: false
});
}
};
if (result.success) {
await AsyncStorage.setItem(
"authToken",
result.token
);
await AsyncStorage.setItem(
"user",
JSON.stringify(result.user)
);
}
const token =
await AsyncStorage.getItem(
"authToken"
);
const storedUser =
await AsyncStorage.getItem(
"user"
);
if (token && storedUser) {
const user =
JSON.parse(storedUser);
console.log(user.name);
}
const logout = async () => {
await AsyncStorage.removeItem(
"authToken"
);
await AsyncStorage.removeItem(
"user"
);
setAuth({
token: null,
user: null,
isAuthenticated: false
});
};
Removing the local token prevents the application from continuing to use that locally stored authentication state.
const getToken = async () => {
return await AsyncStorage.getItem(
"authToken"
);
};
const loadProfile = async () => {
const token =
await getToken();
const response =
await api.get(
"/profile.php",
{
headers: {
Authorization:
`Bearer ${token}`
}
}
);
return response.data;
};
An Axios request interceptor can read the token before a request is sent.
api.interceptors.request.use(
async config => {
const token =
await AsyncStorage.getItem(
"authToken"
);
if (token) {
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
}
);
const token =
await AsyncStorage.getItem(
"authToken"
);
if (!token) {
console.log(
"User is not logged in"
);
return;
}
A missing token can be used to determine that there is no locally available authenticated session.
AsyncStorage does not verify whether a JWT is valid or expired. The server must validate the token.
try {
const response =
await api.get(
"/profile.php"
);
} catch (error) {
if (
axios.isAxiosError(error) &&
error.response?.status === 401
) {
await AsyncStorage.removeItem(
"authToken"
);
}
}
AsyncStorage is persistent storage, but it should not be considered a secure vault for highly sensitive secrets.
For sensitive authentication tokens, a platform secure-storage solution can provide stronger protection depending on the application and threat model.
const saveToken = async (
token: string
) => {
await AsyncStorage.setItem(
"authToken",
token
);
};
const getToken = async (): Promise<
string | null
> => {
return await AsyncStorage.getItem(
"authToken"
);
};
const removeToken = async () => {
await AsyncStorage.removeItem(
"authToken"
);
};
import AsyncStorage
from "@react-native-async-storage/async-storage";
const TOKEN_KEY = "authToken";
const USER_KEY = "authUser";
export const saveAuth =
async (
token: string,
user: User
) => {
await AsyncStorage.setItem(
TOKEN_KEY,
token
);
await AsyncStorage.setItem(
USER_KEY,
JSON.stringify(user)
);
};
export const getAuth =
async () => {
const token =
await AsyncStorage.getItem(
TOKEN_KEY
);
const userData =
await AsyncStorage.getItem(
USER_KEY
);
const user =
userData
? JSON.parse(userData)
: null;
return {
token,
user
};
};
export const clearAuth =
async () => {
await AsyncStorage.multiRemove([
TOKEN_KEY,
USER_KEY
]);
};
Multiple key-value pairs can be handled together.
await AsyncStorage.multiSet([
["authToken", token],
["userId", String(user.id)]
]);
Multiple values can also be removed together.
await AsyncStorage.multiRemove([
"authToken",
"userId"
]);
const handleLogin = async () => {
try {
const result =
await login({
email,
password
});
if (result.success) {
await AsyncStorage.setItem(
"authToken",
result.token
);
await AsyncStorage.setItem(
"authUser",
JSON.stringify(
result.user
)
);
setAuth({
token: result.token,
user: result.user,
isAuthenticated: true
});
}
} catch (error) {
console.log(
"Login failed"
);
}
};
const checkAuthentication =
async () => {
const token =
await AsyncStorage.getItem(
"authToken"
);
const userData =
await AsyncStorage.getItem(
"authUser"
);
if (token && userData) {
const user =
JSON.parse(userData);
setAuth({
token,
user,
isAuthenticated: true
});
} else {
setAuth({
token: null,
user: null,
isAuthenticated: false
});
}
};
This allows the application to restore its local authentication state when it starts. The server should still validate the token before granting access to protected resources.
1. User logs in
2. React Native sends credentials
3. PHP verifies credentials
4. PHP returns JWT
5. React Native receives JWT
6. Token is stored locally
7. User closes the application
8. Application starts again
9. Token is loaded from storage
10. Authentication state is restored
11. Axios sends Bearer JWT
12. PHP verifies JWT
13. Protected API returns data
14. Logout removes local authentication data
AsyncStorage can help maintain persistent application state, while the backend remains responsible for validating the JWT. For sensitive authentication credentials, use an appropriate secure storage mechanism rather than treating AsyncStorage as a secure vault.
setItem() stores a value.getItem() retrieves a value.removeItem() removes a value.null.Question: Which AsyncStorage method is used to retrieve a stored token?