
# Obtener el estado de inicio de sesión de un usuario

`GET /v1/users/{user_id}/sign-in-state` · permiso `users:read`

Devuelve lo que su equipo de soporte necesita cuando un usuario no puede iniciar sesión: los bloqueos de cuenta abiertos del usuario, los recuentos de fallos que siguen activos en cada ruta de inicio de sesión y la última autenticación correcta. La llamada solo lee; no se modifica nada ni se escribe ningún registro de auditoría. Para eliminar los bloqueos, use [`POST /v1/users/{user_id}:unlock`](unlock.md).

La respuesta nunca incluye el identificador interno del contador, una dirección de correo electrónico, una dirección IP, un dispositivo ni un identificador de sesión. Para las sesiones del usuario, use [`GET /v1/users/{user_id}/sessions`](list-sessions.md).

## Alcance de la organización

Envíe `org_id` para generar una vista de diagnóstico de miembros para un administrador de la organización. La clave también necesita el permiso `organizations:read`; sin él, la solicitud falla con `403 insufficient_permissions` antes de leer nada. Si el usuario no es miembro de esa organización (sin membresía `invited`, `active` o `suspended`), la solicitud falla con `404 not_found`, la misma respuesta que para un usuario desconocido. Si el usuario es miembro, `last_sign_in` procede solo de las sesiones en el contexto de esa organización, y `failures` siempre es un array vacío, porque los contadores de fallos no se atribuyen a ninguna organización. Los bloqueos se siguen devolviendo, ya que un bloqueo detiene el inicio de sesión de la cuenta en todos los contextos. Sin `org_id`, la respuesta no cambia.

## Solicitud

| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| `user_id` | path | sí | Identificador del usuario, con el prefijo `usr_`. |
| `org_id` | query | no | Identificador de la organización, con el prefijo `org_`. Limita la vista a esa organización; consulte [Alcance de la organización](#alcance-de-la-organización). Requiere `organizations:read`. |

## Respuesta

`200 OK`.

```json
{
  "locked": true,
  "locked_until": "2026-10-20T10:00:00Z",
  "locks": [
    {"id": "lck_0a1b2c3d4e5f6g7h8j9k0m1n2p", "path": "password",
     "locked_at": "2026-10-20T09:45:00Z", "expires_at": "2026-10-20T10:00:00Z"}
  ],
  "failures": [
    {"path": "totp", "count": 3, "window_ends_at": "2026-10-20T09:55:00Z"}
  ],
  "last_sign_in": {"authenticated_at": "2026-10-19T18:12:04Z", "amr": ["pwd", "otp"], "country": "DE"}
}
```

| Campo | Descripción |
|---|---|
| `locked` | `true` cuando al menos una ruta de inicio de sesión está bloqueada. |
| `locked_until` | Cuándo termina el último bloqueo abierto; `null` si no hay ningún bloqueo. |
| `locks` | Bloqueos abiertos, del más antiguo al más reciente, con como máximo uno por ruta (`password`, `totp`, `sms_2fa`, `email_2fa`, `otp`, `recovery_code`). |
| `failures` | Intentos fallidos desde el último éxito o el último bloqueo, por ruta, mientras sigue activa la ventana de recuento. Después de `window_ends_at`, el recuento vuelve a empezar. La ventana de contraseña es `brute_force.window_minutes` del entorno. |
| `last_sign_in` | La autenticación correcta más reciente entre las sesiones que aún se conservan: `authenticated_at` (una nueva autenticación en la misma sesión la adelanta), los métodos de autenticación (`amr`, RFC 8176) y el último país conocido de la sesión (`null` si se desconoce). `null` cuando no se conserva ninguna sesión: el usuario nunca inició sesión, o la última sesión terminó hace más de 30 días. |

## Errores

Cada error es un cuerpo `application/problem+json` con un `code` estable.

| HTTP | `code` | Cuándo |
|---|---|---|
| 404 | `not_found` | El usuario no existe en este entorno (o el identificador tiene un formato incorrecto), o se indica `org_id` y el usuario no es miembro de esa organización. |
| 401 | `unauthenticated` | Falta la clave de API, no es válida o ha sido revocada. |
| 403 | `insufficient_permissions` | La clave no tiene el permiso `users:read`, o se indica `org_id` y la clave no tiene `organizations:read`. |
| 429 | `rate_limited` | Se superó el límite de uso; respete `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente más tarde. |

## Ejemplo

```bash
curl https://api.eu.keenauth.com/v1/users/usr_0a1b2c3d4e5f6g7h8j9k0m1n2p/sign-in-state \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

CLI: `keenauth users sign-in-state usr_0a1b2c3d4e5f6g7h8j9k0m1n2p`
