Autenticación de usuarios finales
Vincent Depassier · 17 de septiembre de 2026
Estas rutas autentican a los usuarios de tu aplicación — no a las personas que entran a Praxsuite. Emiten un JWT que tu cliente después manda en /query y /schema, donde los roles del usuario final deciden qué filas y qué columnas vuelven.
Auth: una API key, pk_live_ o sk_live_. Un JWT de usuario final se rechaza aquí — la única excepción es change-password, que exige el JWT en su lugar. Tres rutas no piden credencial: config, logo y jwks.json.
El sobre de respuesta
Todas las rutas de esta página salvo config, logo y confirm-email responden en el sobre de servicio:
{
"isSuccess": true,
"statusCode": 200,
"message": null,
"data": { },
"errors": []
}Al fallar, isSuccess es false, data es null, y el motivo está en message — con detalle por campo en errors cuando lo que falló fue la validación. El estado HTTP coincide con statusCode.
Arranque
GET /{workspaceId}/auth/configPública, sin credencial. Así es como una app de navegador se entera de qué clave usar.
{
"success": true,
"publicKey": "pk_live_…",
"branding": {
"name": "Acme",
"lightPrimary": "…", "darkPrimary": "…",
"hasLogo": true,
"logoUrl": "/api/v1/gateway/{workspaceId}/auth/logo"
},
"authPageConfig": {
"defaultLanguage": "es",
"enabledRegisterFields": ["firstName", "lastName"],
"enabledSocialProviders": ["google"],
"requireEmailConfirmation": false,
"termsUrl": null,
"privacyUrl": null
},
"providers": [ ],
"oidcProviders": [ ]
}publicKey se entrega sólo para una credencial que alguien haya marcado como publicable. Si nadie lo hizo, esta ruta responde 404 y lo explica: publicar una clave le da sus permisos de tabla a cualquiera que tenga el GUID del workspace, así que es un acto deliberado y no un valor por defecto. Acota la credencial primero.
providers es la lista completa de proveedores de identidad que acepta este workspace, cada uno con su tipo. enabledSocialProviders y oidcProviders son vistas más viejas y angostas de las mismas filas, mantenidas para los clientes que ya las leen.
GET /{workspaceId}/auth/logoPública. Redirige al logo del workspace, cacheable una hora. 404 si el workspace no tiene.
GET /{workspaceId}/auth/jwks.jsonPública. La clave pública RSA del workspace en formato JWKS, para verificar tokens de redirección post-login (RS256) sin secreto compartido. Cacheable una hora.
Registro e inicio de sesión locales
POST /{workspaceId}/auth/register{ "email": "a@b.com", "password": "minimo-8", "firstName": "Ada", "lastName": "Lovelace", "username": "ada" }email y password son obligatorios; la contraseña va de 8 a 128 caracteres. El resto es opcional, y cuáles de esos campos debería mostrar tu formulario está en authPageConfig.enabledRegisterFields.
POST /{workspaceId}/auth/login{ "email": "a@b.com", "password": "…" }Las dos rutas devuelven el mismo data:
{
"accessToken": "eyJ…",
"refreshToken": "…",
"accessTokenExpiresAt": "2026-01-14T10:22:11Z",
"refreshTokenExpiresAt": "2026-02-13T09:22:11Z",
"tokenType": "Bearer",
"user": {
"id": "…",
"email": "a@b.com",
"firstName": "Ada",
"lastName": "Lovelace",
"username": "ada",
"roles": ["cliente"]
},
"requiresEmailConfirmation": false,
"emailVerified": true,
"postLoginRedirectUrl": null
}Cuando el workspace exige confirmación de correo, un registro devuelve requiresEmailConfirmation: true con endUserId y email, y sin tokens. No lo trates como una llamada fallida.
Si el workspace tiene un proveedor OIDC configurado, login valida las credenciales contra ese proveedor en vez de localmente. Sin redirección, y sin que cambie nada para tu cliente.
POST /{workspaceId}/auth/refresh
POST /{workspaceId}/auth/logoutLas dos toman { "refreshToken": "…" }. Refresh devuelve un par nuevo e invalida el refresh token anterior — rotan, así que guarda sólo el último. Logout revoca un refresh token, lo que cierra la sesión en un dispositivo.
Confirmación de correo
GET /{workspaceId}/auth/confirm-email?token=…
POST /{workspaceId}/auth/resend-confirmation { "email": "a@b.com" }confirm-email es pública y devuelve una página HTML con el branding del workspace, no JSON: se abre desde un enlace de correo, que no puede mandar cabeceras.
Contraseña
POST /{workspaceId}/auth/forgot-password { "email": "a@b.com" }
POST /{workspaceId}/auth/verify-reset-code { "email": "a@b.com", "code": "123456" }
POST /{workspaceId}/auth/reset-password { "sessionToken": "…", "newPassword": "…", "confirmPassword": "…" }
POST /{workspaceId}/auth/change-password { "currentPassword": "…", "newPassword": "…", "confirmPassword": "…" }El flujo de recuperación son tres llamadas: pedir un código, canjear el código de seis dígitos por un sessionToken de vida corta, y usar ese token para fijar la contraseña nueva. Las contraseñas nuevas van de 8 a 128 caracteres.
change-password es la única ruta de esta página que toma el JWT del usuario final en vez de una API key — el usuario está cambiando su propia contraseña, así que es su identidad la que hay que demostrar.
Inicio de sesión social y federado
GET /{workspaceId}/auth/{providerSlug}/authorize
POST /{workspaceId}/auth/{providerSlug}/callback { "code": "…", "state": "…", "redirectUri": "…" }authorize devuelve la URL a la que mandar el navegador. callback canjea el código devuelto por un JWT del gateway, en el mismo data que login. state es de un solo uso y se verifica contra CSRF, y redirectUri tiene que coincidir con el que se usó al autorizar.
authorize resuelve qué hacer según el tipo del proveedor: para un proveedor de navegador devuelve una URL; para uno de aserción de servidor responde 400, porque no hay ninguna página a la que mandar a nadie.
La forma vieja, sólo para OIDC, sigue funcionando y significa lo mismo:
GET /{workspaceId}/auth/oidc/{providerSlug}
POST /{workspaceId}/auth/oidc/callback { "providerSlug": "…", "code": "…", "state": "…", "redirectUri": "…" }Un segmento literal de ruta le gana al parámetro, así que /auth/oidc/... sigue llegando al handler viejo.
Autenticar a un jugador desde el servidor del juego
POST /{workspaceId}/auth/{providerSlug}/assert{
"platformPlayerId": "1234567",
"displayName": "ada",
"avatarUrl": "https://…",
"metadata": { "accountAge": 812, "region": "us-east" }
}Dentro de un juego no hay navegador, así que no hay redirección posible. Tu servidor de juego responde por el jugador, y lo que se confía aquí es tu clave sk_live_ — que tiene que ser una clave de servidor marcada para esa plataforma. Lee platformPlayerId del lado del servidor, desde la API de la plataforma; un valor que te mandó el cliente es un valor que el jugador puede falsificar.
displayName y avatarUrl se guardan para rankings y vistas de administración, y no se confían para nada.
Devuelve el mismo data que login.
Errores
Estado | Causa típica |
| Falló la validación, o el proveedor no hace login de navegador |
| Sin API key, o un JWT donde se exige una API key |
| No hay credencial publicable, no existe el proveedor, o no hay logo |
| El correo ya está registrado |
| El tipo de proveedor está registrado pero su camino de login todavía no está construido |
Toda llamada de aquí se escribe en el log de consultas como evento Auth, con la acción, el correo, el estado y la IP de origen.