Sutilezas de autorización: una descripción general de la tecnología OAuth 2.0

El sistema de información de Dodo IS consta de 44 servicios diferentes, como Tracker, Restaurant Cashier o Knowledge Base y muchos otros. Para no distraernos con varias cuentas, hace 3 años escribimos el servicio Auth para implementar la autenticación PassThrough, y ahora estamos escribiendo la segunda versión, que se basa en el estándar de autorización OAuth 2.0. Este estándar es bastante complejo, pero si tiene una arquitectura compleja con muchos servicios, entonces OAuth 2.0 será útil cuando desarrolle su propio servicio de autenticación. En este artículo, traté de contarte sobre el estándar de la manera más simple y comprensible posible para que ahorres tiempo al estudiarlo.





 

Tarea de autenticación



El problema de la autorización en decenas de servicios se encontró hace unos años, al comienzo de la “ era de aserrar un monolito ”. Este problema se resolvió con un nuevo servicio llamado Auth. Ayudó a implementar una autenticación perfecta en varios servicios y a migrar los datos de los usuarios a bases de datos independientes. 



El servicio Auth tiene tres tareas principales:



  • Punto único de autenticación (SSO) para todos los servicios del sistema . Los servicios no almacenan credenciales, pero confían esto a un servicio dedicado.

  • Acceso seguro y granular a los recursos . Seguro porque las contraseñas se almacenan en un solo lugar y son lo más seguras posible. Granular, ya que los propietarios del servicio pueden configurar el acceso a los recursos como lo deseen, en función de los datos que provienen del servicio de autenticación.

  • . , , .





La primera versión de Auth es parte del monolito. Utiliza su propio protocolo para comunicarse con los servicios. Tal "esquema" era necesario en ese momento, pero después de varios años de trabajo aparecieron problemas.



Auth es parte del monolito . En consecuencia, el servicio está vinculado al ciclo de lanzamiento, lo que imposibilita el desarrollo y la implementación independientes. Además, tendría que implementar todo el monolito si quisiera implementar Auth, por ejemplo, al escalar un servicio.



Dodo IS depende de Auth . En la implementación anterior, los servicios externos llaman a Auth en cada acción del usuario para validar los datos al respecto. Este enlace estricto puede hacer que todo el Dodo IS deje de funcionar si Auth se atasca por algún motivo.



La autenticación depende de Redis... Además, es bastante fuerte: un mal funcionamiento de Redis provocará la caída de Auth. Usamos Azure Redis, para el cual el SLA indicado es del 99,9%. Esto significa que el servicio puede no estar disponible hasta 44 minutos al mes. Este tiempo de inactividad no está permitido.



La implementación actual de Auth utiliza su propio protocolo de autenticación sin depender de estándares . En la mayoría de nuestros servicios, usamos C # (si estamos hablando del backend) y no tenemos problemas para mantener la biblioteca para nuestro protocolo. Pero si los servicios en Python, Go o Rust aparecen repentinamente, el desarrollo y soporte de bibliotecas para estos lenguajes tomará más tiempo y traerá complejidad adicional.



La autenticación actual utiliza un esquema de control de acceso basado en roles que se basa en roles... Por lo general, el rol tiene acceso completo a un servicio específico, en lugar de estar vinculado a una funcionalidad específica. Por ejemplo, en las pizzerías hay subgerentes que pueden liderar determinados proyectos: elaborar horarios o tener en cuenta las materias primas. Pero no tenemos la emisión de derechos sobre componentes específicos del sistema. Debe otorgar acceso completo al servicio para que los empleados puedan acceder a la programación o configuración de cualquier componente de contabilidad.



Los problemas nos llevaron a diseñar y escribir una nueva versión de Auth. Al inicio del proyecto, pasamos 3 semanas estudiando los estándares de autorización y autenticación OAuth 2.0 y OpenID Connect 1.0. 



