Introducción al protocolo OAuth 2 para delegar la autorización

Escrito por picodotdev el .
planeta-codigo programacion
Enlace permanente Comentarios

El protocolo OAuth es un protocolo en el ámbito de la seguridad que permite que el dueño de los recursos conceda permisos a un cliente sin necesidad de compartir las credenciales, el servidor de recursos unicamente necesita un token que certifique el cliente tiene permisos para acceder a los recursos, el token es emitido por el servidor de autorización en el que el usuario se autentica y en el que servidor de recursos confía en los tokens de autorización que emite. Gracias a la flexibilidad del protocolo OAuth y estar basado en la ubicuidad del protocolo HTTP este es el mecanismo de autorización adoptado predominantemente en la web por los diferentes servicios que ofrecen sus APIs mediante REST sin limitarse a este tipo de APIs. OAuth se compone de varias especificaciones que permiten delegar la autorización, OpenID Connect construido sobre OAuth proporciona otro aspecto de la seguridad, la autenticación.

OAuth

La web se compone de una multitud de páginas desarrolladas y bajo el control de múltiples organizaciones no relacionadas. Los usuarios almacenan sus datos y recursos en un servicio, en una web interconectada compuesta de múltiples servicios surge la necesidad de que un servicio pueda acceder de forma segura a los recursos de un usuario en otro servicio.

Este es el caso de una aplicación de un tercero que por ejemplo desea acceder a los documentos, fotos o datos de un usuario alojada en otro servicio pero sin tener que compartir las credenciales del servicio donde están alojadas las fotos.

El protocolo OAuth

El protocolo OAuth permite al usuario delegar la autorización a un cliente para el acceso a sus recursos proporcionado por un servidor de recursos, permite delegar la autorización de forma segura y sin que el usuario comparta sus credenciales de acceso en el servicio donde están los recursos al mismo tiempo que mantiene la posibilidad de revocar el acceso u otorgarlo de forma temporal.

Esta definición se compone de cuatro elementos:

  • El usuario: que es propietario de los recursos almacenados en el servidor de recursos.
  • El servidor de recursos: que contiene los recursos del usuario.
  • El cliente: la aplicación a la que el usuario quiere otorgar acceso a los recursos del servidor.
  • El servidor de autorización: es el que proporciona el token (entre otras cosas) a modo de credencial al cliente y que el servidor de recurso utiliza para verificar el acceso al recurso o no. El servidor de recurso confía en el servidor de autorización.

Componentes de OAuth 2.0

Componentes de OAuth 2.0
Fuente: Libro OAuth 2 in Action

El protocolo OAuth define varios flujos de autorización que un cliente puede emplear para obtener un token pero deja otros aspectos sin definir como cuál es el formato o contenido del token, tampoco define como el servidor de autorización debe realizar la autenticación del usuario. Esta no definición de algunos aspectos del protocolo le permite ser flexible y adaptarse a diferentes contextos.

El protocolo OAuth en esencia consiste básicamente en una forma que emplea el cliente para obtener un token y que este envía en sus peticiones para el acceso al recurso en el servidor de recursos.

Es el mecanismo de autorización más empleado para proteger servicios que utilizan una API con interfaz REST. En las peticiones REST que el cliente realiza al servidor de recurso envía el access token en una cabecera que el servidor de recurso válida ya sea por sí mismo o validando el access token haciendo una petición al servidor de autorización.

El servidor de autorización junto al access token emite el refresh token, los tokens tienen un tiempo de expiración relativamente corto u que por motivos de seguridad en caso de que sean filtrados han de ser renovados para reducir el tiempo en el que un atacante pueda hacer uso de ellos. Cuando to access token expira el cliente puede obtener un nuevo access token solicitándoselo al servidor de autorización y haciendo uso del refresh token. Dado que los refresh token solo se usan en las peticiones para obtener un nuevo access token hay menos posibilidades de que sean filtrados al contrario que los access token que son enviados en cada petición.

Flujos de autorización

El protocolo OAuth define varios flujos o grant types que un cliente puede seguir para la obtención del access token que le permite acceder los recursos. Para el servidor de recursos la forma que el cliente emplee para obtener el token y para el cliente el contenido o formato del token si tiene alguno no tiene relevancia.

