Flujo de autorización
Cómo pedir la autorización con PKCE: parámetros de /api/oauth/authorize, qué ve la persona, cómo vuelve el código y qué errores pueden llegar.
Actualizado el . 6 min de lectura
El flujo de autorización lleva a la persona desde tu aplicación hasta UTILIA OS, donde inicia sesión y decide qué te concede, y la devuelve a tu redirect_uri con un código de un solo uso. Tú solo construyes la URL de GET /api/oauth/authorize y recoges el código; la decisión la registra la propia pantalla de consentimiento de UTILIA OS.
Autorización con PKCE
Participan: Tu aplicación, Navegador, UTILIA OS.
De Tu aplicación a Navegador:
Redirige a /api/oauth/authorize
Con client_id, redirect_uri, scope, state y code_challenge.De Navegador a UTILIA OS:
GET /api/oauth/authorize
UTILIA OS valida la solicitud y la guarda durante 10 minutos.Nota en UTILIA OS:
Inicio de sesión, si hace falta, y pantalla de consentimiento.De UTILIA OS a Navegador:
Vuelta a tu redirect_uri con code y state (respuesta)
El código vale 60 segundos y sirve una sola vez.De Navegador a Tu aplicación:
GET /callback?code=...&state=...
Compruebas que el state es el que enviaste.De Tu aplicación a UTILIA OS:
POST /api/oauth/token
Con el code y el code_verifier.Nota en UTILIA OS:
Comprueba que el SHA-256 del code_verifier coincide con el code_challenge.De UTILIA OS a Tu aplicación:
access_token, refresh_token e id_token (respuesta)
Access token de 1 hora; el id_token, solo si se concedió openid.
1. Genera el par PKCE
PKCE es obligatorio. El code_verifier es una cadena aleatoria que guardas en tu lado; el code_challenge es su SHA-256 en Base64URL sin relleno, y es lo único que viaja en la URL.
function base64url(bytes) {
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
const codeVerifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
const codeChallenge = base64url(new Uint8Array(digest));
// Guárdalo para el canje del código
sessionStorage.setItem("code_verifier", codeVerifier);- code_verifierstringobligatorio
- De 43 a 128 caracteres del conjunto [A-Za-z0-9-._~]. Se envía al canjear el código.
- code_challengestringobligatorio
- SHA-256 del code_verifier en Base64URL sin relleno. Se envía en la URL de autorización.
- code_challenge_methodstringobligatorio
- Siempre S256.
2. Lleva a la persona a la autorización
/api/oauth/authorizeInicia la autorización y lleva a la persona a la pantalla de consentimiento de UTILIA OS
- client_idstringobligatorio
- Identificador de tu cliente.
- redirect_uristringobligatorio
- Una de las direcciones registradas para el cliente, carácter por carácter.
- response_typestringobligatorio
- Siempre code.
- code_challengestringobligatorio
- Base64URL del SHA-256 del code_verifier, de 43 a 128 caracteres.
- code_challenge_methodstringobligatorio
- Siempre S256.
- scopestring
- Scopes separados por espacios. Si lo omites, se usan los allowedScopes del registro del cliente o, si no tiene, solo openid, profile y email.
- statestring
- Valor aleatorio contra la falsificación de peticiones. Vuelve tal cual en la redirección; compruébalo siempre.
- resourcestring
- Servidor MCP al que irá atado el token (RFC 8707), por ejemplo https://os.utilia.ai/api/mcp/crm. Recorta los scopes al dominio de ese servidor.
- noncestring
- Valor aleatorio que vuelve dentro del id_token. Verifícalo para evitar reutilizaciones.
- promptstring
- none, login, select_account o consent. Ver más abajo.
- login_hintstring
- Correo con el que rellenar el inicio de sesión. Hasta 255 caracteres del conjunto [A-Za-z0-9._@+-].
- themestring
- light o dark. Se acepta y se valida, pero hoy la pantalla de consentimiento muestra el tema que tenga elegido la persona.
Un valor no admitido en response_type, code_challenge_method, prompt, theme o login_hint, o un code_challenge mal formado, hace que la solicitud se rechace sin volver a tu aplicación.
const params = new URLSearchParams({
client_id: "TU_CLIENT_ID",
redirect_uri: "https://mi-app.com/callback",
response_type: "code",
scope: "openid profile email crm:clients:view",
resource: "https://os.utilia.ai/api/mcp/crm",
code_challenge: codeChallenge,
code_challenge_method: "S256",
state: crypto.randomUUID(),
});
window.location.href = `https://os.utilia.ai/api/oauth/authorize?${params}`;UTILIA OS guarda la solicitud durante 10 minutos y lleva a la persona a su pantalla de consentimiento. Si no ha iniciado sesión, primero pasa por el inicio de sesión y después vuelve sola. Allí ve qué pides, agrupado en permisos comprensibles, y puede desactivar los que no quiera conceder. Lo cuenta Pantalla de consentimiento.
3. Recoge la respuesta
Si la persona autoriza, vuelve a tu redirect_uri con el código:
https://mi-app.com/callback?code=CODIGO&state=TU_STATE
El código vale 60 segundos y sirve una vez; si se reutiliza, se revocan los tokens emitidos con él. Canjéalo enseguida en POST /api/oauth/token, como explica Canje, revocación e introspección de tokens.
Si la persona pulsa No permitir, vuelve con un error:
https://mi-app.com/callback?error=access_denied&error_description=The+user+denied+the+authorization+request&state=TU_STATE
El token puede llevar menos de lo que pediste
La persona puede desactivar grupos de permisos, y además cada scope se recorta por sus permisos y por el plan de su organización. Lee siempre el campo scope de la respuesta del token y no des por hecho el conjunto que pediste.
Errores
Dónde aparece el error depende de lo que falla. Si tu client_id o tu redirect_uri no son válidos, UTILIA OS no puede devolver a la persona a tu aplicación sin riesgo y le muestra su propia página de error.
¿Dónde llega un error de autorización?
¿El cliente existe y está activo, y la redirect_uri está registrada?
No:
Página de error de UTILIA OS
invalid_client · invalid_redirect_uri
La persona no vuelve a tu aplicación.Sí:
¿Qué falla?
Scope o resource:
Tu redirect_uri
invalid_scope · invalid_request
Un scope que no existe o no está permitido para el cliente, o un resource no permitido.prompt=none:
Tu redirect_uri
login_required
No hay sesión utilizable (ver más abajo).La persona no autoriza:
Tu redirect_uri
access_denied
Pulsó No permitir, o Volver en «Acceso no autorizado».Nada que conceder:
Página de error de UTILIA OS
invalid_scope
Al decidir, le faltan permisos o su organización no tiene contratado el aplicativo.
En todos los casos que vuelven a tu redirect_uri, llegan error, error_description y tu state.
Si a la persona no le queda ningún permiso que conceder, la pantalla le dice «Acceso no autorizado» y el botón Volver te la devuelve con access_denied.
Parámetros de OpenID Connect
prompt
| Valor | Qué hace |
|---|---|
none | Exige una sesión ya abierta. El navegador no envía la sesión de UTILIA OS al servidor de autorización, así que en una redirección normal la respuesta es siempre login_required: no sirve para renovar en silencio. Para eso, usa el refresh token. |
login | Obliga a la persona a iniciar sesión otra vez, aunque ya la tuviera abierta. |
select_account | Si el navegador tiene varias cuentas guardadas, la pantalla muestra un selector para cambiar de cuenta; si no tiene ninguna, lleva al inicio de sesión para añadir una. |
consent | Obliga a mostrar la pantalla de consentimiento, sin selector de cuenta. Es el único valor que la muestra también a las aplicaciones propias de UTILIA OS. |
Las aplicaciones marcadas como propias de UTILIA OS se saltan la pantalla de consentimiento cuando la persona ya tiene sesión. Esa marca solo la puede poner la organización que administra UTILIA OS; si otra organización intenta activarla por la API, recibe un 403 con SAAS_ADMIN_REQUIRED_FOR_OAUTH_TRUST. Una aplicación de terceros pasa siempre por la pantalla.
login_hint
Rellena el correo en el inicio de sesión. Solo admite letras, dígitos y los símbolos . _ @ + -, con un máximo de 255 caracteres: es una medida de seguridad, porque el valor se refleja en la interfaz. Cualquier otro carácter hace que la solicitud se rechace.
max_age
Se acepta, pero no tiene efecto: UTILIA OS no registra el momento del último inicio de sesión y no puede evaluarlo. Si necesitas una autenticación reciente, por ejemplo antes de una acción delicada, usa prompt=login.
nonce y resource
noncevuelve tal cual dentro delid_token. Genéralo aleatorio en cada petición y compruébalo al recibir el token.resourcefija el destinatario del token y recorta los scopes al dominio de ese servidor MCP. Si lo omites y el cliente tiene un recurso por defecto en su registro, se usa ese. La tabla de qué viaja con cada servidor está en Scopes y recursos.