OAuth 2.0: flujos para obtener un token de acceso

OAuth 2.0[1] es un protocolo de autorización de estándar abierto que permite a terceros (aplicaciones) acceder a los datos de un usuario sin que el usuario tenga que informar sus credenciales. Apps usan este método para obtener acceso a la cuenta de un usuario determinado a través de un client ID o client secret (claves de autenticación de OAuth).

Si desea obtener más información, visite este sitio web: http://oauth.net/2/.

Tipos de clientes

En OAuth, hay dos tipos de clientes:

  • Cliente confidencial: se trata de clientes que pueden garantizar que ningún usuario malintencionado obtendrá un client secret o client ID para intentar pasar por una aplicación válida.

  • Cliente no confidencial: clientes que no pueden garantizar que un usuario malintencionado obtendrá su client secret o client ID para intentar pasar por una aplicación válida. En estos casos, se informa una dirección URL de devolución de llamada para asegurarse de que la información vuelve solo a la aplicación original. Como alternativa, el tiempo de vida del client ID se puede reducir mediante un refresh token.

Formas de obtener un access token

Hay diferentes flujos de OAuth 2.0, que exigen formas distintas de obtener un token de acceso.

Para algunos métodos, debe enviar un extraInfo. Los valores enviados como extraInfo se muestran en el campo Extra Fields de la pantalla de información general de un token, a la que se puede acceder haciendo clic en el nombre de un token en la página Access Tokens.

Authorization Code

1. Generación de un authorization code:

En primer lugar, se debe haber creado una aplicación. Con ella, podemos obtener el client ID, que es el primer elemento que debemos informar a través de una petición POST a este endpoint: <gateway URL>/oauth/grant-code.

El header debe contener la siguiente información:

Content-Type: application/json

Tenemos que incluir los siguientes elementos en el cuerpo:

{
  "client_id": "f9212173-e705-373b-a698-61923e378359",
  "extraInfo": {},
  "redirect_uri": "string",
  "state": "string"
}
El client ID informado debe ser el mismo de la app creada.

Una vez hecho esto, se espera una respuesta que contenga un authorization code, como en el ejemplo siguiente:

{
  "redirect_uri": "string?state=string&code=8748d39f-1d4f-311f-92c6-4b279db1b317"
}

2. Notificación del authorization code para generar el access token:

Debe realizar una nueva petición POST, a este endpoint: <gateway URL>/oauth/access-token.

El header debe contener la siguiente información:

Authorization: Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

En el cuerpo, debemos informar el código de autorización generado por el endpoint anterior. Puede usar x-www-form-urlencoded o JSON para el cuerpo, solo recuerde incluir la cabecera Content-Type correspondiente (application/x-www-form-urlencoded o application/json).

Incluya la siguiente información en el cuerpo:

  • grant_type, con valor authorization_code.

  • code, con el valor recibido del endpoint anterior.

Se generará el token de acceso y se devolverá el siguiente código:

{
  "access_token": "57f10f0e-3d2e-311f-a797-4011f66e1cbf",
  "refresh_token": "ca81cb16-43e4-3e96-aaea-4861e7791dc7",
  "token_type": "access_token",
  "expires_in": 3600
}

Authorization code con PKCE

A partir de la versión 4.6.1.0 de la API Platform , admitimos PKCE en el flujo del código de autorización.

PKCE (Proof Key for Code Exchange) es una extensión de OAuth que tiene como objetivo hacer más seguro el flujo de Authorization Code, evitando el ataque de interceptación — es decir, cuando una aplicación maliciosa accede al código de autorización en una comunicación no TLS y utiliza el código para emitir un token de acceso. Puede leer más sobre esto en la RFC 7636.