El flujo por defecto, más completo, seguro y recomendado es el flujo de authorization code. Los otros flujos son variaciones adaptadas a algunas limitaciones de otros contextos o casos de uso.

Authorization code grant

El flujo de authorization code es el más completo y por ello más recomendado de usar siempre que se pueda. De forma simplificada en cuanto a la información que se utiliza en las peticiones los pasos de este flujo son los siguientes:

  1. El cliente solicita al usuario autorización para el acceso a un recurso, para ello construye una URL entre cuya información está el identificativo del cliente que redirige al usuario al servidor de autorización para la obtención de un token.
  2. El servidor de autorización autentica al usuario si es necesario, informa y permite al usuario de cambiar los permisos que desea otorgar al cliente. Una vez el usuario otorga el acceso el servidor genera un authorization code que el cliente utiliza para intercambiar por el token. El servidor de autorización envía al agente del usuario una redirección a un endpoint del cliente con el authorization code.
  3. El cliente recibe el callback del servidor de autorización con el authorization code que junto con las credenciales del cliente utiliza para intercambiarlos por el token con una nueva petición al servidor de autorización.
  4. El servidor de autorización recibe la petición del cliente con sus credenciales y el authorization code, si son correctos el servidor de autorización genera un token o access token que el cliente utiliza para obtener acceso al recurso del usuario.
  5. Una vez el cliente tiene el token o access token realiza una petición al servidor de recurso para obtener acceso al recurso.
  6. El servidor de recurso valida el access token y en función de la autorización otorgada en el token concede o no acceso al recurso.

En estos pasos desde el 1 al 4 hay una comunicación a través del agente o navegador del usuario entre el cliente y el servidor de autorización, en el contexto de OAuth a este canal de comunicación se le denomina front channel y como es a través del navegador del usuario es público. En estos pasos el authorization code se envía a través del agente del usuario.

Los pasos 4 y 5 componen lo que en el contexto de OAuth se les denomina back channel y se realiza de forma privada y directa entre el cliente y el servidor de autorización. En estos pasos las credenciales del cliente se mantienen privadas.

Otro aspecto importante es que el usuario en caso de tener que autenticarse únicamente proporciona las credenciales al servidor de autenticación, el cliente no tiene acceso a las credenciales del usuario que únicamente recibe un authorization code y access token, el agente del usuario no tiene acceso al access token.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+----------+
| Resource |
|   Owner  |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier      +---------------+
|         -+----(A)-- & Redirection URI ---->|               |
|  User-   |                                 | Authorization |
|  Agent  -+----(B)-- User authenticates --->|     Server    |
|          |                                 |               |
|         -+----(C)-- Authorization Code ---<|               |
+-|----|---+                                 +---------------+
  |    |                                         ^      v
 (A)  (C)                                        |      |
  |    |                                         |      |
  ^    v                                         |      |
+---------+                                      |      |
|         |>---(D)-- Authorization Code ---------'      |
|  Client |          & Redirection URI                  |
|         |                                             |
|         |<---(E)----- Access Token -------------------'
+---------+       (w/ Optional Refresh Token)
authorization-code-grant.txt

Client credentials grant

El flujo client credentials se usa cuando el cliente accede a los recursos en su propia representación sin la intervención de un usuario.

Para obtener el token el cliente proporciona sus credenciales al servidor de autorización que obtiene como respuesta directamente el access token.

1
2
3
4
5
6
7
+---------+                                  +---------------+
|         |                                  |               |
|         |>--(A)- Client Authentication --->| Authorization |
| Client  |                                  |     Server    |
|         |<--(B)---- Access Token ---------<|               |
|         |                                  |               |
+---------+                                  +---------------+
client-credentials-grant.txt

Device grant

Este flujo se utiliza en caso de que el dispositivo no tenga posibilidad de utilizar un agente de usuario. En vez de ello el dispositivo simplemente presenta un código que el usuario utilizando otro dispositivo se autentica con sus credenciales y junto al código realiza la autorización. El dispositivo comprueba periódicamente si el usuario ha realizado la autorización al servidor de autorización que en caso de ser correcta le devuelve el access token.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+----------+                                +----------------+
|          |>---(A)-- Client Identifier --->|                |
|          |                                |                |
|          |<---(B)-- Device Code,      ---<|                |
|          |          User Code,            |                |
|  Device  |          & Verification URI    |                |
|  Client  |                                |                |
|          |  [polling]                     |                |
|          |>---(E)-- Device Code       --->|                |
|          |          & Client Identifier   |                |
|          |                                |  Authorization |
|          |<---(F)-- Access Token      ---<|     Server     |
+----------+   (& Optional Refresh Token)   |                |
      v                                     |                |
      :                                     |                |
     (C) User Code & Verification URI       |                |
      :                                     |                |
      v                                     |                |