Nota... Exagerado, el artículo es un recuento del RFC, que tuvo que ser releído varias veces para comprender lo que estaba sucediendo a su alrededor. Aquí traté de alejarme de esta complejidad y contar todo de manera simple, estructurada, concisa y sin describir cosas complejas, por ejemplo, qué símbolos puede contener la respuesta del servicio. A diferencia de RFC, después de leer esto una vez, puedes resolverlo todo. Espero que el artículo sea de utilidad y ahorre tiempo a la hora de elegir una solución para implementar un servicio de autenticación, o tal vez haga que alguien piense en su necesidad.



¿Qué es OAuth2.0?



Decidimos comenzar el desarrollo del nuevo Auth examinando los protocolos y tecnologías disponibles. El estándar de autorización más común es el marco de autorización OAuth2.0. 



El estándar se adoptó en 2012 y durante 8 años el protocolo se ha modificado y complementado. Hay tantas RFC que los autores del protocolo original decidieron escribir OAuth 2.1, que combinaría todos los cambios actuales en OAuth 2.0 en un solo documento. Mientras está en la etapa de draft .



La versión actual de OAuth se describe en RFC 6749 . Lo analizaremos. 



OAuth 2.0 es un marco de autorización.


Describe cómo se debe implementar la comunicación entre servicios para garantizar una autorización segura. Se describen muchos matices con suficiente detalle, por ejemplo, el flujo de interacción entre nodos, pero algunos quedan a merced de una implementación específica.



caracteristicas:



  • Separando la entidad del usuario y la aplicación que solicita el acceso . Gracias a esta separación, podemos gestionar los derechos de las aplicaciones por separado de los derechos de los usuarios. 



  • En lugar del nombre de usuario y la contraseña habituales, que tienen un cierto conjunto de derechos y vida útil, obtenemos acceso a los recursos mediante cadenas generadas aleatoriamente: tokens .

  • Puede emitir derechos con la mayor precisión posible , en función de sus propios deseos, y no en un conjunto predeterminado de derechos.



Echemos un vistazo más de cerca a las características.



Roles



OAuth 2.0 define cuatro roles:



  • El propietario del recurso es una entidad que tiene derechos de acceso a un recurso protegido. Una entidad puede ser un usuario final o algún tipo de sistema. Un recurso protegido es un punto final HTTP, que puede ser cualquier cosa: punto final de API, archivo en CDN, servicio web.

  • Servidor de recursos : servidor que almacena un recurso protegido al que tiene acceso el propietario del recurso.

  • Cliente . Esta es una aplicación que solicita acceso a un recurso protegido en nombre del propietario del recurso y con su permiso, con autorización. 

  • Servidor de autorización : un servidor que emite un token a un cliente para acceder a un recurso protegido después de una autorización exitosa del propietario del recurso.



Cada participante en la interacción puede combinar varios roles. Por ejemplo, un cliente puede ser propietario de un recurso al mismo tiempo y solicitar acceso a sus propios recursos. Consideremos más el esquema de interacción.



Importante: el cliente debe estar registrado en el servicio con antelación. ¿Cómo hacerlo?



Registro de cliente



Usted elige el método de registro del cliente, por ejemplo, descubrimiento manual o de servicio, según la fantasía de una implementación en particular. Pero con cualquier método durante el registro, además del ID de cliente, se deben especificar 2 parámetros: URI de redirección y tipo de cliente.



URI de redirección : la dirección a la que se enviará al propietario del recurso después de una autorización exitosa. Además de la autorización, la dirección se utiliza para confirmar que el servicio que solicitó la autorización es quien dice ser.



Tipo de cliente : el tipo de cliente que determina cómo interactúa con él. El tipo de cliente está determinado por su capacidad para almacenar de forma segura sus credenciales para autorización: un token. Por tanto, solo existen 2 tipos de clientes:



  • Confidential — , . , web-, backend.

  • Public — . , , .





