Skip to main content

Authentication Guide

Guide to implement authentication in 54VIE Super App.

Overview

54VIE uses JWT-based authentication with support for:

  • Email/Password login
  • SSO (Google, Apple, Facebook)
  • Biometric authentication
  • Automatic token refresh

In Host App

useAuth Hook

import { useAuth } from '../auth';

function ProfileScreen() {
const {
user, // Current user object
isAuthenticated,// Boolean
isLoading, // Loading state
login, // Email login
register, // Register new account
logout, // Logout
refreshToken, // Manual token refresh
} = useAuth();

if (!isAuthenticated) {
return <LoginScreen />;
}

return <UserProfile user={user} onLogout={logout} />;
}

SSO Login

import { useSSO } from '../auth';

function SSOButtons() {
const {
signInWithGoogle,
signInWithApple,
signInWithFacebook,
isLoading,
} = useSSO();

return (
<View>
<Button
icon="google"
onPress={signInWithGoogle}
loading={isLoading}
>
Continue with Google
</Button>
<Button
icon="apple"
onPress={signInWithApple}
loading={isLoading}
>
Continue with Apple
</Button>
</View>
);
}

Biometric Authentication

import { useBiometric } from '../auth';

function BiometricLogin() {
const {
isSupported,
isEnabled,
biometryType, // 'FaceID' | 'TouchID' | 'Fingerprint'
authenticate,
enable,
disable,
} = useBiometric();

const handleBiometricLogin = async () => {
const success = await authenticate('Login to 54VIE');
if (success) {
// Get stored credentials and login
}
};

if (!isSupported) return null;

return (
<Button onPress={handleBiometricLogin}>
Login with {biometryType}
</Button>
);
}

In Mini-App

Checking Authentication

import { useHostAuth } from '@54vie/miniapp-sdk';

function MiniAppScreen() {
const {
user,
isAuthenticated,
accessToken,
requestLogin,
} = useHostAuth();

if (!isAuthenticated) {
return (
<View>
<Text>Please login to continue</Text>
<Button onPress={requestLogin}>Login</Button>
</View>
);
}

return <AuthenticatedContent user={user} />;
}

API Calls with Auth

import { api, useHostAuth } from '@54vie/miniapp-sdk';

function OrderList() {
const { accessToken } = useHostAuth();

// Token is automatically injected
const orders = await api.get('/orders');

// Or manual if needed
const response = await fetch('https://api.54vie.vn/orders', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
}

Token Management

Token Refresh

Tokens are automatically refreshed when:

  • Token is about to expire (5 minutes before expiry)
  • API returns 401
// In AuthProvider
const AUTH_CONFIG = {
tokenRefreshThreshold: 300, // 5 minutes
autoRefreshTokens: true,
};

Token Storage

Tokens are stored securely:

  • iOS: Keychain
  • Android: Encrypted SharedPreferences
// TokenManager.ts
import * as Keychain from 'react-native-keychain';

class TokenManager {
async setTokens(tokens: AuthTokens) {
await Keychain.setGenericPassword(
'tokens',
JSON.stringify(tokens),
{ service: '54vie-auth' }
);
}

async getTokens(): Promise<AuthTokens | null> {
const result = await Keychain.getGenericPassword({ service: '54vie-auth' });
if (result) {
return JSON.parse(result.password);
}
return null;
}
}

Session Management

// SessionManager.ts
class SessionManager {
private session: Session | null = null;

startSession(user: User) {
this.session = {
user,
startedAt: Date.now(),
lastActiveAt: Date.now(),
};
}

isAuthenticated(): boolean {
if (!this.session) return false;
return !this.isSessionExpired();
}

private isSessionExpired(): boolean {
const timeout = 30 * 60 * 1000; // 30 minutes
return Date.now() - this.session.lastActiveAt > timeout;
}
}

Error Handling

import { useAuth } from '../auth';

function LoginForm() {
const { login, error, clearError } = useAuth();

const handleSubmit = async () => {
try {
await login({ email, password });
} catch (error) {
if (error.code === 'INVALID_CREDENTIALS') {
Toast.error('Invalid email or password');
} else if (error.code === 'ACCOUNT_LOCKED') {
Toast.error('Account locked. Please contact support.');
} else {
Toast.error('Login failed. Please try again.');
}
}
};
}

Best Practices

1. Always check authentication

// ✅ Good
if (!isAuthenticated) {
return <LoginPrompt />;
}

// ❌ Bad - assuming user is logged in
return <ProtectedContent />;

2. Handle token expiry gracefully

// ✅ Good - auto refresh or prompt re-login
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
const refreshed = await tokenManager.refreshToken();
if (refreshed) {
return apiClient.request(error.config);
}
navigation.navigate('Login');
}
throw error;
}
);

3. Secure token storage

// ✅ Good - Use Keychain/EncryptedStorage
await Keychain.setGenericPassword('tokens', JSON.stringify(tokens));

// ❌ Bad - Plain AsyncStorage
await AsyncStorage.setItem('tokens', JSON.stringify(tokens));