This episode covers authentication: JWT and OAuth 2.1, the refresh token flow with an interceptor, biometric authentication with Face ID and fingerprints, SSO, and the principle of never trusting client input.

Every app that stores personal data must make sure the owner is indeed the legitimate user. Authentication is the gate — and on mobile, this gate has special challenges: tokens must be safe on the device, and the session must keep working when the app is opened again.
Episode 15 covers authentication and authorization: JWT and OAuth 2.1, the refresh token flow with an Axios interceptor, biometric authentication using Face ID and fingerprints, SSO, and the security principle that client input must not be trusted.
JWT is a common token format for identifying users: the payload contains claims, signed by the server. The modern flow uses two tokens: a short-lived access token for every request, and a long-lived refresh token to obtain a new access token.
OAuth 2.1 combines the best practices of OAuth 2.0 — Authorization Code with PKCE for mobile apps, without a client secret on the client side. PKCE ensures that the code exchange can't be intercepted. For mobile apps that aren't server-side applications, the PKCE pattern is the correct standard.
Access tokens expire quickly. When a request is rejected with status 401, the app must refresh the access token with the refresh token, then retry the request. All of this happens automatically in the interceptor:
let refreshPromise = null;
api.interceptors.response.use(
(res) => res,
async (error) => {
const status = error.response?.status;
if (status !== 401) return Promise.reject(error);
if (!refreshPromise) {
refreshPromise = refreshToken().finally(() => {
refreshPromise = null;
});
}
await refreshPromise;
error.config.headers.Authorization = `Bearer ${bacaToken()}`;
return api(error.config);
}
);The refreshPromise pattern ensures that many requests failing at once trigger only a single refresh process. When the refresh finishes, all requests are retried with the new token.
If the refresh token is also rejected, the session ends. Don't replay the request forever — clear the credentials, redirect to the login screen, and tell the user the session has expired. Episode 13 explains how to remove tokens from secure storage.
A comfortable login experience uses biometrics: the user just scans their face or fingerprint. react-native-keychain supports storing credentials that can only be accessed via biometrics:
import * as Keychain from "react-native-keychain";
async function loginBiometrik() {
const kredensial = await Keychain.getGenericPassword({
accessControl:
Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
return kredensial;
}accessControl establishes that Keychain only releases the credentials after successful biometric verification. The token can never be read without a valid fingerprint or Face ID.
Check biometric support before offering it: does the device have Face ID or a fingerprint sensor, and has the user enrolled biometrics in the system? If not, fall back to a regular password. Every failed scan should provide an alternative path.
SSO (Single Sign-On) lets users sign in with an existing account — Apple, Google, or a corporate service. Apple Sign In and Google Sign In provide a smooth flow on mobile, including profile retrieval and valid tokens.
For Expo projects, expo-auth-session and expo-apple-authentication handle the OAuth and platform SSO flows. For the React Native CLI, libraries like react-native-apple-authentication and react-native-google-signin are available. Choose one primary login path and keep it consistent.
All security validation must happen on the server. The client can be modified — via reverse engineering or request tools — so re-check on the server: does the user have the right to access this resource, and is the sent data valid.
Login endpoints and public APIs must have rate limiting to slow down brute force. Validate input on the server, not just in the UI. Treat every request coming from the app as untrusted, even with a valid token.
Warning
A token on the client only proves the client knows the token, not that the client is the real user. Always evaluate authorization on the server for every resource, and treat all input as a threat until proven valid.
Episode 15 closed the authentication gate: JWT and OAuth 2.1 with PKCE, the refresh token flow via an interceptor, biometric authentication in Keychain, SSO, and the principle that the server must not trust the client.
Key takeaways:
In the next episode, episode 16, we'll discuss privacy and data handling: data minimization per GDPR and CCPA, minimal permissions, the consent flow, secure logging, plus data retention and audit policies.