El token en OAuth 2.0 es una cadena que no es transparente para el cliente. Por lo general, la cadena parece que se generó aleatoriamente; su formato no le importa al cliente. Un token es una clave para acceder a algo, por ejemplo, a un recurso protegido (token de acceso) oa un token nuevo (token de actualización).



Cada token tiene su propia vida . Pero el token de actualización debería tener más, porque se utiliza para obtener un token de acceso. Por ejemplo, si la vida útil de un token de acceso es de aproximadamente una hora, entonces el token de actualización se puede dejar en vivo durante toda una semana. 



El token de actualización es opcional y está disponible solo para clientes confedenciales... Al usar el token opcional, en algunas implementaciones, la vida útil del token de acceso es muy larga y el token de actualización no se usa en absoluto, para no molestarse en actualizar. Pero esto no es seguro. Si el token de acceso se ha visto comprometido, se puede restablecer y el servicio recibirá un nuevo token de acceso utilizando el token de actualización. Si no hay un token de actualización, deberá volver a realizar el proceso de autorización.



A un token de acceso se le asigna un determinado conjunto de derechos de acceso, que se emite al cliente durante la autorización. Echemos un vistazo a cómo se ven los permisos en OAuth 2.0.



Derechos de acceso



Los derechos de acceso se otorgan al cliente como alcance. El alcance es un parámetro que consta de cadenas separadas por espacios: alcance-token.



Cada uno de los tokens de alcance representa derechos específicos otorgados al cliente. Por ejemplo, un token de alcance doc_read puede proporcionar acceso de lectura a un documento en un servidor de recursos y employee acceso a la funcionalidad de la aplicación solo para los empleados de la empresa. El alcance final podría tener este aspecto: email doc_read employee.



En OAuth 2.0, creamos tokens de alcance nosotros mismos y los personalizamos para que se adapten a nuestras necesidades. Los nombres de token de alcance están limitados solo por fantasía y dos caracteres ASCII, "y \.



En la etapa de registro del cliente, en la configuración del servicio de autorización, el cliente recibe un alcance estándar de forma predeterminada. Pero el cliente puede solicitar al servidor de autorización un alcance diferente al estándar. Según las políticas del servidor de autorización y la elección del propietario del recurso, el ámbito resultante puede verse muy diferente. En el futuro, después de que se autorice al cliente, el propietario del recurso puede quitar algunos de los derechos sin volver a autorizar el servicio, pero para otorgar permisos adicionales, se requerirá una nueva autorización del cliente.



Resumen OAuth 2.0. Flujo con token de acceso



Observamos los roles, los tipos de tokens y también cómo se ve el alcance. Veamos el flujo de proporcionar acceso al servicio.



A continuación se muestra un diagrama abstracto (o flujo) de interacción entre los participantes. Todos los pasos de este diagrama se realizan estrictamente de arriba a abajo. Analicemos con más detalle.







  • El cliente envía una solicitud para acceder al propietario del recurso requerido.

  • El propietario del recurso devuelve al cliente una concesión de autorización, que confirma la identidad del propietario del recurso y sus derechos sobre el recurso al que el cliente solicita acceso. Dependiendo del flujo, esto puede ser un token o credenciales.

  • El Cliente envía la concesión de autorización obtenida en el paso anterior al servidor de autorización, esperando un token de acceso de este para acceder al recurso protegido. 

  • El servidor de autorización se asegura de que la concesión de autorización sea válida y luego envía el token de acceso al cliente.

  • Después de recibir el token de acceso, el cliente solicita el recurso protegido del servidor de recursos. 

  • El servidor de recursos se asegura de que el token de acceso sea correcto y luego proporciona acceso al recurso protegido.



El cliente recibe la aprobación del propietario del recurso, sobre la base de la cual se le concede acceso al recurso. Es simple. ¿Será tan fácil si agregamos un token de actualización a este esquema?



