OpenID Connect: autorización de aplicaciones internas del desarrollo a los estándares

Hace unos meses estuve implementando un servidor OpenID Connect para gestionar el acceso a cientos de nuestras aplicaciones internas. Pasamos de nuestras soluciones propias, que funcionaban bien a menor escala, a un estándar ampliamente reconocido. El acceso a través del servicio central simplifica enormemente las operaciones monótonas, reduce los costos de implementación de autorizaciones, permite encontrar muchas soluciones ya hechas y evita dificultades al desarrollar nuevas. En este artículo hablaré sobre esta transición y los obstáculos que hemos encontrado en el camino.

OpenID Connect: autorización de aplicaciones internas del desarrollo a los estándares

Hace mucho tiempo… Cómo comenzó todo

Hace varios años, cuando el número de aplicaciones internas superó la capacidad de gestión manual, creamos una aplicación para controlar los accesos dentro de la empresa. Era una simple aplicación en Rails que se conectaba a una base de datos con información sobre los empleados, donde se configuraba el acceso a diferentes funcionalidades. En ese momento también implementamos nuestro primer SSO, que se basaba en la verificación de tokens del lado del cliente y del servidor de autorización; el token se transmitía encriptado con varios parámetros y se verificaba en el servidor de autorización. No era la solución más práctica, ya que cada aplicación interna requiredía describir una capa considerable de lógica, y las bases de datos de empleados no se sincronizaban con el servidor de autorización.

Con el tiempo decidimos simplificar la tarea de la autorización centralizada. El SSO se trasladó al balanceador. Con OpenResty en Lua, añadimos una plantilla que verificaba los tokens, sabía a qué aplicación iba la solicitud y podía comprobar si había acceso. Este enfoque facilitó enormemente la tarea de controlar los accesos a las aplicaciones internas: ya no era necesario describir lógica adicional en el código de cada aplicación. Como resultado, cerramos el tráfico externamente, y la propia aplicación no tenía conocimiento de la autorización.

Sin embargo, uno de los problemas sigue sin resolverse. ¿Qué hacer con las aplicaciones que necesitan información sobre los empleados? Se podría escribir una API para el servicio de autorización, pero eso implicaría agregar lógica adicional para cada una de esas aplicaciones. Además, queríamos deshacernos de la dependencia de una de nuestras aplicaciones internas, diseñada para convertirse en OpenSource, de nuestro servidor de autorización interno. Hablaremos de esto en otra ocasión. La solución a ambos problemas fue OAuth.

A los estándares generalmente aceptados

OAuth es un estándar de autorización claro y ampliamente aceptado, pero dado que su funcionalidad por sí sola no es suficiente, se empezó a considerar OpenID Connect (OIDC) de inmediato. OIDC es en sí mismo la tercera implementación de un estándar de autenticación abierto que se basa en el protocolo OAuth 2.0 (un protocolo de autorización abierto). Esta solución resuelve el problema de la falta de datos sobre el usuario final, así como la posibilidad de cambiar el proveedor de autorización.

Sin embargo, no elegimos un proveedor específico y decidimos agregar integración con OIDC para nuestro servidor de autorización existente. Esta decisión se fundamentó en que OIDC es muy flexible en términos de autorización del usuario final. De este modo, había la posibilidad de implementar soporte para OIDC en nuestro servidor de autorización actual.

OpenID Connect: autorización de aplicaciones internas del desarrollo a los estándares

Nuestro camino para implementar nuestro propio servidor OIDC

1) Transformamos los datos al formato necesario

Para la integración de OIDC, es necesario adaptar los datos actuales de los usuarios a un formato comprensible para el estándar. En OIDC, esto se llama Claims. Los claims son, en esencia, los campos finales en la base de datos de usuarios (nombre, correo electrónico, teléfono, etc.). Existe una lista estándar de claims, y todo lo que no esté en esta lista se considera personalizado. Por lo tanto, el primer aspecto al que se debe prestar atención, si desea elegir un proveedor OIDC existente, es la posibilidad de personalizar fácilmente nuevos claims.

Un grupo de claims se agrupa en el siguiente subconjunto: Scope. Durante la autorización, se solicita acceso no a claims específicos, sino a scopes, incluso si parte de los claims del scope no son necesarios.

2) Implementamos los grants necesarios

La siguiente parte de la integración de OIDC es la selección e implementación de los tipos de autorización, conocidos como grants. El grant seleccionado determinará el escenario de interacción entre la aplicación elegida y el servidor de autorización. El esquema aproximado para seleccionar el grant adecuado se presenta en la figura a continuación.

OpenID Connect: autorización de aplicaciones internas del desarrollo a los estándares

Para nuestra primera aplicación, utilizamos el grant más común: el Authorization Code. Su diferencia con otros es que es un proceso de tres pasos, es decir, pasa por una verificación adicional. Primero, el usuario realiza una solicitud de autorización, recibe un token: el Authorization Code, y luego con este token, como si fuera un billete de viaje, solicita el token de acceso. Toda la interacción principal de este escenario de autorización se basa en redirecciones entre la aplicación y el servidor de autorización. Puedes leer más sobre este grant aquí. aquí.

OAuth se adhiere a la concepción de que los tokens de acceso obtenidos tras la autorización deben ser temporales y cambiarse idealmente cada 10 minutos. El grant Authorization Code es una verificación de tres pasos a través de redirecciones, y honestamente, repetir este paso cada 10 minutos no es la tarea más agradable de realizar. Para solucionar este problema, existe otro grant: el Refresh Token, que también utilizamos. Aquí todo es más sencillo. Durante la verificación con otro grant, además del token de acceso principal, se emite otro: el Refresh Token, que puede utilizarse solo una vez y su tiempo de vida suele ser significativamente más largo. Con este Refresh Token, cuando expire el TTL (Time to Live) del token de acceso principal, la solicitud de un nuevo token de acceso se enviará al endpoint de otro grant. El Refresh Token utilizado se anula inmediatamente. Esta verificación es de dos pasos y puede realizarse en segundo plano, sin que el usuario lo note.

