Skip to content

loonylabs-dev/react-native-offline-auth

Repository files navigation

@loonylabs/react-native-offline-auth

Offline-first authentication library for React Native with Guest Mode and JWT support

npm version License: MIT TypeScript

A production-ready authentication library for React Native that prioritizes offline-first functionality. Built with TypeScript, Zustand, and AsyncStorage.

Features

  • Offline-First - Works without internet, syncs when available
  • Guest Mode - Let users try your app without registration
  • Easy Upgrade - Seamless guest β†’ authenticated account flow
  • JWT Support - Standard JWT token management
  • Type-Safe - Full TypeScript support with comprehensive types
  • Zero Native Code - Pure JavaScript/TypeScript, works with Expo
  • Flexible API - Bring your own API implementation
  • Tree-Shakeable - Optimized bundle size

Installation

npm install @loonylabs/react-native-offline-auth zustand @react-native-async-storage/async-storage

or

yarn add @loonylabs/react-native-offline-auth zustand @react-native-async-storage/async-storage

Peer Dependencies

This library requires:

  • react >= 18.0.0
  • react-native >= 0.70.0
  • zustand >= 4.0.0
  • @react-native-async-storage/async-storage >= 1.17.0

Quick Start

1. Wrap your app with AuthProvider

import { AuthProvider } from '@loonylabs/react-native-offline-auth';
import App from './App';

export default function Root() {
  return (
    <AuthProvider
      apiCallbacks={{
        login: async (credentials) => {
          const response = await fetch('https://api.example.com/auth/login', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(credentials),
          });
          return response.json(); // { user, token }
        },
        register: async (credentials) => {
          const response = await fetch('https://api.example.com/auth/register', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(credentials),
          });
          return response.json(); // { user, token }
        },
        validateToken: async (token) => {
          const response = await fetch('https://api.example.com/auth/me', {
            headers: { Authorization: `Bearer ${token}` },
          });
          const data = await response.json();
          return data.user;
        },
      }}
    >
      <App />
    </AuthProvider>
  );
}

2. Use the hook in your components

import { useAuth } from '@loonylabs/react-native-offline-auth';

function HomeScreen() {
  const {
    user,
    isAuthenticated,
    isGuestMode,
    login,
    logout,
    continueAsGuest,
  } = useAuth();

  if (isGuestMode) {
    return <Text>Welcome Guest! πŸ‘€</Text>;
  }

  if (isAuthenticated) {
    return (
      <View>
        <Text>Welcome {user.username}! πŸ”</Text>
        <Button title="Logout" onPress={logout} />
      </View>
    );
  }

  return (
    <View>
      <Button title="Login" onPress={() => login({ email, password })} />
      <Button title="Continue as Guest" onPress={continueAsGuest} />
    </View>
  );
}

Core Concepts

Three Authentication States

  1. Authenticated - User logged in with JWT token
  2. Guest Mode - User using app without account (local data only)
  3. Unauthenticated - User needs to login or continue as guest

Offline-First Philosophy

  • App starts immediately with cached credentials
  • Token validation happens in background (non-blocking)
  • Users stay logged in even when offline
  • No forced logout on network errors

Navigation Logic

function RootNavigator() {
  const { isLoading, isAuthenticated, isGuestMode } = useAuth();

  if (isLoading) {
    return <SplashScreen />;
  }

  if (isAuthenticated || isGuestMode) {
    return <MainApp />;
  }

  return <AuthScreens />;
}

Documentation

Key Features Explained

Guest Mode

Allow users to try your app without registration:

await continueAsGuest();
// User can now use the app with local-only data

Upgrade to Account

Convert guest users to authenticated users:

await upgradeToAccount({
  email: '[email protected]',
  username: 'newuser',
  password: 'securepass123'
});
// Local data is preserved, now synced to cloud

Offline Authentication

Users stay logged in when offline:

// On app start:
// 1. Load token + user from AsyncStorage (instant)
// 2. Show app immediately
// 3. Validate token in background (when online)
// 4. Update user data if validation succeeds
// 5. Stay logged in even if validation fails (offline)

Configuration

<AuthProvider
  apiCallbacks={...}
  config={{
    // Custom storage keys (optional)
    storageKeys: {
      token: '@custom_token',
      user: '@custom_user',
      guestMode: '@custom_guest',
    },
    // Enable debug logging
    debug: true,
    // Custom token validator
    validateToken: async (token) => {
      // Your custom validation logic
      return user;
    },
    // Error callback
    onTokenValidationError: (error) => {
      console.log('Token validation failed:', error);
    },
  }}
  onReady={() => console.log('Auth initialized')}
>
  <App />
</AuthProvider>

TypeScript Support

Fully typed with comprehensive TypeScript definitions:

import type {
  User,
  AuthState,
  LoginCredentials,
  RegisterCredentials,
  AuthConfig,
} from '@loonylabs/react-native-offline-auth';

Testing

The library includes test infrastructure:

npm test              # Run tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report

Note: We're actively working on expanding test coverage. Contributions welcome!

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           React Components                  β”‚
β”‚         (useAuth hook)                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         AuthProvider                        β”‚
β”‚    (Zustand Store + Context)                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         AuthService                         β”‚
β”‚  (Business Logic + Validation)              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         AuthStorage                         β”‚
β”‚      (AsyncStorage Wrapper)                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Best Practices

βœ… DO

  • Use guest mode for better onboarding
  • Load auth state on app start
  • Handle offline scenarios gracefully
  • Validate tokens in background
  • Cache user data locally

❌ DON'T

  • Wait for API calls on app start
  • Logout users on network errors
  • Block UI during token validation
  • Store sensitive data beyond JWT token
  • Force registration before trying the app

Compatibility

  • React Native: 0.70+
  • Expo: βœ… Compatible (SDK 47+)
  • React Native Web: βœ… Compatible
  • iOS: βœ… Tested
  • Android: βœ… Tested

Contributing

Contributions are welcome! Please read our Contributing Guide first.

License

MIT Β© LoonyLabs

Support

Acknowledgments

Built with:


Made with ❀️ by Loonylabs

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published