React Native authentication utilities for token management, biometric authentication, and secure key storage. Works with Expo and bare React Native.
npm install @egintegrations/auth-servicesReact Native authentication utilities for token management, biometric authentication, and secure key storage. Works with Expo and bare React Native.
- TokenManager: Secure token storage and retrieval
- BiometricAuth: Face ID / Touch ID authentication
- SecureKeyStore: Generic secure key/value storage
- TypeScript: Full type safety with TypeScript support
- Configurable: Customizable storage keys, prompts, and behavior
- Well-tested: 100% test coverage (69 tests)
- Dual Module: ESM and CommonJS support
``bash`
npm install @egintegrations/auth-services expo-secure-store expo-local-authentication
This package requires:
- expo-secure-store >= 12.0.0expo-local-authentication
- >= 13.0.0 react-native
- >= 0.70.0
`typescript
import { TokenManager, BiometricAuth, SecureKeyStore } from '@egintegrations/auth-services';
// Token management
const tokenManager = new TokenManager();
await tokenManager.saveToken('your-auth-token');
const token = await tokenManager.getToken();
// Biometric authentication
const bioAuth = new BiometricAuth();
if (await bioAuth.canAuthenticate()) {
await bioAuth.enable('username');
const username = await bioAuth.authenticate();
}
// Secure key storage
const keyStore = new SecureKeyStore({ keyPrefix: 'app_' });
await keyStore.set('api_key', 'secret-key');
const apiKey = await keyStore.get('api_key');
`
Manages authentication tokens in secure storage.
`typescript
import { TokenManager } from '@egintegrations/auth-services';
// Create manager with default storage key
const tokenManager = new TokenManager();
// Save token
await tokenManager.saveToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
// Get token
const token = await tokenManager.getToken();
// Check if token exists
const hasToken = await tokenManager.hasToken();
// Clear token
await tokenManager.clearToken();
`
`typescript
// Use custom storage key for multiple token types
const accessTokenManager = new TokenManager({ storageKey: 'access_token' });
const refreshTokenManager = new TokenManager({ storageKey: 'refresh_token' });
await accessTokenManager.saveToken('access-token-123');
await refreshTokenManager.saveToken('refresh-token-456');
`
For simple apps with single token:
`typescript
import { SingletonTokenManager } from '@egintegrations/auth-services';
// All methods are static
await SingletonTokenManager.saveToken('token');
const token = await SingletonTokenManager.getToken();
const hasToken = await SingletonTokenManager.hasToken();
await SingletonTokenManager.clearToken();
`
Manages biometric authentication (Face ID / Touch ID).
`typescript
import { BiometricAuth } from '@egintegrations/auth-services';
const bioAuth = new BiometricAuth();
// Check if device supports biometric auth
const canAuth = await bioAuth.canAuthenticate();
// Get biometric type
const type = await bioAuth.getBiometricType(); // 'faceId' | 'touchId' | 'fingerprint' | 'none'
`
`typescript
const bioAuth = new BiometricAuth();
try {
// Prompt user to authenticate and enable biometric login
await bioAuth.enable('username');
console.log('Biometric login enabled');
} catch (error) {
console.error('Failed to enable biometric:', error.message);
}
`
`typescript
const bioAuth = new BiometricAuth();
try {
// Prompt biometric authentication
const username = await bioAuth.authenticate();
console.log('Authenticated as:', username);
} catch (error) {
console.error('Authentication failed:', error.message);
}
`
`typescript
const bioAuth = new BiometricAuth({
promptMessages: {
enable: 'Enable Face ID for MyApp',
authenticate: 'Sign in to MyApp',
},
fallbackLabel: 'Use Password',
});
await bioAuth.enable('user@example.com');
const username = await bioAuth.authenticate();
`
`typescript
const bioAuth = new BiometricAuth();
// Save username without prompting
await bioAuth.saveUsername('newuser@example.com');
// Get saved username (only if biometric is enabled)
const savedUsername = await bioAuth.getSavedUsername();
// Clear saved username
await bioAuth.clearSavedUsername();
// Disable biometric completely
await bioAuth.disable();
`
`typescript
import { BiometricAuth, TokenManager } from '@egintegrations/auth-services';
const bioAuth = new BiometricAuth();
const tokenManager = new TokenManager();
async function loginWithBiometric() {
try {
// Check if biometric is enabled
const isEnabled = await bioAuth.isEnabled();
if (!isEnabled) {
console.log('Biometric not enabled, use password login');
return;
}
// Authenticate
const username = await bioAuth.authenticate();
// Call your API to get token
const response = await fetch('https://api.example.com/auth/biometric', {
method: 'POST',
body: JSON.stringify({ username }),
});
const { token } = await response.json();
// Save token
await tokenManager.saveToken(token);
console.log('Logged in as:', username);
} catch (error) {
console.error('Biometric login failed:', error.message);
}
}
`
Generic secure key-value storage with namespace support.
`typescript
import { SecureKeyStore } from '@egintegrations/auth-services';
const store = new SecureKeyStore();
// Store value
await store.set('api_key', 'sk-1234567890');
// Retrieve value
const apiKey = await store.get('api_key');
// Check if key exists
const hasKey = await store.has('api_key');
// Delete key
await store.delete('api_key');
`
`typescript
// Create separate stores for different purposes
const apiStore = new SecureKeyStore({ keyPrefix: 'api_' });
const userStore = new SecureKeyStore({ keyPrefix: 'user_' });
await apiStore.set('openai_key', 'sk-...');
await userStore.set('preference', 'dark_mode');
// Keys are isolated
const apiKeys = await apiStore.get('openai_key'); // Works
const userKeys = await userStore.get('openai_key'); // null (different namespace)
`
`typescript
const store = new SecureKeyStore({ keyPrefix: 'app_' });
await store.set('key1', 'value1');
await store.set('key2', 'value2');
await store.set('key3', 'value3');
// Clear specific keys
await store.clear(['key1', 'key2']);
`
For simple use cases without creating a store instance:
`typescript
import {
getSecureKey,
setSecureKey,
deleteSecureKey,
hasSecureKey,
} from '@egintegrations/auth-services';
// Direct access to secure storage (no prefix)
await setSecureKey('my_key', 'my_value');
const value = await getSecureKey('my_key');
const exists = await hasSecureKey('my_key');
await deleteSecureKey('my_key');
`
`typescript
import {
TokenManager,
BiometricAuth,
SecureKeyStore,
} from '@egintegrations/auth-services';
class AuthService {
private tokenManager = new TokenManager();
private bioAuth = new BiometricAuth({
promptMessages: {
enable: 'Enable Face ID for faster login',
authenticate: 'Authenticate to continue',
},
});
private keyStore = new SecureKeyStore({ keyPrefix: 'auth_' });
async login(username: string, password: string): Promise
// Call your API
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const { token } = await response.json();
// Save token
await this.tokenManager.saveToken(token);
// Offer biometric enrollment if available
const canAuth = await this.bioAuth.canAuthenticate();
if (canAuth) {
await this.bioAuth.enable(username);
}
}
async loginWithBiometric(): Promise
const username = await this.bioAuth.authenticate();
// Exchange biometric authentication for token
const response = await fetch('https://api.example.com/auth/biometric', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username }),
});
const { token } = await response.json();
await this.tokenManager.saveToken(token);
}
async logout(): Promise
await this.tokenManager.clearToken();
// Keep biometric enabled for next login
}
async getAuthToken(): Promise
return this.tokenManager.getToken();
}
async isAuthenticated(): Promise
return this.tokenManager.hasToken();
}
async saveApiKey(service: string, key: string): Promise
await this.keyStore.set(${service}_key, key);
}
async getApiKey(service: string): Promise
return this.keyStore.get(${service}_key);
}
}
// Usage
const auth = new AuthService();
// Normal login
await auth.login('user@example.com', 'password123');
// Biometric login
if (await auth.bioAuth.canAuthenticate()) {
await auth.loginWithBiometric();
}
// Get auth token for API calls
const token = await auth.getAuthToken();
`
`typescript
import { TokenManager } from '@egintegrations/auth-services';
import { ApiClient } from '@egintegrations/api-client';
const tokenManager = new TokenManager();
// Create API client
const apiClient = new ApiClient({
baseUrl: 'https://api.example.com',
});
// Add token to all requests
apiClient.addRequestInterceptor(async (config) => {
const token = await tokenManager.getToken();
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = Bearer ${token};
}
return config;
});
// Handle 401 errors
apiClient.addErrorInterceptor(async (error) => {
if (error.response?.status === 401) {
await tokenManager.clearToken();
// Redirect to login
}
throw error;
});
`
#### Constructor
`typescript`
new TokenManager(config?: TokenManagerConfig)
Config:
- storageKey?: string - Storage key (default: 'auth_token')
#### Methods
- getToken(): Promise - Get stored tokensaveToken(token: string): Promise
- - Save tokenclearToken(): Promise
- - Delete tokenhasToken(): Promise
- - Check if token exists
Static methods matching TokenManager interface.
#### Constructor
`typescript`
new BiometricAuth(config?: BiometricAuthConfig)
Config:
- savedUsernameKey?: string - Username storage key (default: 'biometric_username')enabledKey?: string
- - Enabled flag key (default: 'biometric_enabled')promptMessages?: { enable?: string, authenticate?: string }
- - Custom promptsfallbackLabel?: string
- - Fallback button label (default: 'Use password')
#### Methods
- canAuthenticate(): Promise - Check device supportisEnabled(): Promise
- - Check if biometric enabledenable(username: string): Promise
- - Enable biometric logindisable(): Promise
- - Disable biometric logingetBiometricType(): Promise
- - Get biometric typeauthenticate(): Promise
- - Authenticate and get usernamesaveUsername(username: string): Promise
- - Save usernameclearSavedUsername(): Promise
- - Clear usernamegetSavedUsername(): Promise
- - Get saved username
#### Constructor
`typescript`
new SecureKeyStore(config?: SecureKeyStoreConfig)
Config:
- keyPrefix?: string - Key prefix for namespace isolation (default: 'secure_key_')
#### Methods
- get(key: string): Promise - Get valueset(key: string, value: string): Promise
- - Set valuedelete(key: string): Promise
- - Delete keyhas(key: string): Promise
- - Check if key existsclear(keys?: string[]): Promise
- - Clear multiple keys
All methods can throw errors. Always use try-catch:
`typescript
try {
await tokenManager.saveToken(token);
} catch (error) {
console.error('Failed to save token:', error);
}
try {
const username = await bioAuth.authenticate();
} catch (error) {
console.error('Biometric auth failed:', error.message);
// Handle failure (show password login)
}
`
`bashInstall dependencies
npm install
Contributing
Contributions are welcome! Please ensure:
1. All tests pass (
npm test)
2. Code coverage remains 100%
3. TypeScript types are properly defined
4. Code follows the existing style (run npm run lint`)MIT © EGI Integrations
This package was extracted from AutismTrainerApp's authentication services and refactored to be reusable across React Native applications.
What was removed:
- App-specific user management (roles, permissions, progress tracking)
- Local user database and AsyncStorage user management
- Supervised user relationships
- Application-specific authentication flows
What was kept and improved:
- Token management (now configurable)
- Biometric authentication (now with custom prompts)
- Secure key storage (now with namespace support)
- @egintegrations/api-client - HTTP client for API integrations
- @egintegrations/core-utils - Utility functions
For issues and questions, please open an issue on GitHub.