3) Configuramos los formatos de salida de datos del usuario

Una vez que se han implementado las subvenciones seleccionadas y la autorización está funcionando, es importante mencionar la obtención de datos sobre el usuario final. En OIDC hay un endpoint específico para esto, donde con su token de acceso actual, siempre que esté vigente, se pueden solicitar datos sobre los usuarios. Y si los datos del usuario no cambian con frecuencia, pero se necesita acceder a ellos múltiples veces, se puede optar por soluciones como los tokens JWT. Estos tokens también son compatibles con el estándar. Un token JWT en sí mismo consiste en tres partes: header (información sobre el token), payload (los datos necesarios) y signature (la firma, donde el token es firmado por el servidor y se puede verificar la fuente de su firma).

En la implementación de OIDC, el token JWT se llama id_token. Puede ser solicitado junto con el token de acceso normal y lo único que queda es verificar la firma. El servidor de autorización tiene un endpoint específico para esto con un conjunto de claves públicas en formato JWK. Y al hablar de esto, vale la pena mencionar que hay otro endpoint que, basado en el estándar RFC5785 , refleja la configuración actual del servidor OIDC. Contiene todas las direcciones de los endpoints (incluyendo la dirección del conjunto de claves públicas utilizadas para la firma), los reclamos soportados y los alcances, así como los algoritmos de cifrado utilizados, las subvenciones soportadas, etc.

Por ejemplo, en Google:

{
 "issuer": "https://accounts.google.com",
 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
 "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
 "token_endpoint": "https://oauth2.googleapis.com/token",
 "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
 "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
 "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
 "response_types_supported": [
  "code",
  "token",
  "id_token",
  "code token",
  "code id_token",
  "token id_token",
  "code token id_token",
  "none"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "RS256"
 ],
 "scopes_supported": [
  "openid",
  "email",
  "profile"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_post",
  "client_secret_basic"
 ],
 "claims_supported": [
  "aud",
  "email",
  "email_verified",
  "exp",
  "family_name",
  "given_name",
  "iat",
  "iss",
  "locale",
  "name",
  "picture",
  "sub"
 ],
 "code_challenge_methods_supported": [
  "plain",
  "S256"
 ],
 "grant_types_supported": [
  "authorization_code",
  "refresh_token",
  "urn:ietf:params:oauth:grant-type:device_code",
  "urn:ietf:params:oauth:grant-type:jwt-bearer"
 ]
}

Así, a través del id_token se pueden transmitir todos los reclamos necesarios en el payload del token sin necesidad de consultar repetidamente al servidor de autorización para solicitar datos del usuario. La desventaja de este enfoque es que los cambios en los datos del usuario desde el servidor no llegan de inmediato, sino junto con un nuevo token de acceso.

Resultados de la implementación

Así, tras implementar nuestro propio servidor OIDC y configurar las conexiones a él en el lado de las aplicaciones, resolvimos el problema de la transmisión de información sobre los usuarios.
Dado que OIDC es un estándar abierto, tenemos la opción de elegir un proveedor existente o implementar nuestro propio servidor. Probamos Keycloak, que resultó muy conveniente de configurar; una vez realizadas las configuraciones y cambios necesarios en el lado de las aplicaciones, está listo para funcionar. En las aplicaciones, solo queda cambiar las configuraciones de conexión.

Hablando de soluciones existentes

Dentro de nuestra organización, como primer servidor OIDC, construimos nuestra propia implementación, que se fue complementando según fue necesario. Tras un análisis detallado de otras soluciones listas, se puede decir que este es un tema controvertido. La decisión de implementar nuestro propio servidor se justifica por temores de los proveedores debido a la falta de funcionalidad necesaria, así como por la existencia de un antiguo sistema que incluía diversas autorizaciones personalizadas para algunos servicios y en el que ya se almacenaba una cantidad considerable de datos sobre los empleados. Sin embargo, en las implementaciones listas, existen comodidades para la integración. Por ejemplo, en Keycloak hay un sistema de gestión de usuarios propio y los datos se almacenan directamente en él, y trasladar a nuestros usuarios allí no representa un gran esfuerzo. Para esto, Keycloak tiene una API que permitirá llevar a cabo todas las acciones necesarias para la migración.

Otro ejemplo de una implementación certificada, que considero interesante, es Ory Hydra. Es interesante porque consta de diferentes componentes. Para la integración, necesitarás vincular tu servicio de gestión de usuarios con su servicio de autorización y expandirlo según sea necesario.

Keycloak y Ory Hydra no son las únicas soluciones listas. Es mejor seleccionar una implementación certificada por OpenID Foundation. Generalmente, estas soluciones tienen el distintivo de Certificación OpenID.

OpenID Connect: autorización de aplicaciones internas del desarrollo a los estándares

También no olvides los proveedores de pago existentes si no deseas mantener tu servidor OIDC. Actualmente hay muchas buenas opciones.

¿Qué sigue?

En un futuro cercano, planeamos restringir el tráfico a los servicios internos de otra manera. Planeamos trasladar nuestro SSO actual a un balanceador utilizando OpenResty en un proxy basado en OAuth. Aquí también hay muchas soluciones listas, por ejemplo:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Materiales adicionales

jwt.io – un buen servicio para verificar tokens JWT
openid.net/developers/certified — lista de implementaciones certificadas de OIDC

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster