User Service
Gestion du profil utilisateur, préférences, authentification 2FA et appareils connectés.
Overview
Le User Service centralise toutes les opérations liées au profil utilisateur, incluant la gestion des informations personnelles, les paramètres de sécurité et les préférences de l'application.
Profile
Informations personnelles
2FA
Authentification forte
Devices
Appareils connectés
Settings
Préférences app
Profile Management
Gestion complète du profil utilisateur avec upload d'avatar.
src/services/userService.tstypescript
import { apiClient } from './api/client';
export interface User {
id: string;
email: string;
phone?: string;
firstName: string;
lastName: string;
avatar?: string;
dateOfBirth?: string;
address?: Address;
kycLevel: 'NONE' | 'BASIC' | 'INTERMEDIATE' | 'ADVANCED';
createdAt: string;
updatedAt: string;
}
export interface Address {
street: string;
city: string;
postalCode: string;
country: string;
}
export interface UpdateProfileDTO {
firstName?: string;
lastName?: string;
phone?: string;
dateOfBirth?: string;
address?: Partial<Address>;
}
export const userService = {
// Get current user profile
async getProfile(): Promise<User> {
const { data } = await apiClient.get<User>('/users/me');
return data;
},
// Update profile
async updateProfile(updates: UpdateProfileDTO): Promise<User> {
const { data } = await apiClient.patch<User>('/users/me', updates);
return data;
},
// Upload avatar
async uploadAvatar(imageUri: string): Promise<{ avatarUrl: string }> {
const formData = new FormData();
formData.append('avatar', {
uri: imageUri,
type: 'image/jpeg',
name: 'avatar.jpg',
} as unknown as Blob);
const { data } = await apiClient.post<{ avatarUrl: string }>(
'/users/me/avatar',
formData,
{
headers: { 'Content-Type': 'multipart/form-data' },
}
);
return data;
},
// Delete avatar
async deleteAvatar(): Promise<void> {
await apiClient.delete('/users/me/avatar');
},
// Change email (requires verification)
async requestEmailChange(newEmail: string): Promise<void> {
await apiClient.post('/users/me/email/change-request', { newEmail });
},
// Verify email change with OTP
async verifyEmailChange(code: string): Promise<void> {
await apiClient.post('/users/me/email/verify', { code });
},
// Change phone number
async requestPhoneChange(newPhone: string): Promise<void> {
await apiClient.post('/users/me/phone/change-request', { newPhone });
},
// Verify phone change with SMS code
async verifyPhoneChange(code: string): Promise<void> {
await apiClient.post('/users/me/phone/verify', { code });
},
// Delete account (requires confirmation)
async deleteAccount(password: string, reason?: string): Promise<void> {
await apiClient.delete('/users/me', {
data: { password, reason },
});
},
};User Preferences
Gestion des préférences utilisateur stockées côté serveur.
src/services/preferencesService.tstypescript
export interface UserPreferences {
// Notifications
notifications: {
push: boolean;
email: boolean;
sms: boolean;
marketing: boolean;
transactionAlerts: boolean;
securityAlerts: boolean;
};
// Display
display: {
language: 'fr' | 'en';
theme: 'light' | 'dark' | 'system';
currency: string;
compactAmounts: boolean;
};
// Privacy
privacy: {
showBalanceOnHome: boolean;
requireAuthForSensitiveData: boolean;
shareAnalytics: boolean;
};
// Security
security: {
biometricEnabled: boolean;
pinEnabled: boolean;
autoLockTimeout: number; // minutes
hideNotificationContent: boolean;
};
}
export const preferencesService = {
async getPreferences(): Promise<UserPreferences> {
const { data } = await apiClient.get<UserPreferences>('/users/me/preferences');
return data;
},
async updatePreferences(updates: Partial<UserPreferences>): Promise<UserPreferences> {
const { data } = await apiClient.patch<UserPreferences>(
'/users/me/preferences',
updates
);
return data;
},
async updateNotificationPreferences(
notifications: Partial<UserPreferences['notifications']>
): Promise<void> {
await apiClient.patch('/users/me/preferences/notifications', notifications);
},
async updateSecurityPreferences(
security: Partial<UserPreferences['security']>
): Promise<void> {
await apiClient.patch('/users/me/preferences/security', security);
},
async updatePrivacyPreferences(
privacy: Partial<UserPreferences['privacy']>
): Promise<void> {
await apiClient.patch('/users/me/preferences/privacy', privacy);
},
};Two-Factor Authentication
Service d'authentification à deux facteurs avec TOTP et backup codes.
src/services/twoFactorService.tstypescript
export interface TwoFactorSetup {
secret: string;
qrCodeUrl: string;
backupCodes: string[];
}
export interface TwoFactorStatus {
enabled: boolean;
enabledAt?: string;
methods: ('totp' | 'sms' | 'email')[];
backupCodesRemaining: number;
}
export const twoFactorService = {
// Get 2FA status
async getStatus(): Promise<TwoFactorStatus> {
const { data } = await apiClient.get<TwoFactorStatus>('/users/me/2fa/status');
return data;
},
// Start TOTP setup (returns QR code)
async setupTOTP(): Promise<TwoFactorSetup> {
const { data } = await apiClient.post<TwoFactorSetup>('/users/me/2fa/totp/setup');
return data;
},
// Verify and enable TOTP
async verifyAndEnableTOTP(code: string): Promise<{ backupCodes: string[] }> {
const { data } = await apiClient.post('/users/me/2fa/totp/verify', { code });
return data;
},
// Disable 2FA (requires password)
async disable2FA(password: string, code: string): Promise<void> {
await apiClient.post('/users/me/2fa/disable', { password, code });
},
// Regenerate backup codes
async regenerateBackupCodes(password: string): Promise<{ backupCodes: string[] }> {
const { data } = await apiClient.post('/users/me/2fa/backup-codes/regenerate', {
password,
});
return data;
},
// Verify 2FA code (for sensitive operations)
async verifyCode(code: string): Promise<{ verified: boolean }> {
const { data } = await apiClient.post('/users/me/2fa/verify', { code });
return data;
},
// Setup SMS 2FA
async setupSMS(phoneNumber: string): Promise<void> {
await apiClient.post('/users/me/2fa/sms/setup', { phoneNumber });
},
// Verify SMS code and enable
async verifySMS(code: string): Promise<void> {
await apiClient.post('/users/me/2fa/sms/verify', { code });
},
};Device Management
Gestion des appareils connectés au compte avec révocation de sessions.
src/services/deviceService.tstypescript
export interface Device {
id: string;
name: string;
type: 'mobile' | 'tablet' | 'desktop' | 'web';
platform: 'ios' | 'android' | 'web' | 'windows' | 'macos';
lastActiveAt: string;
location?: {
city: string;
country: string;
};
isCurrent: boolean;
trusted: boolean;
}
export interface Session {
id: string;
deviceId: string;
ipAddress: string;
userAgent: string;
createdAt: string;
lastActiveAt: string;
expiresAt: string;
isCurrent: boolean;
}
export const deviceService = {
// Get all devices
async getDevices(): Promise<Device[]> {
const { data } = await apiClient.get<Device[]>('/users/me/devices');
return data;
},
// Get current device info
async getCurrentDevice(): Promise<Device> {
const { data } = await apiClient.get<Device>('/users/me/devices/current');
return data;
},
// Register current device for push notifications
async registerDevice(pushToken: string): Promise<void> {
const deviceInfo = await getDeviceInfo();
await apiClient.post('/users/me/devices/register', {
...deviceInfo,
pushToken,
});
},
// Mark device as trusted
async trustDevice(deviceId: string): Promise<void> {
await apiClient.post(`/users/me/devices/${deviceId}/trust`);
},
// Revoke device access
async revokeDevice(deviceId: string): Promise<void> {
await apiClient.delete(`/users/me/devices/${deviceId}`);
},
// Get all active sessions
async getSessions(): Promise<Session[]> {
const { data } = await apiClient.get<Session[]>('/users/me/sessions');
return data;
},
// Revoke specific session
async revokeSession(sessionId: string): Promise<void> {
await apiClient.delete(`/users/me/sessions/${sessionId}`);
},
// Revoke all sessions except current
async revokeAllOtherSessions(): Promise<void> {
await apiClient.post('/users/me/sessions/revoke-all');
},
};
// Helper to get device info
async function getDeviceInfo() {
const deviceName = Device.deviceName || Device.modelName || 'Unknown Device';
const platform = Platform.OS as 'ios' | 'android';
return {
name: deviceName,
type: Device.deviceType === 2 ? 'tablet' : 'mobile',
platform,
osVersion: Platform.Version.toString(),
appVersion: Constants.expoConfig?.version || '1.0.0',
deviceId: await getUniqueId(),
};
}React Query Hooks
Hooks React Query pour la gestion des données utilisateur avec cache.
src/hooks/useUser.tstypescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { userService, User, UpdateProfileDTO } from '@/services/userService';
import { preferencesService, UserPreferences } from '@/services/preferencesService';
import { deviceService, Device, Session } from '@/services/deviceService';
// Query keys
export const userKeys = {
all: ['user'] as const,
profile: () => [...userKeys.all, 'profile'] as const,
preferences: () => [...userKeys.all, 'preferences'] as const,
devices: () => [...userKeys.all, 'devices'] as const,
sessions: () => [...userKeys.all, 'sessions'] as const,
};
// Profile hooks
export function useProfile() {
return useQuery({
queryKey: userKeys.profile(),
queryFn: userService.getProfile,
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useUpdateProfile() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (updates: UpdateProfileDTO) => userService.updateProfile(updates),
onSuccess: (data) => {
queryClient.setQueryData(userKeys.profile(), data);
},
});
}
export function useUploadAvatar() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: userService.uploadAvatar,
onSuccess: (data) => {
queryClient.setQueryData(userKeys.profile(), (old: User | undefined) =>
old ? { ...old, avatar: data.avatarUrl } : old
);
},
});
}
// Preferences hooks
export function usePreferences() {
return useQuery({
queryKey: userKeys.preferences(),
queryFn: preferencesService.getPreferences,
staleTime: 10 * 60 * 1000, // 10 minutes
});
}
export function useUpdatePreferences() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: preferencesService.updatePreferences,
onSuccess: (data) => {
queryClient.setQueryData(userKeys.preferences(), data);
},
});
}
// Device hooks
export function useDevices() {
return useQuery({
queryKey: userKeys.devices(),
queryFn: deviceService.getDevices,
});
}
export function useRevokeDevice() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: deviceService.revokeDevice,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: userKeys.devices() });
},
});
}
// Session hooks
export function useSessions() {
return useQuery({
queryKey: userKeys.sessions(),
queryFn: deviceService.getSessions,
});
}
export function useRevokeSession() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: deviceService.revokeSession,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: userKeys.sessions() });
},
});
}