Los scopes OAuth son diseño de autorización, no configuración de checkboxes

Los scopes OAuth aparecen en la pantalla de consentimiento, pero su trabajo real es codificar el modelo de autorización que heredan los clientes de terceros. Scopes diseñados como configuración de checkboxes — amplios, vagos, nunca validados en el servidor — convierten cada integración en acceso sobre-privilegiado esperando un token robado.

Ingeniería8 min de lectura
OAuthSeguridad APIAutorizaciónIdentidadControl de acceso
Compartir

Una integración de terceros solicita acceso de read y write. El usuario hace clic en Permitir. Seis meses después, un refresh token filtrado otorga mutación completa de la cuenta porque nadie definió qué significaba write en el resource server — y nadie verifica scopes en endpoints individuales de todos modos. La autorización con scopes OAuth no es un checkbox de UX en la pantalla de consentimiento. Es el contrato entre proveedor de identidad, cliente y resource server sobre lo que un bearer token puede hacer. Cuando los scopes se diseñan como etiquetas improvisadas en lugar de permisos ejecutables, cada integración hereda la brecha entre lo que los usuarios creen haber aprobado y lo que el código realmente permite.

OAuth 2.1 consolidó la línea base de seguridad: PKCE obligatorio para clientes públicos, access tokens de corta duración, rotación de refresh tokens y desaliento explícito del flujo implícito. Los scopes siguen siendo el vocabulario de autorización — pero vocabulario sin gramática es ruido. La gramática es la aplicación en el resource server: cada endpoint protegido valida el claim scope del token contra la operación que ejecuta, devuelve 403 con insufficient_scope cuando falta el claim, y nunca infiere permiso por coincidencias parciales de cadenas.

Los scopes fallan cuando describen clientes en lugar de capacidades

El error de diseño de scopes más común mapea permisos a aplicaciones integradoras en lugar de a recursos y acciones. calendar-app:full-access no le dice nada al resource server sobre qué operaciones de calendario están permitidas. google-calendar como scope único no le dice nada al usuario sobre lo que la app hará realmente.

El patrón duradero es recurso:acción — a veces extendido a recurso:subrecurso:acción cuando la superficie del producto lo justifica:

ScopePermiteNo permite
invoices:readGET /invoices, GET /invoices/:idCrear, actualizar, eliminar
invoices:writeCrear y actualizar facturasEliminar (scope separado)
invoices:deleteDELETE /invoices/:idExportación de solo lectura
profile:readLeer campos del perfil de usuarioEnvío de email, cambio de contraseña

La jerarquía puede simplificar el consentimiento sin inflar la aplicación. Un scope padre invoices:manage puede implicar invoices:read e invoices:write en el authorization server — pero el resource server debe verificar el scope canónico de la operación, no asumir jerarquía salvo que esté configurada explícitamente. La jerarquía implícita que existe solo en documentación falla la primera vez que un cliente solicita el scope padre y el servidor otorga acceso a endpoints de eliminación que el equipo de producto nunca pretendió.

Evitar scopes catch-all. admin, full_access e identity.full existen porque son fáciles de solicitar y difíciles de revisar. Derrotan el propósito de tokens con scope: limitar el blast radius cuando un token se filtra. Un token comprometido con invoices:read expone datos de facturas. Un token comprometido con admin expone todo el tenant.

Los scopes son límites de mínimo privilegio, no etiquetas de marketing en la pantalla de consentimiento.

Los agentes de IA y clientes de automatización intensifican el problema. Un agente con email.send y una lista de destinatarios alucinada puede dañar la confianza más rápido que un humano con el mismo scope — porque el agente actúa a velocidad de máquina sin fricción social. Scopes granulares para clientes agente (email:send:draft-only, calendar:read:next-7-days) alinean capacidad con tarea en lugar de otorgar acceso amplio perpetuo en el momento del consentimiento.

Los resource servers deben validar scopes en cada solicitud

Los scopes acordados en el momento de autorización no significan nada si el API gateway y los handlers de recursos no los aplican. La validación pertenece a dos capas.

Capa gateway — firma JWT, iss, aud, exp, nbf, y presencia gruesa de scope para prefijos de ruta. Rechazar tokens malformados o expirados antes de que el tráfico llegue al código de aplicación. La validación local de JWT evita round trips por solicitud al proveedor de identidad — el patrón dominante en 2026 para APIs sensibles a la latencia.

Capa handler — verificación de scope específica de la operación dentro de la lógica de negocio que sabe qué hace el endpoint. DELETE /invoices/:id requiere invoices:delete, no meramente invoices:write. La verificación usa pertenencia exacta al conjunto después de dividir la cadena de scopes delimitada por espacios — nunca coincidencia por subcadena, que produce falsos positivos cuando read coincide con read_everything.

function requireScopes(token: AccessToken, required: string[]): void {
  const granted = new Set(token.scope.split(" "))
  const missing = required.filter((s) => !granted.has(s))
  if (missing.length > 0) {
    throw new InsufficientScopeError(missing)
  }
}
 
// Route handler
app.delete("/invoices/:id", async (req, res) => {
  requireScopes(req.token, ["invoices:delete"])
  await invoiceService.delete(req.params.id)
  res.status(204).end()
})

Devolver 403 Forbidden con WWW-Authenticate: Bearer error="insufficient_scope", scope="invoices:delete" — no un 401 genérico que confunde fallo de autenticación con fallo de autorización. Clientes y operadores diagnostican scopes faltantes desde la respuesta sin adivinar.

