Uecko_ERP/docs/frontend/auth-and-company-selection.md

175 lines
3.5 KiB
Markdown
Raw Normal View History

2026-07-06 12:04:47 +00:00
# Frontend Auth And Company Selection
## Runtime actual
El frontend ERP usa el runtime nuevo:
```txt
@erp/identity/client
IdentityAuthSessionProvider
DataSourceProvider
Axios compartido
```
No debe usarse `@erp/auth/client` como runtime nuevo.
## Flujo actual
```txt
login
-> GET /identity/auth/session
-> GET /companies/available
-> resolver empresa activa
-> auto-selección si hay una sola empresa
-> selector si hay varias
-> /no-companies si no hay empresas
-> X-Company-Id en rutas tenant-scoped
```
## Login
El login usa:
```http
POST /identity/auth/login
```
Tras login correcto:
- se guardan `accessToken` y `refreshToken`
- se limpia cualquier `activeCompanyId` previo
- se carga `GET /companies/available`
- se resuelve el redirect autenticado según companies disponibles
## Session restore
Si existe `accessToken` persistido:
1. se llama `GET /identity/auth/session`
2. se hidrata la sesión autenticada
3. se llama `GET /companies/available`
4. se resuelve `activeCompanyId` persistido o auto-selección
Si la restauración falla:
- se limpia la sesión
- se limpia `activeCompanyId`
## Selección de empresa
### Cero empresas
Si `companies.length === 0`:
- se muestra `/no-companies`
- no existe `activeCompanyId`
- no se envía `X-Company-Id`
- sigue disponible el logout
### Una empresa
Si `companies.length === 1`:
- se selecciona automáticamente
- se guarda `activeCompanyId`
- no se muestra `/company-selection`
- se continúa al área privada
### Varias empresas
Si `companies.length > 1`:
- se muestra `/company-selection`
- no existe `activeCompanyId` hasta elegir
- el usuario selecciona company
- se guarda `activeCompanyId`
- se continúa al área privada
### `activeCompanyId` persistido ya no disponible
Si el `activeCompanyId` persistido ya no aparece en `GET /companies/available`:
- con `0` companies -> `/no-companies`
- con `1` company -> auto-selección de la disponible
- con varias companies -> `/company-selection`
## `GET /companies/available`
Endpoint actual:
```http
GET /companies/available
Authorization: Bearer <access_token>
```
No requiere:
- `X-Company-Id`
No usa:
- `requireIdentityTenant(params)`
Devuelve:
```ts
export type AvailableCompaniesResponseDTO = {
companies: AvailableCompanyDTO[];
};
```
## Headers
### No deben llevar `X-Company-Id`
```txt
POST /identity/auth/login
POST /identity/auth/refresh
POST /identity/auth/logout
GET /identity/auth/session
GET /companies/available
```
### Sí deben llevar `X-Company-Id`
Rutas tenant-scoped como:
- `/customers`
- `/catalogs/*`
- `/customer-invoices/*`
- `/factuges`
siempre que exista `activeCompanyId`.
## Refresh automático sobre `401`
El frontend implementa refresh automático global sobre Axios.
Comportamiento:
1. una request autenticada recibe `401`
2. si existe `refreshToken`, se llama `POST /identity/auth/refresh`
3. se guardan los nuevos tokens
4. se reintenta una única vez la request original
5. si refresh falla, se limpia la sesión y se redirige a `/login`
## Reglas implementadas
- retry único por request con `_retry`
- refresh concurrente único con `refreshPromise` compartida
- varias requests `401` esperan el mismo refresh
- login `401` no dispara refresh
- refresh `401` no intenta refrescarse a sí mismo
- `/identity/auth/refresh` no lleva `X-Company-Id`
- la request tenant-scoped reintentada conserva `X-Company-Id`
## Endpoints excluidos de tenant header y refresh
```txt
/identity/auth/login
/identity/auth/refresh
/identity/auth/logout
/identity/auth/session
/companies/available
```