Resumen OAuth 2.0. Flujo usando el token de actualización



El primer y segundo paso se omiten en este diagrama; no son diferentes del diagrama de flujo abstracto anterior.







Esquema con más detalle:



  • El Cliente viene con una concesión de autorización al servidor de autorización y solicita que le proporcione un token de acceso y un token de actualización.

  • Authorization server , authorization grant access token refresh token.

  • Client access token , — invalid token error.

  • , authorization server refresh token access token . 

  • access token, refresh token, refresh token. 



grant?



La concesión son datos que representan la autorización exitosa del cliente por parte del propietario del recurso, utilizados por el cliente para obtener un token de acceso.



Por ejemplo, cuando nos autenticamos con Google en algún lugar, aparece una notificación frente a nuestros ojos. Dice que tal o cual servicio desea acceder a datos sobre usted o sus recursos (se muestra el token de alcance solicitado). Esta notificación se llama "Pantalla de consentimiento".



En el momento en que hacemos clic en "Aceptar", la misma concesión entra en la base de datos: se registran los datos que tal o cual usuario ha dado tal o cual acceso a tal o cual servicio. El cliente recibe algún tipo de identificador de autenticación exitoso, como una cadena, que está asociada con los datos en la base de datos.



Hay 4 + 1 formas de obtener una subvención: tipo de subvención:



  • Authorization code — confedencial — web-.

  • Client credentials — confedential , , .

  • Implicit — public-, redirection URI (, ), authorization code grant PKCE (Proof Key for Code Exchange — , , token , . — RFC 7636).

  • Credenciales de contraseña del propietario del recurso . En la seguridad RFC 6819 de OAuth 2.0, este tipo de concesión se considera poco fiable. Si anteriormente se permitía que se usara solo para la migración de servicios a OAuth 2.0, por el momento no se puede usar en absoluto.

  • Autorización de dispositivo (agregada en RFC 8628): se utiliza para autorizar dispositivos que pueden no tener navegadores web, pero que pueden funcionar a través de Internet. Por ejemplo, se trata de aplicaciones de consola, dispositivos inteligentes o televisores inteligentes.



Solo el código de autorización (con PKCE), las credenciales del cliente y la concesión de autorización del dispositivo pueden considerarse relevantes , pero lo consideraremos todo. Consideraremos la subvención en orden de complejidad creciente de comprensión.



Client credentials grant flow



Tiene el flujo más simple, que recuerda a la autorización regular en cualquier servicio. Se realiza utilizando las credenciales del cliente, que son la identificación del cliente y el secreto del cliente, un análogo del nombre de usuario y la contraseña del usuario. Dado que la autenticación requiere un secreto de cliente que debe almacenarse adecuadamente, solo los clientes confedenciales pueden usar este flujo.







El esquema es simple: el cliente se autentica en el servidor de autorización pasando la identificación del cliente y el secreto del cliente. En respuesta, recibe un token de acceso, con el que ya puede acceder al servicio requerido.



Este flujo es necesario cuando el cliente intenta acceder a sus propios recursos o recursos previamente acordados con el servidor de autorización. Por ejemplo, el servicio A debe ir al servicio B de vez en cuando y actualizar los datos sobre el número de pizzerías en la red.



Flujo de credenciales de contraseña del propietario del recurso



De acuerdo con las recomendaciones de seguridad actuales descritas en esta RFC , no se recomienda utilizar este flujo debido a preocupaciones obvias de seguridad.





En la ilustración de este flujo, hay dos Clientes y, en teoría, debería haber un Cliente y un Servidor de Autorización.



El propietario del recurso transfiere su nombre de usuario y contraseña al cliente, por ejemplo, a través de formularios en el cliente. El cliente, a su vez, lo usa para obtener un token de acceso (y, opcionalmente, un token de actualización).



