Autenticación
La API no tiene clave de API. Quien llama entra como una persona (correo y contraseña) y recibe dos tokens:
| Token | Duración | Para qué |
|---|---|---|
accessToken | corta (expiresIn segundos; hoy 15 minutos) | Va en todas las llamadas: Authorization: Bearer <accessToken>. |
refreshToken | larga (hasta 30 días; la institución puede reducirla) | Solo para obtener un par nuevo en POST /auth/refresh. |
Use siempre el valor de expiresIn de la respuesta en vez de fijar 15 minutos en su código.
Cuenta para la integración
Pida al curador de la institución que cree una cuenta solo para la integración (por ejemplo integracao@exemplo.org), con el rol mínimo necesario (vea Permisos). La persona recibe una invitación por correo y define la contraseña en la aplicación. Evite usar la cuenta de una persona del equipo: si se va, la integración se detiene.
Iniciar sesión
POST /auth/login con email y senha. La respuesta es la sesión:
curl -X POST "$API/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"integracao@exemplo.org","senha":"********"}'const r = await fetch(`${API}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "integracao@exemplo.org", senha: process.env.SENHA }),
})
const sesion = await r.json()import os, requests
r = requests.post(f"{API}/auth/login", json={"email": "integracao@exemplo.org", "senha": os.environ["SENHA"]})
sesion = r.json(){
"accessToken": "eyJhbGciOi...",
"refreshToken": "q3v8...",
"expiresIn": 900,
"usuario": {
"id": "0198a000-0000-7000-8000-000000000001",
"email": "integracao@exemplo.org",
"nome": "Integración Ejemplo",
"locale": "es",
"nationality": null,
"vinculos": [
{ "papel": "CURADOR", "instituicaoId": "0198a000-0000-7000-8000-0000000000aa", "disciplinaId": null, "turmaId": null, "podeAvaliar": false, "podeCadastrar": false }
]
}
}usuario.vinculos muestra los roles de la cuenta y dónde valen (institución, asignatura o grupo). GET /auth/me devuelve la misma información en cualquier momento.
Llamar a la API
curl "$API/auth/me" -H "Authorization: Bearer $TOKEN"Renovar
Antes de que venza el accessToken (o al recibir 401 UNAUTHENTICATED), cambie el refreshToken:
curl -X POST "$API/auth/refresh" -H "Content-Type: application/json" \
-d '{"refreshToken":"q3v8..."}'La respuesta es una sesión nueva, con un refreshToken nuevo. Reglas que importan a quien implementa:
- Cada
refreshTokensirve una sola vez. Guarde siempre el par más reciente. - Presentar de nuevo un
refreshTokenya usado se trata como posible robo: se cierran todas las sesiones de la cuenta y hay que iniciar sesión otra vez con la contraseña. Por eso, en una integración con varios procesos, renueve en un solo lugar y comparta el token resultante; dos renovaciones simultáneas con el mismorefreshTokenhacen que una falle con401. - Si la renovación falla con
401, inicie sesión de nuevo conPOST /auth/login.
Cerrar sesión
POST /auth/logout con el refreshToken cierra esa sesión. POST /auth/logout-todas (autenticada) cierra todas las sesiones de la cuenta.
Bloqueo por intentos
Errores de contraseña seguidos para el mismo correo bloquean nuevos intentos por un tiempo (por defecto: 5 fallos, bloqueo de 15 minutos). En ese caso, y cuando hay exceso de intentos desde el mismo origen, la respuesta es 429 RATE_LIMITED con retryAfterSeg y la cabecera Retry-After. No reintente en bucle: vea Paginación y límites. Contraseña incorrecta, correo inexistente y cuenta bloqueada reciben la misma respuesta 401, a propósito, para no revelar qué cuentas existen.
Guarde el secreto con cuidado
La contraseña y el refreshToken dan el mismo acceso que tendría la persona. Guárdelos en un almacén de secretos o en una variable de entorno del servidor, nunca en el código, el repositorio o los registros.