Los clientes machine-to-machine usan el flujo client credentials con scopes restringidos a identidad de servicio — nunca permisos delegados de usuario. Un worker de facturación nocturno necesita invoices:read y billing:aggregate:write, no un superconjunto heredado del token de un admin humano. Los secretos de CI/CD que alimentan pipelines suelen portar estas credenciales; el diseño de scopes y la rotación de secretos son la misma conversación de seguridad.

Las pantallas de consentimiento reflejan la calidad del diseño de scopes

Los usuarios experimentan los scopes en el momento del consentimiento. Un diseño pobre produce fatiga de consentimiento — doce permisos granulares que suenan igual — o ceguera de consentimiento — un permiso full_access que los usuarios aprueban sin leer porque no significa nada.

El equilibrio para OAuth orientado al usuario:

  • Agrupar por área de producto en la pantalla de consentimiento: Facturación, Proyectos, Perfil — cada una con variantes read/write según corresponda.
  • Explicar consecuencias en lenguaje claro vinculado a nombres de scope: invoices:delete → "Eliminar facturas permanentemente."
  • Solicitar incrementalmente cuando sea posible — registro dinámico de clientes y consentimiento escalonado para scopes sensibles en lugar de pedir todo al momento de la instalación.

Los desarrolladores de terceros siempre solicitarán los scopes más amplios que el authorization server permita. El trabajo del servidor es otorgar menos de lo solicitado cuando el cliente no necesita acceso completo — una característica de authorization servers bien configurados que muchos equipos deshabilitan por conveniencia.

Para clientes first-party (app de la misma organización accediendo a API de la misma organización), el consentimiento suele omitirse — pero la aplicación de scopes en el resource server debe ejecutarse igual. Los tokens first-party se filtran por logs, almacenamiento del navegador y compromisos de supply chain de la misma forma que los tokens de terceros.

¿Cómo deberían codificar los scopes OAuth las decisiones de autorización?

Estas preguntas exponen las brechas entre la configuración del proveedor de identidad y la aplicación en la API.

¿Los scopes deben ser granulares o gruesos?

Granulares para clientes machine y agentes de IA donde el mínimo privilegio limita el blast radius automatizado. Moderadamente gruesos para consentimiento orientado al usuario donde projects:read y projects:write son comprensibles sin veinte sub-scopes. La regla: cada scope mapea a una verificación ejecutable en un endpoint o clase de recurso específica — si la aplicación no puede articular la verificación, el scope es demasiado vago.

¿Dónde ocurre la validación de scopes?

En el API gateway para validez del token y guardas gruesas de ruta. En el handler para verificaciones específicas de operación. Nunca solo en el authorization server durante la emisión del token — los scopes emitidos son claims, no aplicación. Los tokens viven minutos u horas después de la emisión; la revocación y las duraciones cortas ayudan, pero las verificaciones en handlers son la puerta autoritativa.

¿Cómo interactúan los scopes con RLS y las APIs internas?

Los scopes OAuth gobiernan lo que una aplicación cliente puede solicitar. Row-Level Security en base de datos gobierna qué filas puede ver una identidad de usuario específica dentro de una solicitud permitida. Ambas capas deben sostenerse: un token con invoices:read aún devuelve solo las facturas que el usuario autenticado posee bajo RLS. Los scopes no sustituyen la autorización en capa de datos — son la verificación de perímetro antes de que corra la consulta. La guía de blindaje de seguridad en Supabase cubre la capa de base de datos; los scopes cubren el perímetro de la API.

Un argumento habitual va en la dirección opuesta

La postura contraria sostiene que los scopes OAuth son complejidad heredada — que API keys con allowlists de IP, mutual TLS o motores de política centralizados como Cedar reemplazan cadenas de scope con reglas basadas en atributos más ricas.

Los motores de política agregan valor para delegación compleja — cadenas multi-agente, condiciones de atributos, ABAC dinámico. No reemplazan scopes para integraciones OAuth estándar de terceros donde clientes, pantallas de consentimiento y claims de token deben hablar un vocabulario portable. La arquitectura productiva usa scopes como formato de claim estable y motores de política para service mesh interno u orquestación de agentes donde las cadenas de scope solas son insuficientes.

Los scopes no son la forma final de autorización. Son la línea base interoperable que los resource servers pueden aplicar hoy sin que cada cliente implemente un lenguaje de políticas.

Puntos clave

  • Los scopes OAuth codifican el modelo de autorización — patrones recurso:acción, no etiquetas vagas de cliente.
  • Los resource servers validan scopes por endpoint con pertenencia exacta al conjunto — nunca coincidencia por subcadena.
  • Los scopes catch-all (admin, full_access) derrotan el mínimo privilegio y amplifican el impacto del robo de tokens.
  • El gateway valida claims del token; los handlers validan scopes específicos de operación.
  • Clientes de IA y M2M necesitan scopes más finos de lo que el consentimiento orientado al usuario suele mostrar.
  • Los scopes gobiernan el perímetro de la API; RLS y políticas internas gobiernan el acceso a datos dentro de solicitudes permitidas.

Conclusión

La pantalla de consentimiento es donde los usuarios ven los scopes. El resource server es donde los scopes importan. Los equipos que tratan el diseño de scopes como configuración de checkboxes envían integraciones rápido y pasan trimestres limpiando después de que el primer token filtrado demuestra que write significaba todo.

El ejercicio de diseño es corto: listar cada operación protegida, asignar un scope, implementar la verificación en el handler, y verificar que el texto de la pantalla de consentimiento coincide con lo que la verificación aplica. Las brechas entre esos cuatro artefactos son bugs de autorización esperando producción — no hallazgos de seguridad esperando un penetration test.

Artículos relacionados

Paleta de comandos

Buscá un comando para ejecutar...