Safeguard
Open Source

react-native-app-auth: Secure OAuth and OpenID Connect for React Native

react-native-app-auth bridges the native AppAuth SDKs so your React Native app gets PKCE and RFC 8252 flows for free. Here is how to wire it up safely, including Azure AD.

Karan Patel
Platform Engineer
5 min read

react-native-app-auth is the React Native bridge to the native AppAuth SDKs, and it exists so your mobile app can do OAuth 2.0 the way RFC 8252 says native apps should. If you have ever hand-rolled a login flow in a WebView and felt uneasy about it, react native app auth is the library that removes the guesswork. It wraps Apple's and Google's official AppAuth clients rather than reimplementing OAuth in JavaScript, and that architectural choice is what makes it trustworthy.

Why a native bridge beats a WebView login

The tempting shortcut in mobile OAuth is to open the provider's login page inside a WebView, capture the redirect, and pull the code out of the URL. It works in a demo and fails every security review. A WebView gives your app access to the credentials the user types, defeats the browser's saved-session and passkey support, and is explicitly discouraged by RFC 8252, the OAuth 2.0 for Native Apps specification.

react-native-app-auth, maintained by FormidableLabs and Nearform, delegates to the platform's secure browser tab instead: ASWebAuthenticationSession / SFSafariViewController on iOS and Custom Tabs on Android. The user authenticates in a real browser context your app cannot read, and control returns via a registered redirect URI. That isolation is the entire security argument.

PKCE is on by default, and that matters

Public clients like mobile apps cannot keep a client secret secret. Anyone can unzip an IPA or APK and read strings out of it. PKCE (Proof Key for Code Exchange, pronounced "pixy") solves this by having the app generate a random code_verifier, send its hash as the code_challenge on the authorization request, and prove possession of the verifier when exchanging the code for tokens. An intercepted authorization code is useless without the verifier.

react-native-app-auth performs the PKCE flow automatically. You do not opt in; it is the default behavior of the underlying AppAuth clients. This is a genuine advantage over a naive implementation where a developer might forget PKCE entirely and ship the far weaker implicit flow.

A minimal configuration

Install the package and follow the native setup steps for registering your redirect URI scheme in both the iOS Info.plist and Android build.gradle. The JavaScript config is small:

import { authorize } from 'react-native-app-auth';

const config = {
  issuer: 'https://accounts.google.com',
  clientId: 'YOUR_CLIENT_ID',
  redirectUrl: 'com.myapp://oauthredirect',
  scopes: ['openid', 'profile', 'email'],
};

const result = await authorize(config);
// result.accessToken, result.idToken, result.refreshToken, result.accessTokenExpirationDate

For OpenID Connect providers, setting issuer lets the library use autodiscovery: it fetches the .well-known/openid-configuration document and learns the authorization and token endpoints on its own. For plain OAuth 2.0 providers without discovery, you supply serviceConfiguration with the endpoints manually.

Wiring up Azure AD

React native azure auth is one of the most common real-world targets, and it works cleanly with react-native-app-auth as long as you match Azure's expectations. Register the app in Entra ID (Azure AD) as a Mobile and desktop application, add your custom-scheme redirect URI, and use the tenant-specific issuer:

const azureConfig = {
  issuer: 'https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0',
  clientId: 'YOUR_AZURE_CLIENT_ID',
  redirectUrl: 'com.myapp://oauthredirect',
  scopes: ['openid', 'profile', 'offline_access', 'User.Read'],
};

Two gotchas trip people up. First, include offline_access in scopes or Azure will not return a refresh token. Second, use the v2.0 endpoint in the issuer; mixing v1.0 and v2.0 endpoints produces confusing token-validation failures. Autodiscovery against the v2.0 issuer keeps the endpoints consistent.

Storing tokens like they are dangerous

Getting tokens back is half the job. Storing them is where mobile apps leak. Do not put access or refresh tokens in AsyncStorage, which is unencrypted plaintext on disk. Use the platform secure storage: Keychain on iOS and Keystore-backed encrypted storage on Android, via a library like react-native-keychain.

A refresh token in plaintext on a rooted or jailbroken device is a long-lived credential an attacker can lift. Treat it with the same care you would a password. When you refresh, use the library's refresh function and update your secure store atomically.

import { refresh } from 'react-native-app-auth';

const refreshed = await refresh(config, {
  refreshToken: storedRefreshToken,
});

Keeping the dependency healthy

react-native-app-auth is a mature, widely used library, but it is still a dependency in your supply chain, and it pulls native code from AppAuth. A few habits keep it safe:

  • Pin the version and review the changelog before upgrading across major versions, since native setup steps occasionally change.
  • Watch for advisories on the package and its native AppAuth dependencies. An SCA tool that understands both the npm graph and native modules gives you coverage the package.json alone does not.
  • Rotate client IDs if you ever suspect a redirect-URI hijack, and confirm no other app registered your custom scheme (a reason to prefer app-claimed HTTPS redirects where the platform supports them).

The short version: react-native-app-auth gets the hard parts right by delegating to the platform. Your job is to configure it correctly, store the tokens it returns in secure storage, and keep it patched.

FAQ

Does react-native-app-auth support PKCE automatically?

Yes. PKCE is enabled by default through the underlying AppAuth SDKs. You do not need to configure it manually, and there is no supported way to fall back to the less secure implicit flow, which is by design.

Can I use react-native-app-auth with Azure AD?

Yes. Register a mobile/desktop app in Entra ID, use the tenant-specific v2.0 issuer for autodiscovery, and include offline_access in your scopes to receive a refresh token. The rest of the flow is identical to any OpenID Connect provider.

Where should I store the tokens it returns?

In platform secure storage: Keychain on iOS and Keystore-backed storage on Android, typically through react-native-keychain. Never store access or refresh tokens in AsyncStorage, which is unencrypted.

Is a WebView login a valid alternative?

No. RFC 8252 explicitly recommends against embedded WebViews for OAuth in native apps. They expose credentials to the app, break saved sessions and passkeys, and are commonly rejected in security reviews. Use the secure browser tab that react-native-app-auth relies on.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.