Skip to content

API Auth - Documentazione

Questo documento descrive la struttura delle API di autenticazione basata sulle best practice di React Router con validazione Zod.

Struttura dei file

app/
  ├── api/
  │   └── auth/
  │       └── auth.api.ts    # Funzioni API per autenticazione
  ├── schemas/
  │   ├── api.schema.ts      # Schema Zod base per API
  │   └── auth/
  │       └── auth.schema.ts # Schema Zod per autenticazione
  ├── lib/
  │   ├── api.ts             # Utility API (apiRequest, ApiError)
  │   ├── storage.ts         # Utility localStorage
  │   └── auth/
  │       ├── auth.ts        # Servizio di autenticazione
  │       └── loaders.ts     # Factory functions
  ├── store/
  │   └── auth/
  │       └── auth.store.ts  # Zustand store
  └── hooks/
      └── api/
          └── auth/
              └── useAuthApi.ts  # TanStack Query hooks

Schemi Zod

Gli schemi definiscono e validano i dati inviati e ricevuti dalle API:

LoginRequestSchema

typescript
{
  username: string,
  password: string,
  recaptcha?: string
}

LoginResponseSchema

typescript
{
  success: boolean,
  message: string,
  context: string,
  data: {
    redirect: "main" | "module" | "confirm_login" | "companies_list" | "password_change" | "contract" | "dealer",
    module?: string,
    debug_cookie_data?: any
  }
}

CompanySchema

typescript
{
  id: number,
  name: string
}

SetCompanyRequestSchema

typescript
{
  id: number  // >= 0 (usa 0 per resettare la ditta attiva)
}

UserSettingsSchema

typescript
{
  success: boolean,
  message: string,
  context: string,
  data: {
    id_company: number,
    d_company: string,
    d_user: string,
    multiple_companies: boolean,
    commoncart_btn_fastlabel: boolean,
    commoncart_btn_offline: boolean,
    modules: Array<{
      id: number,
      name: string,
      path: string,
      target?: string
    }>
  }
}

Funzioni API

Tutte le funzioni in app/api/auth/auth.api.ts:

login(credentials: LoginRequest): Promise<LoginResponse>

Esegue il login e restituisce i dati di redirect.

Esempio:

typescript
import { login } from "@/api/auth/auth.api";

const response = await login({
  username: "mario",
  password: "rossi123"
});

console.log(response.data.redirect); // "main", "module", ecc.

checkAuth(): Promise<SessionData>

Verifica lo stato di autenticazione e restituisce i dati di sessione.

Esempio:

typescript
import { checkAuth } from "@/api/auth/auth.api";

const sessionData = await checkAuth();
console.log(sessionData.data);

logout(): Promise<ApiResponse>

Esegue il logout invalidando la sessione sul server.

Esempio:

typescript
import { logout } from "@/api/auth/auth.api";

await logout();

getCompaniesList(): Promise<CompaniesListResponse>

Ottiene la lista delle aziende disponibili per l'utente.

Esempio:

typescript
import { getCompaniesList } from "@/api/auth/auth.api";

const response = await getCompaniesList();
response.data.forEach(company => {
  console.log(`${company.id}: ${company.name}`);
});

setActiveCompany(companyData: SetCompanyRequest): Promise<ApiResponse>

Imposta l'azienda attiva per la sessione. Usa id: 0 per resettare la ditta attiva e tornare all'elenco.

Esempio:

typescript
import { setActiveCompany } from "@/api/auth/auth.api";

// Imposta una ditta specifica
await setActiveCompany({ id: 42 });

// Reset per tornare all'elenco ditte
await setActiveCompany({ id: 0 });

getConfirmLoginData(): Promise<ConfirmLoginData>

Ottiene i dati di un altro utente già autenticato (per conferma).

Esempio:

typescript
import { getConfirmLoginData } from "@/api/auth/auth.api";

const data = await getConfirmLoginData();
console.log(`Browser: ${data.data.browser}, Piattaforma: ${data.data.platform}`);

confirmLogin(): Promise<LoginResponse>

Conferma l'accesso sostituendo la sessione esistente.

Esempio:

typescript
import { confirmLogin } from "@/api/auth/auth.api";

const response = await confirmLogin();
// Gestisci il redirect come nel login

getUserSettings(): Promise<UserSettings>

Ottiene le impostazioni dell'utente inclusi i moduli disponibili.

Esempio:

typescript
import { getUserSettings } from "@/api/auth/auth.api";

const settings = await getUserSettings();
console.log(`Azienda: ${settings.data.d_company}`);
console.log(`Moduli:`, settings.data.modules);

Gestione errori

Tutte le funzioni API lanciano ApiError in caso di errore:

typescript
import { ApiError } from "@/lib/api";

try {
  await login({ username: "test", password: "wrong" });
} catch (error) {
  if (error instanceof ApiError) {
    console.error(`Errore API: ${error.message}`);
    console.error(`Status: ${error.status}`);
    console.error(`Context: ${error.context}`);
  }
}

