All files / src/api auth.ts

100% Statements 17/17
100% Branches 0/0
100% Functions 6/6
100% Lines 17/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135                                                                                                                                                                4x 51x 39x           4x 4x           4x 11x 7x           4x 6x 2x           4x       8x       3x           4x     8x       4x    
/**
 * Authentication API
 */
 
import apiClient from './client';
 
export interface LoginCredentials {
  email: string;
  password: string;
}
 
import { UserRole } from '../types';
 
export interface QuotaOverage {
  id: number;
  quota_type: string;
  display_name: string;
  current_usage: number;
  allowed_limit: number;
  overage_amount: number;
  days_remaining: number;
  grace_period_ends_at: string;
}
 
export interface MasqueradeStackEntry {
  user_id: number;
  username: string;
  role: UserRole;
  business_id?: number;
  business_subdomain?: string;
}
 
export interface LoginResponse {
  // Regular login success
  access?: string;
  refresh?: string;
  user?: {
    id: number;
    username: string;
    email: string;
    name: string;
    role: UserRole;
    avatar_url?: string;
    email_verified?: boolean;
    is_staff: boolean;
    is_superuser: boolean;
    business?: number;
    business_name?: string;
    business_subdomain?: string;
  };
  masquerade_stack?: MasqueradeStackEntry[];
  // MFA challenge response
  mfa_required?: boolean;
  user_id?: number;
  mfa_methods?: ('SMS' | 'TOTP' | 'BACKUP')[];
  phone_last_4?: string;
}
 
export interface User {
  id: number;
  username: string;
  email: string;
  name: string;
  role: UserRole;
  avatar_url?: string;
  email_verified?: boolean;
  is_staff: boolean;
  is_superuser: boolean;
  business?: number;
  business_name?: string;
  business_subdomain?: string;
  permissions?: Record<string, boolean>;
  can_invite_staff?: boolean;
  can_access_tickets?: boolean;
  quota_overages?: QuotaOverage[];
}
 
/**
 * Login user
 */
export const login = async (credentials: LoginCredentials): Promise<LoginResponse> => {
  const response = await apiClient.post<LoginResponse>('/auth/login/', credentials);
  return response.data;
};
 
/**
 * Logout user
 */
export const logout = async (): Promise<void> => {
  await apiClient.post('/auth/logout/');
};
 
/**
 * Get current user
 */
export const getCurrentUser = async (): Promise<User> => {
  const response = await apiClient.get<User>('/auth/me/');
  return response.data;
};
 
/**
 * Refresh access token
 */
export const refreshToken = async (refresh: string): Promise<{ access: string }> => {
  const response = await apiClient.post('/auth/refresh/', { refresh });
  return response.data;
};
 
/**
 * Masquerade as another user (hijack)
 */
export const masquerade = async (
  user_pk: number,
  hijack_history?: MasqueradeStackEntry[]
): Promise<LoginResponse> => {
  const response = await apiClient.post<LoginResponse>(
    '/auth/hijack/acquire/',
    { user_pk, hijack_history }
  );
  return response.data;
};
 
/**
 * Stop masquerading and return to previous user
 */
export const stopMasquerade = async (
  masquerade_stack: MasqueradeStackEntry[]
): Promise<LoginResponse> => {
  const response = await apiClient.post<LoginResponse>(
    '/auth/hijack/release/',
    { masquerade_stack }
  );
  return response.data;
};