+----------+                                |                |
| End User |                                |                |
|    at    |<---(D)-- End user reviews  --->|                |
|  Browser |          authorization request |                |
+----------+                                +----------------+
device-grant.txt

Implicit grant

El uso del flujo implicit está desaconsejado y se recomienda utilizar el flujo authorization code en caso de ser posible. En este flujo se utiliza cuando el cliente se ubica en el propio agente del usuario. En la redirección del front channel el servidor responde con una redirección que incluyen el fragmento de la URL el access token.

Dado que el agente del usuario tiene acceso al access token se considera menos seguro.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+----------+
| Resource |
|  Owner   |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier     +---------------+
|         -+----(A)-- & Redirection URI --->|               |
|  User-   |                                | Authorization |
|  Agent  -|----(B)-- User authenticates -->|     Server    |
|          |                                |               |
|          |<---(C)--- Redirection URI ----<|               |
|          |          with Access Token     +---------------+
|          |            in Fragment
|          |                                +---------------+
|          |----(D)--- Redirection URI ---->|   Web-Hosted  |
|          |          without Fragment      |     Client    |
|          |                                |    Resource   |
|     (F)  |<---(E)------- Script ---------<|               |
|          |                                +---------------+
+-|--------+
  |    |
 (A)  (G) Access Token
  |    |
  ^    v
+---------+
|         |
|  Client |
|         |
+---------+
implicit-grant.txt

Resource owner password credentials grant

Antes de OAuth para que un servicio pudiera acceder a los recursos de otro servicio una posibilidad era que el usuario compartiera las credenciales del servicio del recurso con el cliente. Compartir las credenciales del usuario entre servicios es una de las cosas que trata de evitar OAuth ya que cualquier cliente inseguro compromete las credenciales del usuario.

En el flujo resource owner password el cliente solicita las credenciales al usuario, una vez las obtiene las utiliza para obtener un access token en el servidor de autorización. Para su funcionamiento el cliente tiene acceso a las credenciales del usuario por eso también está desaconsejado su uso pero al menos una vez el cliente obtiene el access token no tiene que enviar las credenciales del usuario en cada petición al servidor de recursos lo que reduce las posibilidades de que las credenciales sean filtradas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
+----------+
| Resource |
|  Owner   |
|          |
+----------+
     v
     |    Resource Owner
    (A) Password Credentials
     |
     v
+---------+                                  +---------------+
|         |>--(B)---- Resource Owner ------->|               |
|         |         Password Credentials     | Authorization |
| Client  |                                  |     Server    |
|         |<--(C)---- Access Token ---------<|               |
|         |    (w/ Optional Refresh Token)   |               |
+---------+                                  +---------------+
resource-owner-password-credentials-grant.txt

Casos de uso

De forma preferente los clientes han de utilizar el flujo authorization code por ser el más seguro de los flujos que define OAuth 2. La flexibilidad de OAuth le permite ser usado en múltiples dispositivos y protocolos.

Web

En los servicios y aplicaciones web las arquitecturas de las aplicaciones se componen de una parte cliente que se ejecuta en el navegador del usuario y una parte servidor. En esta arquitectura la parte que hace de servidor para el cliente del navegador web a su vez hace de cliente para el servidor del recurso.

En este caso dado el flujo de autorización OAuth que se utiliza es el de authorization code.

Servicio backend

Algunos clientes no dependen de ningún usuario, esto es el caso de servicios que únicamente tienen una parte de backend En este caso dado el flujo de autorización OAuth que se utiliza es el de client credentials.

Aplicación nativa

Los clientes actuales incluyen dispositivos con capacidades de cómputo como los teléfonos inteligentes y tabletas que se ejecutan como aplicaciones nativas de estos dispositivos sin utilizar un navegador.