Gestione automatica errori 401 (Unauthorized)

Quando una chiamata API restituisce un errore 401 e l'utente è autenticato:

  1. Alert automatico: Viene mostrato un alert con il messaggio di errore
  2. Logout automatico: Lo store Zustand viene pulito
  3. Broadcast logout: Notifica alle altre tab tramite BroadcastChannel
  4. Redirect al login: L'utente viene reindirizzato alla pagina di login

Questo comportamento è gestito automaticamente dalla funzione apiRequest in app/lib/api.ts e non richiede gestione manuale nelle singole chiamate API.

Esempio di comportamento:

typescript
// Se l'utente è autenticato e la sua sessione è scaduta
try {
  await getCompaniesList();
} catch (error) {
  // Prima che il catch venga eseguito:
  // 1. Viene mostrato un alert con "Credenziali non valide o sessione scaduta"
  // 2. Lo store viene pulito (logout)
  // 3. L'utente viene reindirizzato a "/"
}

TanStack Query Hooks

Per le chiamate API usa i TanStack Query hooks:

typescript
import { useCompaniesList } from "@/hooks/api/auth/useAuthApi";

function MyComponent() {
  const { data, isLoading, error } = useCompaniesList();

  if (isLoading) return <div>Caricamento...</div>;
  if (error) return <div>Errore: {error.message}</div>;
  if (!data) return null;

  return (
    <ul>
      {data.data.map(company => (
        <li key={company.id}>{company.name}</li>
      ))}
    </ul>
  );
}

AuthService

Il servizio centralizzato in app/lib/auth.ts usa le API internamente:

typescript
import { authService } from "@/lib/auth/auth";

// Login
const response = await authService.login(username, password);
if (response) {
  // Gestisci redirect in base a response.data.redirect
}

// Logout
await authService.logout();

// Check auth
const isAuth = authService.isAuthenticated();

Storage utility

Funzioni helper in app/lib/storage.ts:

typescript
import { getLoginData, clearLoginData, isAuthenticated } from "@/lib/auth/storage";

// Recupera dati di login salvati
const loginData = getLoginData();
if (loginData) {
  console.log(`Tipo redirect: ${loginData.redirect}`);
}

// Pulisci dati
clearLoginData();

// Verifica autenticazione
if (isAuthenticated()) {
  console.log("Utente autenticato");
}

Validazione con Zod

I dati sono automaticamente validati da Zod. In caso di dati non validi, viene lanciato un errore:

typescript
import { LoginRequestSchema } from "@/schemas/auth/auth.schema";

try {
  // Valida manualmente se necessario
  const validData = LoginRequestSchema.parse({
    username: "test",
    password: "test123"
  });
} catch (error) {
  if (error instanceof z.ZodError) {
    console.error(error.errors);
  }
}

Configurazione

L'URL base dell'API è definito in app/api/auth.api.ts:

typescript
const API_BASE_URL = "https://api.local.daisy/erp_auth";

Tutte le richieste includono automaticamente:

  • credentials: "include" per gestire i cookie di sessione
  • Content-Type: application/json header
  • Validazione Zod dei dati inviati e ricevuti

Best Practices

  1. Usa sempre le funzioni API invece di fetch diretto

    typescript
    // ❌ Non fare
    fetch("/erp_auth/login", { ... })
    
    // ✅ Fai così
    import { login } from "@/api/auth/auth.api";
    await login({ username, password });
  2. Gestisci sempre gli errori

    typescript
    try {
      const response = await login({ username, password });
    } catch (error) {
      if (error instanceof ApiError) {
        // Gestisci errore API
      }
    }
  3. Usa gli schemi Zod per validare dati custom

    typescript
    import { LoginRequestSchema } from "@/schemas/auth/auth.schema";
    
    const result = LoginRequestSchema.safeParse(formData);
    if (!result.success) {
      // Mostra errori validazione
    }
  4. Usa TanStack Query hooks per componenti React

    typescript
    const { data, isLoading, error } = useUserSettings();

Flusso di autenticazione completo

typescript
// 1. Login
const response = await authService.login(username, password);
if (!response) {
  // Errore login
  return;
}

// 2. Gestisci redirect
switch (response.data.redirect) {
  case "companies_list":
    // Mostra lista aziende
    const companies = await getCompaniesList();
    // Utente sceglie azienda
    await setActiveCompany({ id: selectedId });
    // Poi ottieni settings
    break;

  case "main":
  case "module":
    // Vai direttamente alla app
    const settings = await getUserSettings();
    // Mostra moduli disponibili
    break;

  case "confirm_login":
    // Altro utente loggato
    const otherUser = await getConfirmLoginData();
    // Mostra conferma
    await confirmLogin();
    break;

  // ... altri casi
}

Documentazione Elerama Frontend