cover

Usando OAuth2 con Google y Hono en 6 pasos


Hice este pequeño ejercicio porque me pareció una manera simple y divertida de aprender a usar OAuth2 con Google y así darle sentido para poder implementarlo con cualquier otro proveedor.

Este ejercicio nació de un taller en vivo que suelo hacer con mi comunidad en Discord.

Tú también puedes unirte, te dejo el enlace aquí. Pero, si también quieres construir este login, ven conmigo y hagámoslo juntos.

1. Generando las credenciales

Vamos a APIs en la consola de Google y configuramos la pantalla de consent para luego crear una credencial OAuth.

1.— Activa tu consent screen. 2.— Crea una credencial OAuth. OAuth credential Cuando Google te presente tu client_id y tu client_secret, guárdalos en un lugar seguro o descarga el archivo .json.

👀 No olvides agregar http://localhost:8787 a las URLs de redirección al crear tu credencial OAuth.

Puedes colocarlas como variables de entorno de una vez en un archivo .env Y en tu archivo wrangler.toml que se generará en el siguiente paso.

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

2. Creando/Clonando el proyecto

Generamos un nuevo proyecto Hono para Cloudflare o también puedes clonar el repo terminado desde el link.

[object Object],[object Object]

Funciones reutilizables

Vamos a hacer 3 consumos a diferentes URLs de la API de Google.

La primera es una redirección para conseguir el código que podemos intercambiar por un access_token, una vez que tengamos ese código, haremos el segundo consumo buscando el access token. Teniendo el access_token ya podemos hacer el tercer consumo para conseguir los datos del usuario.

Las tres funciones que hacen cada consumo son las siguientes:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Siempre puedes llamarlas como tú quieras.

3. Vamos a escribir la primera función y usarla

La primera función construye un URL para redireccionar a nuestro usuario que quiere hacer login a la pantalla de consentimiento que activaste.

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Nota como se están usando dos variables de entorno para agregar todos los datos que la URL necesita. También nota como decidimos que redirect_url se empleará si estamos en producción.

Vamos a crear, pues, nuestra Home para redireccionar a nuestro usuario. Puedes encontrar los componentes <Home/> y <Dash/>en el repo.

En <Home/> tenemos un formulario que envía un query string llamado intent que colocamos en el botón.

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

En nuestro index de Hono agregamos:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Toma nota:

  1. Hono nos permite devolver JSX con su método c.html
  2. Estamos capturando los queryParams (searchParams)
  3. Si el intent es igual a google-login utilizamos la función redirectToGoogle.
  4. La función espera que le pasemos redirect y env, pero puedes modificarla a placer.

Nuestro usuario ahora encontraría nuestra pantalla de login y lo estaríamos redireccionando para que nos dé su permiso.

Fixtergeek login screen demo

4. Recibiendo el code

Lo que sigue es la redirección que Google hará una vez que el usuario nos dé permiso de usar sus datos. Esta redirección incluye un parámetro code y eso es justo lo que necesitamos para ejecutar la segunda función:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Escribamos esta segunda función:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

¿Qué pasa en esta función? Recibimos el string del código que Google nos proporcionó después de que el usuario nos diera permiso. En la variable code junto con el objeto de variables de entorno (recuerda que estas variables deben vivir en wrangles.toml). Construimos una nueva url y le hacemos post, devolvemos la respuesta.

La respuesta es un objeto con el access_token dentro.

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

¡Genial! Tenemos progreso, ahora vamos por la tercera.

5. Consiguiendo el email y foto del usuario

Ahora que tenemos el access_token, pues es momento de emplearlo. Escribimos nuestra tercera y última función para conseguir el correo y la imagen del usuario:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Observa como esta función es la más simple, pues ya no necesitamos incluir nuestras credenciales, basta con el token que ya trae consigo toda la información sobre los permisos. Devolvemos la respuesta.

Nuestro if queda de la siguiente manera:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Aquí aprovechamos para colocar la cookie de sesión, pues tenemos todo lo necesario y podemos dejar entrar al usuario a su dashboard, por eso lo redireccionamos también. (en mi ejemplo no hay redirección, solo devuelvo un componente).

¡Bien, ya estamos por terminar! No podemos detenernos ahora. 💪🏼

6. Checando la cookie al iniciar y cerrando sesión

Para terminar, vamos a colocar una pequeña validación. Si la cookie ya está presente, en vez de devolver <Home/> devolveremos <Dash/> en nuestra ruta, antes de todo.

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Y si encontramos un intent igual a logout borramos la cookie y devolvemos <Home/>

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

Recordemos que este intent viene del formulario en <Dash/>:

[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

¡Genial, nuestro flujo está completo! 🤯

Recuerda que te dejo todo el código en el repo de Github. Espero esto te sea útil, si es así, por favor considera suscribirte 😉

Abrazo. Bliss. 🤓

meta cover

¿Qué es el Server Side Rendering (SSR) del que todos hablan?

Checa este otro Post

meta cover

Iniciando la construcción de un Tetris con JS y Canvas

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