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. Sin él, y sin recurso por defecto en el registro del cliente, solo se conceden openid, profile y email.
- 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&iss=https%3A%2F%2Fos.utilia.ai
iss es el emisor (RFC 9207) y llega en todas las respuestas, también en las de error. Si tu aplicación habla con más de un servidor de autorización, comprueba que es https://os.utilia.ai antes de canjear el código.
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&iss=https%3A%2F%2Fos.utilia.ai
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 propias de UTILIA OS se saltan la pantalla de consentimiento cuando la persona ya tiene sesión. Esa marca solo la pone UTILIA OS: 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. 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. Sin ninguno de los dos, la autorización solo concedeopenid,profileyemail: si pides scopes de un servidor MCP, respondeinvalid_requestcon las direcciones de las áreas. La tabla de qué viaja con cada servidor está en Scopes y recursos.- La dirección única
https://os.utilia.ai/api/mcp, con todas las áreas, se retiró el 20 de octubre de 2026. Pedirla comoresourcedevuelveinvalid_request: usa la dirección del área, por ejemplohttps://os.utilia.ai/api/mcp/crm.