Aquí hay un problema. El propietario del recurso simplemente toma y da en forma clara su nombre de usuario y contraseña al cliente, lo cual no es seguro. Originalmente se hizo solo para clientes en los que confía o para aquellos que forman parte del sistema operativo. Más tarde, solo se permitió la migración de la autenticación de inicio de sesión y contraseña a OAuth 2.0. Las pautas de seguridad actuales prohíben su uso. 



Código de Autorización



El flujo más común en este momento. Se usa principalmente para clientes confidenciales, pero con la introducción de validación adicional con PKCE, también se puede usar para clientes públicos. 



En este flujo, el cliente interactúa con el propietario del recurso a través del usuario-agente (navegador). El agente de usuario tiene un requisito: debe poder trabajar con redireccionamientos HTTP. Sin esto, el propietario del recurso no podrá acceder al servidor de autorización y regresar con la concesión. 







Este flujo es más complicado que los anteriores, por lo que lo analizaremos paso a paso. Para empezar, imaginemos que somos propietarios de un recurso y fuimos a la página de un servicio de aprendizaje en línea que quiere guardar los resultados del aprendizaje en nuestra nube. Necesita acceder a nuestro recurso, por ejemplo, un directorio determinado en la nube. Hacemos clic en "Iniciar sesión" y comienza el viaje a través del flujo de concesión del código de autorización:



  • En el primer paso, el cliente redirige al propietario del recurso mediante el agente de usuario a la página de autenticación del servidor de autorización. En el URI, especifica el ID del cliente y el URI de redirección. El URI de redirección se usa para comprender dónde devolver el propietario del recurso después de que la autorización sea exitosa (el propietario del recurso otorgará permiso al alcance solicitado por el cliente).

  • user-agent, resource owner .

  • Resource owner , consent screen .

  • Resource owner user-agent URI, redirection URI. query- authorization code — , , resource owner . 

  • authorization code , access token ( refresh token, ).

  • authorization code, , access token ( refresh token). . 



Si nos imaginamos en el lugar del propietario del recurso, entonces solo vemos una redirección al servidor de autorización, nos autenticamos, confirmamos el acceso a la pantalla de Consentimiento y nos envían a un servicio que ya se está ejecutando. Por ejemplo, pasamos por esto muchas veces cuando vamos al servicio con una cuenta de Google, Facebook o Apple.



El siguiente flujo se basa en esto.



Subvención implícita



Esta es una optimización del flujo de concesión del código de autorización para clientes públicos que saben cómo trabajar con URI de redirección. Por ejemplo, para aplicaciones de navegador JavaScript o aplicaciones móviles. El requisito del usuario-agente, a través del cual interactúan el cliente y el propietario del recurso, permanece: debe poder trabajar con redireccionamientos HTTP.



Existe una diferencia principal entre el código de autorización y el implícito: en lugar de recibir el código de autorización y el token de acceso, recibimos inmediatamente el token de acceso después de la autorización exitosa del propietario del recurso. Además, el secreto del cliente no se usa aquí por razones de seguridad: la aplicación se puede desensamblar y recuperar. La autenticidad es verificada solo por el URI de redirección.







