# Autenticación, farmacias, registro y roles

La SPA usa Sanctum stateful: no recibe ni envía Bearer tokens. Configura Axios con `withCredentials: true`, `withXSRFToken: true` y `Accept: application/json`. Antes de operaciones `POST`, solicitar `GET /sanctum/csrf-cookie`. React no debe leer ni borrar la cookie HttpOnly; al iniciar debe consultar `/api/v1/auth/me`.

```js
export const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
  withCredentials: true,
  withXSRFToken: true,
  headers: { Accept: 'application/json' },
});
```

Éxitos contienen `success`, `message`, `data`; errores contienen `success`, `message`, `errors`, `error_code`. Para `VALIDATION_ERROR` y `REGISTRATION_DATA_ALREADY_EXISTS`, mostrar `errors[campo]`. Para `401 UNAUTHENTICATED`, limpiar el estado local. Nunca usar roles como única medida de seguridad: Laravel aplica la autorización real.

## Pantallas y flujo recomendado

- Pantalla de registro:
  1. El usuario escribe el código de farmacia.
  2. React consulta `GET /api/v1/public/pharmacies/by-code/{code}`.
  3. Si responde `200`, guardar `data.id` como `pharmacy_id`.
  4. Enviar `POST /api/v1/auth/register` usando ese `pharmacy_id`.

- Pantalla de login:
  1. Solicitar `GET /sanctum/csrf-cookie`.
  2. Enviar `POST /api/v1/auth/login`.
  3. Consultar `GET /api/v1/auth/me` para hidratar el usuario autenticado.

- Carga inicial de la app o refresco del navegador:
  1. Consultar `GET /api/v1/auth/me`.
  2. Si responde `200`, mantener sesión.
  3. Si responde `401`, tratar al usuario como no autenticado.

- Acción de logout:
  1. Enviar `POST /api/v1/auth/logout`.
  2. Limpiar el estado local del usuario.
  3. Redirigir a login o landing pública según el flujo de la SPA.

## Buscar farmacia

`GET /api/v1/public/pharmacies/by-code/{code}` es público, limitado a 30/minuto por IP. El código se normaliza (trim y mayúsculas). Una respuesta 200 es:

```json
{
  "success": true,
  "message": "Farmacia encontrada correctamente.",
  "data": {
    "id": "8d20d56e-83ce-48c7-a713-b26aee541a42",
    "code": "FAR001",
    "name": "Farmacia Ejemplo",
    "city": "Santiago"
  }
}
```

`data.id` es el UUID público, no el ID numérico interno. Conservarlo para el registro. Una farmacia inexistente o inactiva devuelve 404 `PHARMACY_NOT_FOUND`.

## Solicitud de registro

`POST /api/v1/auth/register` es público y limitado a 5/minuto por IP. React debe enviar el UUID público obtenido como `pharmacy_id`; el backend vuelve a buscar la farmacia, confirma que está activa y persiste internamente su ID numérico en `registration_requests.pharmacy_id`.

| Campo | Tipo | Obligatorio | Nullable | Confirmación | Reglas |
|---|---|---:|---:|---:|---|
| pharmacy_id | UUID | Sí | No | No | UUID de farmacia pública activa; nunca ID numérico |
| first_name, last_name, city | string | Sí | No | No | máximo 100 |
| whatsapp | string | Sí | No | Sí | elimina espacios, paréntesis y guiones |
| phone | string | No | Sí | No | normalizado como teléfono |
| identification_number | string | Sí | No | No | elimina separadores; único global |
| username | string | Sí | No | No | minúsculas, sin espacios; único global |
| password | string | Sí | No | Sí | mínimo 8 |
| email | string | No | Sí | No | minúsculas; vacío es null; único si existe |
| nickname | string | No | Sí | No | máximo 100 |
| terms_accepted | boolean | Sí | No | No | exactamente `true` |

No enviar `pharmacy_code`, `status`, fechas de revisión, `reviewed_by`, datos de auditoría, `role`, `role_id`, `roles` ni `permissions`. El usuario no elige su rol. La respuesta exitosa es 201 y crea solamente una solicitud `pending`: no crea usuario, no inicia sesión ni asigna roles. Estas solicitudes representan futuros usuarios `dependent`; al implementar la aprobación se deberá crear el usuario y, dentro de la misma transacción, usar `$user->assignRole('dependent')`, sin tomar el rol del payload.

Ejemplo de payload:

```json
{
  "pharmacy_id": "8d20d56e-83ce-48c7-a713-b26aee541a42",
  "first_name": "Albert",
  "last_name": "Mata",
  "whatsapp": "(809) 555-1234",
  "whatsapp_confirmation": "8095551234",
  "phone": "809-555-9876",
  "identification_number": "001-1234567-8",
  "username": "Albert01",
  "password": "Password123!",
  "password_confirmation": "Password123!",
  "email": "USER@example.com",
  "city": "Santo Domingo",
  "nickname": "Beto",
  "terms_accepted": true
}
```

Ejemplo de respuesta `201`:

```json
{
  "success": true,
  "message": "Tu solicitud de registro fue recibida y está pendiente de revisión.",
  "data": []
}
```

Errores posibles: 422 `VALIDATION_ERROR`, 422 `REGISTRATION_DATA_ALREADY_EXISTS`, 404 `PHARMACY_NOT_FOUND`, 429 `TOO_MANY_REQUESTS`.

## Sesión y usuario

`POST /api/v1/auth/login` recibe `{ "login": "usuario o correo", "password": "…", "remember": false }`. Está limitado a 5 intentos/minuto por login normalizado e IP. Solo autentica usuarios activos y devuelve 401 `INVALID_CREDENTIALS` para cualquier fallo. No devuelve tokens.

`GET /api/v1/auth/me` requiere sesión. Ambos endpoints devuelven el mismo usuario, incluyendo roles públicos:

```json
{
  "success": true,
  "message": "Usuario autenticado.",
  "data": {
    "user": {
      "id": "uuid-del-usuario",
      "first_name": "Albert",
      "last_name": "Martinez",
      "username": "albertmat34",
      "email": "albert@example.com",
      "status": "active",
      "roles": ["dependent"],
      "pharmacy": {
        "id": "uuid-publico-de-farmacia",
        "code": "FAR001",
        "name": "Farmacia Ejemplo"
      }
    }
  }
}
```

No se exponen IDs internos de farmacia o roles, `guard_name`, pivotes, password ni tokens.

`POST /api/v1/auth/logout` requiere sesión, la invalida y responde `200`. Ejemplo:

```json
{
  "success": true,
  "message": "Sesión cerrada correctamente.",
  "data": []
}
```
