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));