Este tipo de dispositivos pueden utilizar también el flujo de autenticación authorization code, sin embargo, como se ejecutan en un dispositivo del usuario no pueden mantener la confidencialidad de las credenciales de modo que han de utilizar mecanismos adicionales para obtener unas credenciales únicas del cliente.

El protocolo OAuth en algunas especificaciones relacionadas del protocolo permite el registro de forma dinámica de los clientes que en esencia proporciona unas credenciales únicas para cada instalación de la aplicación de forma que si las credenciales de un cliente son filtradas el problema no afecte al resto de clientes, solo exclusivamente a las credenciales del cliente afectado que puedan ser revocadas sin afectar al resto de clientes.

Dispositivo

Los televisores inteligentes también tienen capacidades para acceder a servicios de terceros pero son unos dispositivos especiales por su limitación de métodos de entrada y de entorno de ejecución que limitan el poder utilizar el flujo de authorization code. Para cubrir las necesidades de estos dispositivos está el flujo device.

Especificaciones

El protocolo OAuth está definido en una colección de especificaciones que son bastante cortas, componen la teoría del protocolo y bastante clarificadoras en varios puntos.

Algunas de las especificaciones definen el protocolo y otras elementos relacionados con OAuth como el registro de clientes de forma dinámica que se utiliza para el caso concreto o las especificaciones que permiten implementar un protocolo de autenticación sobre el protocolo de autorización OAuth como lo es OpenID Connect, OpenID Connect a su vez define otro conjunto de especificaciones.

OpenID Connect

La autorización permite aceptar o rechazar una petición en base a algunos criterios de la petición, también en función del usuario o cliente que realiza la acción o los permisos concedidos.

La autenticación es utilizada para identificar que un usuario es quien dice ser, para identificar a un usuario normalmente se utiliza algo que conoce como una contraseña y más reciente con algo que posee como un segundo factor de autenticación que proporciona un código o un dispositivo hardware físico que identifica al usuario.

OAuth es un protocolo para delegar la autorización pero normalmente se utiliza en combinación con un protocolo para realizar la autenticación, en el ámbito de OAuth el protocolo estándar para la autenticación es OpenID Connect.

OpenID Connect define más claramente algunos puntos que OAuth no entra en su definición. Un punto que define es el formato, contenido y qué información contienen los tokens, para los clientes el token sigue siguiendo un valor opaco pero con esta definición los servidores de recursos pueden utilizar el contenido del token para tomar algunas decisiones de autorización sin necesidad de realizar una petición al proveedor de identidad.

Otro aspecto que modifica OpenID Connect es la introducción de algunos elementos para adecuarlos al ámbito de la autenticación, el cliente se denomina reliying party o RP, el servidor de autorización hacer también las funciones de servidor de recursos y se denomina identity provider o IdP, OpenID connect además emite al mismo tiempo que el access token un nuevo _oken_que identifica al usuario, el identity token y la sesión de este.

Discovery

Hay múltiples proveedores que proporcionan identidad, el estándar de OpenID Connect define un archivo que los proveedores hacen accesible en una URL y que los clientes pueden utilizar para utilizarlo independientemente de del proveedor mientras cumpla con el estándar, este archivo forma parte.

Muchos de los grandes actores de internet como Google, Microsoft, Apple, Facebook o GitHub entre muchos otros permiten a los usuarios que tienen una cuenta en esos servicios iniciar sesión en otros servicios y páginas web sin tener que crear una cuenta nueva en el servicio que se quiere iniciar sesión, esto para los usuarios tiene la ventaja de no necesitar recordar o generar múltiples contraseñas ni crear una cuenta en cada servicio.

En el caso de Google por ejemplo hace público su definición de OpenID Connect en la siguiente ubicación y forma parte del descubrimiento de OpenID Connect.

JOSE y JWT

En OpenID Connect tanto el identity token como el access token son en realidad un JSON codificado en base64, el cliente no necesita conocer ni analizar su contenido pero el servidor de recursos puede analizarlo para tomar alguna decisión de autorización. El servidor de recurso tiene dos posibilidades, analizar el contenido de token lo que requiere conocer qué formato contiene o hacer una petición al identity provider para que este le devuelva su contenido, ambas tienen alguna ventaja pero si es suficiente para evitar una llamada adicional y latencia en las llamadas se evita hacer peticiones al identity provider.