Muchos pasos de este diagrama son similares a los pasos del código de autorización, pero propongo analizarlos en detalle también. Imaginemos que una aplicación de navegador quiere guardar su configuración en nuestro repositorio de Git. Hacemos clic en "Iniciar sesión en GitHub" y en esta etapa comienza el flujo implícito:



  • El cliente utiliza el agente de usuario y un redireccionamiento HTTP para redirigir al propietario del recurso al servidor de autorización. En los parámetros de solicitud, pasa el ID de cliente y los URI de redireccionamiento necesarios para autenticar al cliente y luego devolver el propietario del recurso.

  • El propietario del recurso se autentica comunicándose a través del agente de usuario con el servidor de autorización. Al mismo tiempo, confirma la emisión de una subvención al cliente, con cuyo DNI de cliente vino.

  • grant ( «allow» consent screen), user-agent resource owner redirection URI. , URI fragment access token (URI fragment — , URI ‘#’).

  • user-agent. User-agent redirection URI web-, access token . , , , CDN.

  • Web- web- ( ), redirection URI, , .

  • User-agent , , web-hosted client resource, access token.

  • El usuario-agente del token de acceso resultante simplemente se transfiere al cliente.



Este es un flujo complejo. Tiene poco uso en escenarios del mundo real. Pero todavía se puede encontrar en proyectos heredados.



Autorización de dispositivo (RFC 8628)



De 2012 a 2019, han aparecido muchos dispositivos inteligentes que son inconvenientes para iniciar sesión. Por ejemplo, es inconveniente ingresar un nombre de usuario y una contraseña complejos en el televisor cada vez que abre un recurso. Esto no es posible en algunos dispositivos, como los sistemas operativos de servidor sin una interfaz gráfica. En agosto de 2019, este flujo apareció solo para tales escenarios. 



Hay al menos 3 requisitos para que los dispositivos funcionen con el flujo de concesión de autorización de dispositivos para que sea posible:



  • El dispositivo debe poder realizar solicitudes HTTPS salientes.

  • El dispositivo debe poder mostrar el URI y la ID al usuario.

  • Cada dispositivo autorizado pertenece al propietario del recurso, quien, para una autorización exitosa, debe tener otro dispositivo con un navegador para ir al URI especificado e ingresar el código especificado.







Quizás el esquema parezca complicado debido a la abundancia de flechas. Analicémoslo paso a paso, ya que analizamos los flujos complejos antes.



Digamos que estamos intentando iniciar sesión en un servicio web usando el televisor. Vemos el botón "Iniciar sesión como dispositivo" y hacemos clic. En este momento, comienza nuestro flujo de dispositivos:



  • El televisor realiza una solicitud al servidor de autorización y le proporciona su ID de cliente.

  • El servidor de autorización verifica que dicho cliente esté registrado y tenga el tipo de concesión adecuado.

  • , Authorization server device code, user code verification URI. Device code — , .

  • user code verification URI — resource owner. Redirection URI , QR- — .

  • , user code verification URI, .

  • resource owner. verification URI, user code, , scope . resource owner .

  • Durante todo este tiempo, el dispositivo (punto 3) sondeó al servidor de autorización sobre su éxito. El dispositivo vuelve a ir al servidor de autorización con su código de dispositivo y su ID de cliente con la esperanza de que la autorización haya pasado esta vez.

  • Esta vez, cuando el propietario del recurso ha confirmado la transferencia de los derechos necesarios al dispositivo, el servidor de autorización devuelve un token de acceso en respuesta a la solicitud (si la configuración del servidor y el token de actualización lo proporcionan). Y con la ayuda del token, el dispositivo ya puede seguir funcionando con el recurso.



A pesar de la aparente complejidad de las flechas, este flujo también es bastante simple. Si necesita interactuar con dispositivos (y tenemos muchos de ellos: rastreador, caja registradora, escaparates y otros dispositivos), entonces debe usar este flujo.



En lugar de salida



En este artículo, he omitido muchos detalles para poder hablar de las cosas más importantes de la manera más sencilla y accesible. Por ejemplo, los tipos de solicitudes, cómo y de qué forma pasar parámetros, qué caracteres se permiten como valores para eso. 



Si desea profundizar en el tema con más detalle, le recomiendo RFC 6749 (para OAuth 2.0) y RFC 8628 (para Device Flow). También puede consultar el recurso OAuth para obtener RFC actualizadas .



Si el artículo fue útil y desea más detalles, escriba en los comentarios, y en los próximos artículos hablaré sobre PKCE, el protocolo de autenticación OpenID Connect 1.0, nuestra implementación del servidor de autenticación y mucho más.



Enlaces útiles:






All Articles