Implementando Authorization Code Grant (PKCE)
Clase 25 de 39 • Curso de Autenticación con OAuth
Authorization Code Grant tiene algunos problemas de seguridad cuando se implementa en aplicaciones nativas. Por ejemplo, un atacante malicioso puede interceptar el authorization_code
retornado por el Authorization Server y usarlo para obtener un Access Token.
La Proof Key for Code Exchange (PKCE), definida en https://tools.ietf.org/html/rfc7636, es una técnica usada para mitigar la intercepción del authorization_code
.
Con PKCE la aplicación crea, por cada petición de autorización, una llave criptográfica aleatoria llamada code_verifier
y su valor transformado llamado code_challenge
el cual es enviado al Authorization Server para obtener un authorization_code
y posteriormente enviarlo junto con el code_verifier
para obtener un Access Token.
Conociendo el flujo
- La aplicación nativa inicia el flujo y redirecciona al usuario al Authorization Server enviando los parámetros
code_challenge
ycode_challenge_method
. - El Authorization Server redirecciona el usuario a la aplicación nativa con un
authorization_code
en el query string. - La aplicación nativa envía el
authorization_code
y elcode_verifier
junto con la url de redireccionamiento (redirect_uri
) y elclient_id
al Authorization Server. - El Authorization Server valida la información y retorna un Access Token.
- La aplicación nativa ahora puede usar el Access Token para llamar los recursos en nombre del usuario (API).
Detalles de implementación
Vamos a ver los detalles de implementación usando JavaScript/Node.js, ya que son operaciones y request básicos que puede implementar cualquier lenguaje nativo. Para ver más detalles de una implementación especifica en lenguajes como Java o Swift pueden consultar una guía aquí.
- Necesitamos generar y almacenar un
code_verifier
.
function base64URLEncode(str) { return str .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); } const verifier = base64URLEncode(crypto.randomBytes(32));
- Mediante el
code_verifier
generamos uncode_challenge
que será enviando en el llamado de autorización.
function sha256(buffer) { return crypto .createHash("sha256") .update(buffer) .digest(); } const challenge = base64URLEncode(sha256(verifier));
- Para comenzar el Authorization Code Grant (PKCE), nuestra aplicación nativa deberá enviar primero el usuario a la url de autorización incluyendo el
code_challenge
y el método usado para su generación y así obtener elauthorization_code
.
https://<authorization-server>/authorize? audience=<your-audience>& scope=<your-scopes>& response_type=code& client_id=<your-cient-id>& code_challenge=<code-challenge>& code_challenge_method=S256& redirect_uri=<your-redirect-uri>
- Ahora que nuestra aplicación tiene el
authorization_code
lo debemos cambiar por un Access Token que puede ser usado para hacer llamados a los recursos del usuario. Para ello obtenemos elauthorization_code
(code
) de nuestro paso anterior y hacemos un llamado tipoPOST
al endpoint de obtención de token enviando también elcode_verifier
.
const request = require("request"); const options = { method: "POST", url: "https://<authorization-server>/oauth/token", headers: { "content-type": "application/json" }, body: '{"grant_type":"authorization_code","client_id": "<your-client-id>","code_verifier": "<your-code-verifier>","code": "<your-authorization-code>","redirect_uri": "<your-redirect-uri>"}' }; request(options, function(error, response, body) { if (error) throw new Error(error); console.log(body); });
La respuesta tiene un JSON Web Token, generalmente de tipo Bearer
:
{ "access_token": "eyJz93a...k4laUWw", "token_type": "Bearer" }
- Ya teniendo nuestro Access Token podemos hacer llamados a los recursos del usuario (API) en nombre de él.
const request = require("request"); const options = { method: "GET", url: "https://someapi.com/api", headers: { authorization: "Bearer <access-token>", "content-type": "application/json" } }; request(options, function(error, response, body) { if (error) throw new Error(error); console.log(body); });
Con esto tenemos los conocimientos necesarios para poder implementar este flujo. Recuerda seguir las recomendaciones para darle un uso adecuado.