El formato de los tokens según lo define OpenID Connect normalmente es un texto en formato JSON codificado en base64 que aparentemente parece algo aleatorio. Se compone de tres partes separadas por un punto, una cabecera en la que se especifica el algoritmo de firma empleado y formato, un cuerpo que contiene varios campos o claims definidos en el estándar y una firma digital que el servidor de recursos utiliza para validar que ha sido emitido por el servidor de autorización y garantizar que su contenido no ha sido modificado.

El formato de los tokens se engloban en una serie de colección de siglas relacionadas para cada una de las partes los definen:

  • JSON Object Signing and Encryption (JOSE)
  • JSON Web Token (JWT, RFC 7519): son dos documentos JSON en base64 separados por un punto.
  • JSON Web Signature (JWS, RFC 7515): añade una forma digital a los token, siendo el tercer documento de un token.
  • JSON Web Encryption (JWE, RFC 7516): permite el cifrado del cuerpo de los tokens para que su contenido aunque firmado no sea analizable sin la correspondiente clave de descifrado.
  • JSON Web Algorithms (JWA, RFC 7518): define los algoritmos de cifrado que deben usar JWS y JWE.
  • JSON Web Key (JWK, RFC 7517): define el formato para representar una clave criptográfica en formato JSON.

Otra término mencionado es proof key for code exchange o PKCE que evita algunos problemas de seguridad al enviar servidor de autorización un dato que solo conoce el cliente de modo que solo el cliente legítimo pueda intercambiar el autorization code por el access token, útil principalmente para las aplicaciones nativas que no pueden garantizar el secreto de sus credenciales.

Implementar OAuth y OpenID Connect

Dado que los protocolos OAuth y OpenID Connect se basan en el protocolo HTTP cualquier herramienta que utilice este protocolo es capaz de utilizar OAuth incluso de forma transparente sin que el servicio protegido sea consciente de ello con un proxy y un servidor en el que delegar la autenticación y autorización.

Tanto el servidor web Apache HTTPD como Nginx ofrecen plugins para el uso de OAuth y OpenID Connect, cualquier lenguaje de programación capaz de realizar peticiones HTTP y trabajar con JSON como son la mayoría de lenguajes de propósito general poseen librerías de modo que su uso sea fácil empezar a usarlos simplemente incluyendo una dependencia.

Servidor OpenID Connect

Keycloak es un servidor implementado en el lenguaje Java desarrollado por RedHat y publicado con una licencia de código abierto que proporciona la funcionalidad de servidor de autorización y OpenID Connect para cualquier aplicación incluso aquellas no implementadas en Java. Keycloak se caracteriza por implementar las especificaciones con los diferentes flujos de autorización del protocolo OAuth y OpenID Connect.

Cliente de OAuth con Spring

En Java el framework Spring que proporciona dependencias para prácticamente cualquier funcionalidad común que un desarrollador necesite también proporciona soporte para OAuth y OpenID Connect añadiendo unas pocas líneas de configuración e integrándose con el framework de seguridad Spring Security.

Dado que los access tokens tienen un tiempo de expiración relativamente corto estos han de ser renovados utilizando el refresh token, la librería OkHttp proporciona soporte para realizar la renovación de forma transparente a través de la clase Authenticator explicada en su Javadoc.

Servidor de recurso

Al igual que Spring proporciona soporte para la utilización de OpenID Connect el framework de seguridad Spring Security ofrece soporte para la integración de autorización con OAuth de modo que un endpoint de una API REST o funcionalidad de un servicio pueda implementar la lógica para permitir o denegar una petición.

Libros sobre OAuth

Las especificaciones de OAuth y OpenID Connect son cortas y explican el protocolo en detalle con descripciones fáciles de entender, incluso a veces es mejor leer las especificaciones directamente que cualquier otro material.

Aún así, los libros explican de otra forma más guiada y paso a paso el funcionamiento del protocolo, dos libros que he leído y me han parecido una buena introducción al protoclo ha sido por un lado OAuth 2 in Action que explica la teoría de OAuth con varios ejemplos de código y aplicaciones con el que probar el protocolo, por otro lado el libro Keycloak - Identity and Access Management for Modern Applications que explica el uso y repasa la teoría de OAuth de la implementación del protocolo OpenID Connect y OAuth en el servidor Keycloak.


Comparte el artículo: