Going Pro: Job-Ready React NativePro· 50 min read

Authentication & Secure Token Storage

Almost every real app has login — and the access token it gives back must be stored in the secure Keychain, never in plain AsyncStorage.

What you will learn

  • Walk through a token-based login flow
  • Store a token safely with expo-secure-store (not AsyncStorage)
  • Send the token on later requests and log out by deleting it

What "authentication" really means

Authentication is just the app proving who the user is — the login step. The modern way works with a token: a long secret string the server gives your app after a correct email and password. Often it is a JWT (JSON Web Token — a compact, signed string that encodes who you are and when it expires). From then on, the app sends that token with each request as a badge that says "it is still me", instead of asking for the password again.

Picture a theme park: you show your ticket once at the gate (login), get a wristband (the token), and after that you just flash the wristband on each ride (each API request) — no need to buy a ticket every time.

Why NOT AsyncStorage for the token

You met AsyncStorage earlier for small data. It is perfect for a theme or a username — but it stores data unencrypted in plain text. A token is a key to the user’s account, so it must go somewhere encrypted that the operating system protects: the iOS Keychain and the Android Keystore. Expo wraps both in one module, expo-secure-store.

AsyncStorageexpo-secure-store
Encrypted?No — plain textYes — OS Keychain / Keystore
Good forTheme, username, small listsTokens, passwords, secrets
SizeLarger dataSmall values only
Use for auth tokens?NeverYes — this is the right place

Install it (one-time):

Install Expo Secure Store
npx expo install expo-secure-store

Note: Output: (Adds the secure-store module. Nothing on screen yet.)

Step 1 — Log in and get a token

Logging in is a normal fetch that POSTs the email and password (POST means "send data to the server", unlike GET which only reads). The server checks them and replies with a token. Here we call a login endpoint and read the token out of the JSON:

POST credentials, receive a token in the response
async function login(email, password) {
  const res = await fetch('https://api.example.com/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  });
  if (!res.ok) throw new Error('Wrong email or password');
  const data = await res.json();
  return data.token;   // the secret wristband
}

Note: Output (on success): The function returns a long string like eyJhbGciOiUzI1NiI..." (a JWT). method: 'POST' sends the data; body carries the email and password as JSON; res.ok is false for a 401, so a wrong password throws instead of returning a bad token.

Step 2 — Save the token securely

Now store that token in the encrypted store. The API mirrors AsyncStorage — setItemAsync, getItemAsync, deleteItemAsync — but the value is protected by the OS:

Securely save, read and delete the token
import * as SecureStore from 'expo-secure-store';

// save after a successful login
await SecureStore.setItemAsync('userToken', token);

// read it back later (e.g. when the app reopens)
const token = await SecureStore.getItemAsync('userToken');

// delete it on logout
await SecureStore.deleteItemAsync('userToken');

Note: Output: No visible output — the token is written into the iOS Keychain / Android Keystore. Unlike AsyncStorage, another app or a file browser cannot read it. getItemAsync returns null if nothing was saved (so guard for that).

Step 3 — Send the token on future requests

For pages that need a logged-in user (the profile, the cart), you attach the token to the request in an Authorization header — a standard place to put a credential. The format Bearer THE_TOKEN is the agreed convention:

Attach the token as a Bearer Authorization header
async function getProfile() {
  const token = await SecureStore.getItemAsync('userToken');
  const res = await fetch('https://api.example.com/me', {
    headers: { Authorization: 'Bearer ' + token },
  });
  return res.json();
}

Note: Output: The server sees the token, knows who you are, and returns that user’s profile JSON. Without the header (or with a wrong token) the server replies 401 Unauthorized.

The whole login-to-logout flow, step by step

Tying it together, here is the full life of a session, in order:

  1. The app opens and calls SecureStore.getItemAsync('userToken'). If it returns a token, the user is still logged in from last time; if null, show the login screen.
  2. On the login screen the user types email and password and taps Log in, which calls our login function.
  3. login POSTs the credentials. The server checks them and, if correct, returns a token; a wrong password throws an error you show to the user.
  4. You save the token with SecureStore.setItemAsync('userToken', token) into the encrypted Keychain/Keystore, and (often) set a global user in your auth store so screens update.
  5. For every protected request after that, you read the token and send it as Authorization: Bearer <token> so the server knows it is still the same user.
  6. When the user taps Log out, you call SecureStore.deleteItemAsync('userToken') and clear the global user — the badge is gone, so the app returns to the login screen.

Watch out: Never store a password or auth token in AsyncStorage, in plain state that gets logged, or hard-coded in your source. Tokens go in secure-store only. Also give tokens an expiry on the server so a stolen one stops working.

Tip: For top-tier apps you can add biometrics (Face ID / fingerprint) to unlock the stored token, and a refresh token to get a new access token automatically when the old one expires — both build directly on this same secure-store foundation.

Q. Where should an authentication token be stored on the device?

Answer: A token is a key to the account, so it must be encrypted. expo-secure-store uses the iOS Keychain and Android Keystore; AsyncStorage stores plain text and must never hold tokens.

✍️ Practice

  1. Write a saveToken and a loadToken helper using SecureStore.setItemAsync / getItemAsync.
  2. Write a logout function that deletes the token and explain in one line why AsyncStorage would be wrong here.

🏠 Homework

  1. Sketch (in code or pseudo-code) a login screen that calls a login API, saves the returned token in secure-store, and shows a profile screen only when a token exists.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →