npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-auth-kit

v0.1.0

Published

Unified biometric authentication + secure token management for React Native — Face ID, Touch ID, fingerprint unlock, Keychain/Keystore storage, JWT auto-refresh, and session timeout in one package.

Downloads

144

Readme

react-native-auth-kit

npm version npm downloads License: MIT Platform

Biometric authentication and secure token management for React Native. Combines Face ID / Touch ID / fingerprint unlock, Keychain/Keystore storage, JWT access/refresh rotation, and session timeout into a single cohesive API.

Background

Most React Native apps that require authentication end up writing the same three pieces from scratch:

  1. A biometrics wrapper around react-native-biometrics
  2. A secure token store on top of react-native-keychain
  3. A fetch interceptor that adds Authorization headers and refreshes on 401

Each piece is not hard alone, but wiring them together correctly — especially the race condition where two concurrent 401s both try to refresh the token — is where bugs get introduced. This package handles all three as a coordinated system.

Installation

npm install react-native-auth-kit

Peer dependencies:

npm install react-native-keychain react-native-biometrics

Follow the native setup for each:

Both require native linking. With Expo, use the managed workflow with expo-local-authentication and expo-secure-store instead.

Quick start

// Configure once at app startup — typically in App.tsx or your root navigator
import { AuthKitManager, createFetchInterceptor } from 'react-native-auth-kit';

const manager = AuthKitManager.getInstance();

manager.configure({
  sessionTimeoutMs: 15 * 60 * 1000, // 15 minutes idle → session expired
  biometricPromptTitle: 'Confirm your identity',
  biometricPromptSubtitle: 'Use Face ID or Touch ID to continue',
  onSessionExpired: () => {
    // Navigate to login — runs when idle timer fires or refresh fails
    RootNavigation.navigate('Login');
  },
});
// After login — store the tokens
const tokens = await api.login(email, password);
await manager.initialize({
  accessToken: tokens.access_token,
  refreshToken: tokens.refresh_token,
  expiresAt: Date.now() + tokens.expires_in * 1000,
});
// Wrap fetch with automatic Bearer injection and 401 refresh
const { fetch: authFetch } = createFetchInterceptor(
  async (refreshToken) => {
    const res = await fetch('/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: refreshToken }),
    });
    if (!res.ok) throw new Error('Refresh failed');
    const data = await res.json();
    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token,
      expiresAt: Date.now() + data.expires_in * 1000,
    };
  }
);

// Use authFetch exactly like the global fetch — no other changes needed
const profile = await authFetch('/api/me').then(r => r.json());

React hooks

useAuthKit

Full authentication state and actions. Use this in screens that need to react to auth changes.

function HomeScreen() {
  const {
    isAuthenticated,
    isLoading,
    biometryType,         // 'FaceID' | 'TouchID' | 'Fingerprint' | 'None'
    login,
    logout,
    authenticateWithBiometrics,
    getAccessToken,
  } = useAuthKit();

  const handleBiometricUnlock = async () => {
    const result = await authenticateWithBiometrics('Unlock to continue');
    if (!result.success) {
      Alert.alert('Authentication failed', result.error);
    }
  };

  if (isLoading) return <LoadingScreen />;
  if (!isAuthenticated) return <LoginScreen onLogin={login} />;

  return (
    <View>
      <Button title={`Unlock with ${biometryType}`} onPress={handleBiometricUnlock} />
      <Button title="Sign out" onPress={logout} />
    </View>
  );
}

useBiometrics

Just the biometrics, without token management. Useful for screens that need re-authentication (payment confirmation, viewing sensitive data) without being tied to the session lifecycle.

function PaymentConfirmScreen() {
  const { isAvailable, biometryType, authenticate } = useBiometrics();

  const confirm = async () => {
    if (!isAvailable) {
      // Fall back to PIN or password prompt
      return promptForPin();
    }
    const result = await authenticate('Confirm payment');
    if (result.success) processPayment();
  };
}

Token management

Tokens are stored in iOS Keychain and Android Keystore — the same secure storage that password managers use. They are never written to AsyncStorage, SQLite, or the filesystem.

// TokenStore — JWT-aware operations
const store = new TokenStore();
await store.saveTokens({ accessToken, refreshToken, expiresAt });
const tokens = await store.getTokens();
const isExpired = TokenStore.isAccessTokenExpired(tokens, 30_000); // 30s buffer

Access token expiry is checked with a 30-second buffer before the actual expiry time, so the refresh happens before the token actually expires rather than after the first failed request.

The 401 refresh flow

createFetchInterceptor handles this sequence automatically:

Request → add Authorization: Bearer <access_token>
  Response 401 → get refresh token from Keychain
               → call your refreshFn
               → save new tokens to Keychain
               → retry original request with new token
  Response 401 again → call onRefreshFailure
                     → call manager.logout() → clears tokens
                     → return the 401 response

Only one refresh can be in flight at a time. If two requests both 401 simultaneously, the second waits for the first refresh to complete and then uses the result rather than triggering a second refresh.

Biometric authentication

const bio = new BiometricAuth();

// Check availability
const { available, biometryType } = await bio.isAvailable();

// Prompt the user
const result = await bio.authenticate('Confirm your identity');
if (result.success) {
  // Proceed
}

// Key management (for signing tokens with device keys)
await bio.createKeys();
const signature = await bio.signPayload('data-to-sign');
await bio.deleteKeys();

BiometricAuth wraps react-native-biometrics and normalizes the response — you always get { success: boolean; error?: string } rather than needing to handle platform-specific result shapes.

SecureStore

Lower-level API for storing any string or JSON value in Keychain/Keystore:

const store = new SecureStore();

// Store a string
await store.set('user_id', '12345');

// Store an object (JSON serialized)
await store.setObject<UserProfile>('profile', { name: 'Alice', role: 'admin' });

// Retrieve
const userId = await store.get('user_id');
const profile = await store.getObject<UserProfile>('profile');

// Check existence without reading value
const hasSession = await store.hasKey('session_token');

// Remove a single key or clear all
await store.delete('user_id');
await store.clear();

Session timeout

The idle timeout is reset every time resetSessionTimer() is called. Wire it to app state changes:

import { AppState } from 'react-native';

AppState.addEventListener('change', (state) => {
  if (state === 'active') {
    AuthKitManager.getInstance().resetSessionTimer();
  }
});

When the timer fires (no resetSessionTimer call within sessionTimeoutMs), onSessionExpired is called. The session is not automatically cleared — your handler decides what happens (navigate to login, show a lock screen, etc.). Call manager.logout() inside onSessionExpired to clear tokens.

Security notes

What is stored in Keychain/Keystore: Access token, refresh token, expiry timestamp. Nothing else.

What is not stored: Passwords, PINs, biometric data. The device OS owns biometric data; this package never sees it.

Jailbreak / root: react-native-keychain can be configured with BIOMETRY_CURRENT_SET access control, which restricts access to the item if biometrics change after storage. This is a meaningful protection against attackers adding their own fingerprint. See the keychain access control docs for configuration.

Token rotation: Every refresh returns a new refresh token. The old refresh token is discarded after the new one is saved. If the refresh API issues single-use tokens (recommended), a stolen refresh token can only be used once before it's invalidated.

License

MIT © Salil Gupta