PKCE no es necesaria, es el cliente quien decide si la utiliza o no en el flujo de Authorization Code. Si desea utilizar PKCE, debe enviar un code_challenge con un code_challenge_method en la llamada de concesión de código y, en la llamada para obtener el token de acceso, enviar un code-verifier:

  • code-verifier: según la RFC 7636, debe ser una cadena aleatoria encriptada de alta entropía, con un mínimo de 43 caracteres y un máximo de 128, y utilizando los caracteres [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~".

  • code_challenge: código derivado de code_verifier mediante un método de transformación (code_challenge_method).

  • code_challenge_method: método de transformación por el que se genera el code_challenge. Hay dos opciones:

    • plain: code_challenge = code_verifier

    • S256: code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

      Si no se pasa el code_challenge_method, se asume que es plain.

Para utilizar PKCE, el cliente debe incluir la siguiente información en las llamadas:

  • en el cuerpo de la llamada para la generación del código de autorización (/grant-code) descrita anteriormente, incluir el code_challenge y el code_challenge_method.

  • en el cuerpo de la llamada para generación de tokens (/access-token) descrita anteriormente, incluir el code_verifier.

El servidor de autorización no valida si el code_challenge sigue la alta entropía que recomienda la RFC, ya que esto es responsabilidad del cliente. Tampoco es obligatorio el uso de PKCE en el flujo de Authorization Code. Sin embargo, una vez que el cliente envía el code_challenge en la llamada de generación del código de autorización, el servidor de autorización requerirá el code_verifier en la llamada de obtención del token. Este code_verifier será validado en base al code_challenge y code_challenge_method enviados anteriormente y si la validación no es exitosa, el token no será generado.

Refresh Token

El refresh token se usará para actualizar un access token caducado. Para actualizar un token, siga los pasos que se indican a continuación.

En primer lugar, realice una petición POST a este endpoint: <gateway URL>/oauth/access-token.

El header debe contener la siguiente información:

Authorization: Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

En el cuerpo, debemos incluir el refresh_token recibido desde el endpoint anterior (que generó el access token) y el grant type en el formato x-www-form-urlencoded, como en este ejemplo:

grant_type=refresh_token&refresh_token=ca81cb16-43e4-3e96-aaea-4861e7791dc7

Por último, se regenerará el access token y se devolverá un código como el ejemplo siguiente.

{
  "access_token": "ca81cb16-43e4-3e96-aaea-4861e7791dc7",
  "refresh_token": "677b881a-d0b6-3b29-b9a8-f0cdb50ce035",
  "token_type": "access_token",
  "expires_in": 3600
}

Implicit

El flujo Implicit se utiliza para obtener tokens de acceso para clientes públicos de forma optimizada. Puede funcionar a través de una IRO de enrutamiento privado junto con un access token. Estos clientes se implementan normalmente en un navegador mediante un lenguaje de script, como JavaScript.

Al igual que con OAuth 2.0 Authorization Code, se debe haber creado una app para que podamos tener acceso al client ID. Después de eso, realice una petición POST a este endpoint: <gateway URL>/oauth/access-token.

Debe contener el siguiente header:

Content-Type: application/json

Debemos incluir estos elementos en el cuerpo:

{
  "grant_type": "implicit",
  "redirect_uri": "<URL>",
  "client_id": "<client ID>"
}
El client ID debe ser el mismo de la app creada.
Tenga en cuenta que la aplicación debe contener un extra field denominado redirect_uri, con una dirección URL válida como valor, que debe ser informado al solicitar el token (por ejemplo: http://supermock.demo.sensedia.com/). Acceda a esta página para ver más información sobre cómo crear y configurar apps, agregando extra fields.

Cuando se realiza el proceso, se espera una respuesta que contenga el URI y el token de acceso, como el ejemplo siguiente.

{
  "redirect_uri": "www.url.com?access_token=57f10f0e-3d2e-311f-a797-4011f66e1cbf",
  "expires_in": 3600
}

Client Credentials

Al igual que el flujo Implicit, Client Credentials NO genera un refresh_token. Por lo tanto, no es posible actualizar un token de acceso cuando expira.

Cuando se usa este flujo, al crear un nuevo access token, debe informar el nombre de la app y el client ID en el campo Extra Info del token, informando también de todos los valores de los campos adicionales (extra fields) que están registrados para esa app. Acceda a esta página para ver más información sobre la creación y configuración de apps, agregando campos adicionales.

Se debe haber creado una app para que podamos obtener el client ID y el client secret.

A continuación, es necesario realizar una petición POST a este endpoint: <gateway URL>/oauth/access-token.

La petición debe contener el siguiente header:

Authorization: Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

En el cuerpo, debemos informar el grant_type en formato x-www-form-urlencoded:

grant_type=client_credentials

A continuación, se generará el access token y se debe devolver como en el ejemplo siguiente.

{
  "access_token": "6c164a82-84a6-3bc4-8122-f777121a4f62",
  "token_type": "access_token",
  "expires_in": 3600
}

También puede crear nuevos campos adicionales (extra fields) al generar el token de acceso. Para eso, es necesario añadir el siguiente header:

Content-Type: application/json

En el cuerpo, debemos informar el grant_type y los campos adicionales, como en este ejemplo:

{
  "grant_type": "client_credentials",
  "extraInfo": {"value": "32423", "value2": "874yhgt3"}
}

Password

Se debe haber creado una app para que podamos tener acceso al client ID y el client secret.

Después de eso, debe realizar una petición POST a este endpoint: <gateway URL>/oauth/access-token.

El header debe contener esta información:

Authorization: Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

Además del grant_type, el cuerpo debe contener otras dos piezas de información que utilizan el userDirectory. Son el login y la contraseña del usuario, que pueden ser de un LDAP o un REST, y deben estar en el formato x-www-form-urlencoded:

grant_type=password&username=<login de usuario>&password=<contraseña>

Si el usuario y la contraseña son válidos en LDAP o REST, la devolución del access token debe ser similar al ejemplo siguiente:

{
  "access_token": "6c164a82-84a6-3bc4-8122-f777121a4f62",
  "token_type": "refresh_token",
  "expires_in": 3600
}
Para que las características anteriores se apliquen a la app, debe configurar una API Identity.
En el caso de LDAP, se debe informar el usuario en el formato User-Principal-Name (por ejemplo, test@da.sa).
La propiedad oauth.grantTypes.resourceOwnerPassword.userDirectory ya no es necesaria.

JWT

"JSON Web Token (JWT) es un estándar abierto (RFC 7519) que define una forma compacta e independiente de transmitir información de forma segura entre partes como un objeto JSON. Esta información se puede verificar y confiar porque está firmada digitalmente. Los JWT se pueden firmar usando un secreto (con el algoritmo HMAC) o un par de claves pública/privada usando RSA." (https://jwt.io/)

En el flujo JWT, se debe haber creado una app para que podamos tener acceso al identificador de cliente y al secreto de cliente.

A continuación, debe realizar una petición POST a este endpoint: <gateway URL>/oauth/access-token.

El header debe contener la siguiente información:

Authorization: Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

En el cuerpo, debemos informar el code generado por el endpoint de grant-code y el grant type en el formato x-www-form-urlencoded, como en este ejemplo:

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&code=8748d39f-1d4f-311f-92c6-4b279db1b317

El access token se generará de nuevo y se debe devolver como en el ejemplo siguiente.

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJsb2NhbGhvc3QiLCJpc3MiOiIxNzIuMTguMC4xIiwic3ViIjoiOWI1Zjc3MWUtNzgyZC0zNTEwLWI2YmEtMzZlOWM2NWJmZDVkIiwiZXhwIjozNjAwLCJpYXQiOjE1Mjg5MTg2MzcsIkFwcDogIjoiUmljYXJkb0FwcCIsIkNvZGU6ICI6IjRlNWIyMTEzLTJkYzYtM2RlNi1iN2ZkLWNkOTYxOTMxZWQyOSIsImxvZ2luIjoidXNlci5sb2dpbiJ9.bhvFpiZ6em9tUgpHxGQGuH7cmvMy4I7STJDRBUybuPM",
  "token_type": "access_token",
  "expires_in": 3600
  }

Con el código generado, deberá agregar el interceptor JWT al flujo de API. Él será responsable de validar el token. Ver más sobre este interceptor aquí.

OpenID

El flujo OpenID permite a los usuarios usar una credencial existente para acceder a varios sitios web sin necesidad de crear nuevas contraseñas. Esto mejora la seguridad de los usuarios porque su contraseña se proporciona solo a un proveedor en el que confían; este proveedor confirma su identidad y concede acceso a los sitios y aplicaciones que desean usar.

Para utilizar el flujo OpenID, es necesario crear la API Google OpenID. Haga clic aquí para obtener instrucciones sobre cómo crear la API.

La instrucción anterior está en Portugués.

También es necesario que se haya creado una app. Con ella, podemos acceder al client ID, que será el primer elemento informado a través de una petición POST a este endpoint: <gateway URL>/google/oauth/openid/redirect.

La petición debe contener los siguientes headers:

Content-Type : application/json

client_id :  client_id

En el cuerpo, debemos informar el siguiente elemento:

{
  "extraInfo": {}
}
El client_id debe ser el mismo de la app creada.

Una vez hecho esto, debe haber una respuesta que contenga un authorization code, como en el ejemplo siguiente:

{
  "redirect_uri": "string?queryParams"
}

A continuación, debe acceder a la redirect_uri, que da acceso a una página de autenticación de su proveedor. El inicio de sesión llevará a un redireccionamiento automático siguiendo los valores del access token.

Revocación de access token y refresh token

La revocación hace inutilizable un token que sigue siendo válido pero que ya no es necesario. Después de la revocación, el token no se acepta en peticiones a las APIs.

Se puede utilizar el endpoint /revoke de la API «OAuth 2.0» para revocar un access token o un refresh token. En ambos casos, se revocan todos los tokens — es decir, si se revoca un access token y hay un refresh token asociado a él, también se revoca, y si se revoca un refresh token, también se revoca el access token asociado a él.

Para hacer la revocación, hay que enviar una petición POST a <URL del gateway>/oauth/revoke. La petición debe contener el header Authorization:

Authorization : Basic client_id:client_secret
El client_id:client_secret debe ser una cadena convertida a Base64, usando los datos de la app creada.

Ejemplo de header con client_id y client_secret convertidos a Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

En el cuerpo, pase la siguiente información en formato x-www-form-urlencoded:

token=<valor>&token_type_hint=access_token

O

token=<valor>&token_type_hint=refresh_token

Tenga en cuenta que no es necesario pasar el token_type_hint (porque el token se encuentra por el valor introducido), pero hace que la operación de revocación sea más rápida.

Thanks for your feedback!
EDIT

Share your suggestions with us!
Click here and then [+ Submit idea]