Cómo implementar OAuth2 para cuentas de Google en Node.js

Cómo implementar el inicio de sesión de OAuth2 a través de Google usando enlaces de autorización y recuperando información de perfil de la API de información de usuario de Google.

Para este tutorial, vamos a utilizar el marco JavaScript de pila completa de CheatCode, Joystick. Joystick reúne un marco de interfaz de usuario de front-end con un back-end de Node.js para crear aplicaciones.

Para comenzar, querremos instalar Joystick a través de NPM. Asegúrese de estar usando Node.js 16+ antes de instalar para garantizar la compatibilidad (lea este tutorial primero si necesita aprender a instalar Node.js o ejecutar varias versiones en su computadora):

Terminal

npm i -g @joystick.js/cli

Esto instalará Joystick globalmente en su computadora. Una vez instalado, vamos a crear un nuevo proyecto:

Terminal

joystick create app

Después de unos segundos, verá un mensaje desconectado de cd en su nuevo proyecto y ejecute joystick start . Antes de hacer eso, necesitamos instalar dos paquetes adicionales, googleapis y node-fetch :

Terminal

cd app && npm i googleapis node-fetch

Después de instalar esos paquetes, puede continuar e iniciar su aplicación:

Terminal

joystick start

Después de esto, su aplicación debería estar ejecutándose y estamos listos para comenzar.

Obtener credenciales de Google

Para completar este tutorial, primero, debemos dirigirnos a Google Developer Console y generar credenciales para autenticar nuestra aplicación con Google. Para hacerlo, diríjase a Google Developer Console e inicie sesión con la cuenta donde vive (o vivirá) su proyecto.

Una vez que haya iniciado sesión, desde el ícono de hamburguesa en la parte superior izquierda, abra el menú de navegación flotante y seleccione "API y servicios". A continuación, seleccione el enlace de navegación "Credenciales" en el menú de la izquierda. Desde esta página, si ya tiene credenciales existentes que le gustaría usar, ubíquelas en la lista bajo el encabezado "ID de cliente de OAuth 2.0" y luego continúe con la siguiente sección.

Si no aún no tiene credenciales, asegúrese de haber seleccionado su proyecto en el menú desplegable a la derecha del logotipo de Google Cloud en la barra de navegación. Si aún no tiene un proyecto, deberá crear uno para continuar.

Con su proyecto creado y seleccionado, desde la página "Credenciales" que cargamos arriba, haga clic en el enlace azul "+ Crear credenciales" cerca de la parte superior de la página. Esto revelará un menú desplegable. Queremos hacer clic en la segunda opción "ID de cliente de OAuth".

En la página siguiente, si aún no lo ha configurado, se le pedirá que "configure su pantalla de consentimiento". Esta es la pantalla a la que los usuarios son redirigidos inmediatamente después de hacer clic en el botón "Iniciar sesión con Google" en su aplicación. Si aún no lo ha configurado, haga clic en el botón junto al mensaje de advertencia y complete la pantalla de consentimiento.

En la siguiente pantalla, si solo está probando cosas, querrá usar la opción "Externo" para el "Tipo de usuario". Esto asegurará que cualquier cuenta de Google pueda usarse para su inicio de sesión (aquí, en desarrollo, pero también en producción).

Una vez que esto esté configurado, será redirigido a un formulario para configurar su pantalla de consentimiento. En el primer paso, proporcionaremos información básica sobre nuestra aplicación. Para este tutorial, en la sección "Dominio de la aplicación", solo estamos ingresando http://localhost:2600 para la "Página de inicio de la aplicación" y http://localhost:2600/privacy y http://localhost:2600/terms para las URL de privacidad y términos, respectivamente. Cabe destacar que nos estamos saltando "Dominios autorizados" aquí.

En la siguiente pantalla, "Ámbitos", podemos omitir esto, ya que pasaremos los ámbitos que necesitamos directamente a Google a través de nuestro código. Finalmente, en la pantalla de resumen, verifique que todo se vea correcto y luego haga clic en "Volver al panel" en la parte inferior de la página.

Desde esta pantalla, querrá desplazarse hacia abajo y ubicar la sección "Probar usuarios". Aquí, queremos agregar usuarios de Google que podrán usar nuestro flujo de inicio de sesión de OAuth en las pruebas (requerido ya que actualmente estamos en modo de prueba).

Una vez que haya agregado su usuario de prueba, ahora podemos volver a crear nuestras credenciales de OAuth. Regrese a la página "Credenciales" en "API y servicios" y haga clic en el enlace azul "+ Crear credenciales", seleccionando nuevamente la opción "ID de cliente de OAuth" en el menú desplegable.

En la siguiente pantalla, para "Tipo de aplicación", queremos seleccionar "Aplicación web", ingrese el nombre de nuestra aplicación en "Nombre" y, en "URI de redireccionamiento autorizado", agregue la URL a la que Google redirigirá al usuario después de que apruebe nuestro acceder a su cuenta en la pantalla de consentimiento.

Para este tutorial, estamos usando http://localhost:2600/oauth/google donde el /oauth/google parte será la ruta que conectaremos más tarde para llamar a la función de controlador que intercambiará el token temporal que Google nos envía por un token de acceso permanente asociado con la cuenta del usuario.

Después de completar esto, haga clic en el botón "Crear" en la parte inferior de la pantalla. Esto revelará una ventana emergente con "Su ID de cliente" y "Su secreto". Nota:se recomienda que los guardes en un lugar seguro, como un administrador de contraseñas, antes de seguir adelante.

Una vez que los tenga, a continuación, queremos copiar estas claves en la configuración de nuestra aplicación para que podamos comenzar con la parte del código del tutorial.

Agregar sus credenciales de Google a la configuración de su aplicación

Antes de profundizar en el código, primero queremos agregar las credenciales que acabamos de obtener de Google a la configuración de nuestra aplicación (esto hará que se pueda acceder a ellas de manera fácil y segura en nuestro código). En el proyecto que creamos a través de joystick create app antes, abre el settings.development.json archivo:

/configuraciones.desarrollo.json

{
  "config": {
    "databases": [
      {
        "provider": "mongodb",
        "users": true,
        "options": {}
      }
    ],
    "i18n": {
      "defaultLanguage": "en-US"
    },
    "middleware": {},
    "email": {
      "from": "",
      "smtp": {
        "host": "",
        "port": 587,
        "username": "",
        "password": ""
      }
    }
  },
  "global": {
    "google": {
      "clientId": "348181960606-aqmbd10e22qd1lru9nc41ehn4ranrq8e.apps.googleusercontent.com",
      "redirectURI": "http://localhost:2600/oauth/google"
    }
  },
  "public": {},
  "private": {
    "google": {
      "secret": "<Paste your secret here>"
    }
  }
}

En este archivo, primero, bajo el global objeto, queremos agregar un objeto en la tecla google que contiene dos propiedades:clientId y redirectURI . Aquí, clientId es el valor copiado del cuadro "Su ID de cliente" arriba mientras que redirectURI es la URL que ingresamos para los "URI de redirección autorizados" arriba.

Ponemos esto debajo de global aquí ya que queremos que esta información sea accesible globalmente en nuestra aplicación (es decir, en el navegador y en el servidor). Tenga en cuenta, sin embargo, que hemos omitido el valor "Tu secreto" aquí.

Estamos agregando eso valor abajo en el private objeto, creando de nuevo un google objeto y en ese objeto, configurando secret como clave y asignando el valor a "Tu secreto" que copiamos en el panel de control de Google. Como habrás adivinado, private aquí está aislado solo para el servidor lado de nuestra aplicación (no accesible al público ni a nadie más que a nosotros mismos y a nuestro código del lado del servidor).

Con todo eso fuera del camino, ahora, finalmente estamos listos para profundizar en el código.

Cableando un captador para generar el enlace de inicio de sesión de OAuth

A diferencia de la mayoría de las implementaciones de OAuth2, Google es un poco diferente en la forma en que maneja la redirección inicial para los usuarios. Mientras que la mayoría de las API ofrecerán una URL directa para redirigir (con algunos parámetros de consulta), Google prefiere que use su API para generar primero la URL de redirección y luego enviar a los usuarios a esa URL.

Para hacer esto en nuestra aplicación, necesitaremos conectar una forma de generar esa URL. Vamos a usar la función de getters de Joystick para ayudarnos a hacer esto. Los captadores son una forma abreviada de conectar una API JSON-RPC en su aplicación (usted escribe funciones y Joystick las asigna automáticamente a rutas HTTP GET como /api/_getters/myGetterName en su servidor).

En el /api carpeta creada para usted en la raíz de su aplicación, queremos agregar otra carpeta oauth y allí, un archivo llamado getters.js :

/api/oauth/getters.js

import { google } from 'googleapis';
import joystick from '@joystick.js/node';

export default {
  googleOAuthPermissionURL: {
    get: (input = {}, context = {}) => {
      const oauth2Client = new google.auth.OAuth2(
        joystick?.settings?.global?.google?.clientId,
        joystick?.settings?.private?.google?.secret,
        joystick?.settings?.global?.google?.redirectURI,
      );

      return oauth2Client.generateAuthUrl({
        // NOTE: Passing 'offline' retrieves a refresh_token but we shouldn't need this for logins.
        access_type: 'online',
        scope: [
          'profile',
          'email'
        ],
        // NOTE: State is a generic "metadata" field that allows us to attach identifying 
        state: JSON.stringify({}),
      });
    },
  },
};

Arriba, agregamos todo el código que necesitaremos para generar la URL de consentimiento de OAuth inicial a la que redirigiremos a nuestros usuarios. Para hacerlo, definimos un getter llamado googleOAuthPermissionURL . En Joystick, un captador es simplemente un objeto asignado a una tecla que representa el nombre del captador que queremos definir. En ese objeto, como mínimo debemos asignar una función get() que, como su nombre lo indica, "obtiene" algunos datos cuando se llama a nuestro captador.

Detrás de escena, Joystick asigna nuestro nombre de captador a una ruta en /api/_getters/googleOAuthPermissionURL . Como veremos en el cliente, usaremos una función especial en el marco de la interfaz de usuario de Joystick @joystick.js/ui llamado get() que llama a un captador. Detrás de escena, esto solo hace una solicitud HTTP GET a esta ruta generada dinámicamente. Cuando esa ruta coincide en el servidor, el get() se llama a la función que estamos definiendo para nuestro captador anterior.

A esa función, esperamos pasar cualquier input incluido al llamar al get() en nuestra interfaz de usuario como primer argumento, y como segundo, el context para la solicitud que incluye HTTP req uest object, el usuario conectado actualmente (si existe) y otros metadatos relacionados con la solicitud.

Aquí, dentro de nuestro get() función para googleOAuthPermissionURL , comenzamos haciendo una llamada a new google.auth.OAuth2() y almacenar su valor de retorno en una variable oauth2Client . Para acceder a esto, estamos importando la exportación con nombre google (indicado por las llaves que rodean google en nuestra declaración de importación) desde el googleapis paquete que instalamos al comienzo del tutorial.

A esa función, técnicamente, un constructor de clases, le pasamos tres argumentos:

  1. ID de cliente de nuestra aplicación.
  2. El secreto de nuestra aplicación.
  3. URI de redirección de nuestra aplicación.

Para acceder a esos valores, los extraemos del archivo de configuración al que los agregamos anteriormente mediante el joystick.settings objeto accesible a través del joystick predeterminado exportar desde el @joystick.js/node paquete (la contraparte del "lado del servidor" de @joystick.js/ui , instalado cuando ejecutamos joystick create app anterior).

Presta mucha atención a los caminos aquí. Recuerda que nuestro secret fue almacenado en el private objeto mientras que nuestro clientId y redireccionarURIwere stored in the objeto global`.

A continuación, en la parte inferior de nuestro get() función, devolvemos una llamada a oauth2Client.generateAuthUrl() . A eso , pasamos un objeto de opciones con tres propiedades:

  1. access_type que se establece en online . Esto le dice a Google que queremos generar un token de acceso de un solo uso, no uno de larga duración (eso es todo lo que necesitamos para acceder a la cuenta). Si pasamos offline aquí, Google incluirá un token de actualización que nos permitirá actualizar el token de acceso cuando caduque después de la vida útil asignada (útil si nos vamos a conectar a una cuenta de Google para realizar funciones API en nombre del usuario).
  2. scope que se establece en una matriz de cadenas que contienen ámbitos API (permisos sobre lo que podemos acceder en la cuenta del usuario). Advertencia justa :Google tiene una tonelada de alcances disponibles.
  3. state que es un valor de cadena opcional (aquí proporcionamos un ejemplo de cadena de un objeto de múltiples valores) que nos permite pasar información de identificación junto con la solicitud. Debido a que la solicitud inicial del usuario está desconectada del intercambio de tokens, el state valor nos da una forma de identificar cuál la solicitud de intercambio de token pertenece a qué usuario (si es necesario).

Eso es todo lo que tenemos que hacer. Ahora, cuando llamemos a este captador, se devolverá una URL para redirigir a nuestro usuario.

Muy rápido, para asegurarnos de que esto funcione, necesitamos importar este archivo de captadores y adjuntarlo al esquema de nuestra API ubicado en /api/index.js :

/api/index.js

import oauthGetters from './oauth/getters';

export default {
  getters: {
    ...oauthGetters,
  },
  setters: {},
};

Aquí, solo estamos usando la extensión de JavaScript ... operador para "distribuir" o "desempaquetar" el contenido del objeto exportado por defecto desde /api/oauth/getters.js en el principal getters objeto del esquema de nuestra API. Este objeto de esquema se entrega a la función de inicio de nuestro servidor en /index.server.js como api que finalmente registra todos nuestros getters y setters como rutas en nuestro servidor.

Agregar una función de ruta y controlador para el intercambio de tokens de OAuth

Antes de pasar al cliente para poner en uso nuestro captador, para ahorrar algo de tiempo y confusión, vamos a conectar la ruta a la que Google redirigirá al usuario para el proceso de intercambio de tokens junto con la función que manejará ese proceso. (y obtener los datos de perfil de nuestro usuario).

/index.servidor.js

import node from "@joystick.js/node";
import api from "./api";
import google from "./api/oauth/google";

node.app({
  api,
  routes: {
    "/": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "/oauth/google": (req, res) => {
      google({ req, res });
    },
    "*": (req, res) => {
      res.render("ui/pages/error/index.js", {
        layout: "ui/layouts/app/index.js",
        props: {
          statusCode: 404,
        },
      });
    },
  },
});

Aquí hemos añadido una ruta /oauth/google que recibirá una solicitud HTTP GET de Google siempre y cuando nuestro usuario apruebe la solicitud de autorización en la URL que aprendimos a generar arriba.

Cuando recibimos esa solicitud de Google, como insinuamos anteriormente, debemos intercambiar un token temporal que incluyen en los parámetros de consulta de la solicitud por un token de acceso permanente. Así es como funciona el estándar OAuth2 (utilizado por muchas empresas diferentes para gestionar la autenticación de usuarios de terceros).

  1. Redireccionamos al usuario al proveedor externo con detalles sobre los permisos que nos gustaría que se nos concedieran en relación con su cuenta.
  2. Si el usuario aprueba esos permisos, el proveedor externo envía una solicitud a una URL que especificamos, incluido un token temporal que se puede cambiar por un token permanente.
  3. Llamamos a otro extremo de la API y pasamos ese token temporal junto con las credenciales que usamos al iniciar la solicitud (prueba de que somos la aplicación prevista que recibe el permiso del usuario) para obtener el token de acceso permanente.

Todos los proveedores son un poco diferentes en la forma en que manejan los detalles, pero en términos generales:este es el flujo de trabajo que se lleva a cabo. Para manejar la solicitud de Google, arriba, dentro de la función de controlador de nuestra ruta, hemos realizado una llamada a una función hipotética google() pasando un objeto que contiene el req uest y res Ponse objetos de nuestra ruta.

A continuación, conectemos esa función (preste atención a la ruta hipotética que usamos al importar la función en la parte superior del archivo) y hagamos que funcione.

/api/oauth/google.js

/* eslint-disable consistent-return */

import joystick from '@joystick.js/node';
import { google as googleAPI } from 'googleapis';

const oauth2Client = new googleAPI.auth.OAuth2(
  joystick?.settings?.global?.google?.clientId,
  joystick?.settings?.private?.google?.secret,
  joystick?.settings?.global?.google?.redirectURI,
);

const getGoogleUser = (accessToken = '') => { ... };

const exchangeToken = async (code = '') => { ... };

export default async (options) => {
  try {
    const state = options?.req?.query?.state ? JSON.parse(options?.req?.query?.state) : null;
    const token = await exchangeToken(options?.req?.query?.code);
    const access_token = token?.access_token;
    const googleUser = await getGoogleUser(access_token);

    console.log({
      state,
      token,
      access_token,
      googleUser,
    });

    options.res.redirect('/');
  } catch (exception) {
    options.res.status(500).send(`[google] ${exception.message}`);
  }
};

Primero, en la parte superior de nuestro archivo, observe que estamos importando nuevamente el google exportar desde el googleapis paquete, sin embargo, esta vez vamos a cambiar el nombre de la variable nombrada de google a googleAPI usando el as operador para evitar colisiones de nombres en nuestro archivo.

A continuación, de forma idéntica a lo que vimos cuando configuramos nuestro getter anteriormente, estamos llamando a new googleAPI.auth.OAuth2() en la parte superior de nuestro archivo, pasando exactamente las mismas credenciales que antes (también en el mismo orden). Al igual que antes, esto nos brinda una instancia de la API de Google OAuth2 en nuestro código.

Antes de ponerlo en uso, en la función exportada como default de nuestro archivo, hemos mapeado las llamadas que necesitaremos hacer para manejar el intercambio de tokens y obtener los datos del perfil de nuestro usuario. Para manejar cualquier error inesperado, hemos envuelto el cuerpo de nuestra función en un try/catch declaración. En el caso de que alguno de nuestros códigos "atrape", llamamos al .status().send() método en el options.res valor que anticipamos que se pasará a la función en el momento de la llamada. Aquí, el 500 siendo pasado a status() es el código de estado HTTP para un "Error de servidor interno" genérico. A send() , solo pasamos una cadena que contiene cualquier mensaje de error que hayamos recibido.

Dentro del try , comenzamos comprobando si hay algún state El valor se transmitió junto con nuestra solicitud. Recuerde, cuando generamos nuestra URL de solicitud de autorización anteriormente, incluimos un objeto en forma de cadena como state que podemos usar para identificar la solicitud.

Aquí, verificamos si state se define en el query parámetros del req uest objeto y, si lo es, suponga que contiene un objeto JSON en cadena que necesitamos analizar en un objeto JavaScript con JSON.parse() . Si es no definido, solo queremos establecer el const state variable que estamos creando aquí para null .

A continuación, hacemos una llamada a una función que definiremos a continuación exchangeToken() , pasando el code parámetros de req?.query (un objeto que contiene todos los parámetros de consulta de la URL de solicitud). Aquí, code es el token que necesitamos intercambiar con Google para recuperar el token de acceso permanente para nuestro usuario.

Saltando un poco, después de que hayamos completado este proceso de intercambio y tengamos un access_token (esperamos recuperar un objeto con múltiples parámetros de Google que estamos almacenando en la variable const token aquí), luego querremos tomar ese access_token y llame al extremo de la API de Google para recuperar el perfil de un usuario.

La idea aquí es que no esperar que el intercambio de tokens haga cualquier cosa menos darnos un token de acceso. Para contextualizar ese token, debemos (pero no es obligatorio) obtener los datos de perfil asociados del usuario para poder usarlos en nuestra aplicación con fines de identificación.

Finalmente, una vez que tenemos las credenciales y los datos de perfil de nuestro usuario, lo desconectamos (no vamos a hacer nada especial con los datos para este tutorial, solo mostraremos cómo recuperarlos) y luego llamamos al res.redirect() función, redirigiendo al usuario/navegador a la raíz de nuestra aplicación.

Para darle sentido a esto, desarrollemos esas dos funciones:exchangeToken() y getGoogleUser() .

Manejo del intercambio de tokens

La buena noticia sobre el intercambio de tokens es que usar el envoltorio API provisto en el googleapis paquete, es bastante simple:

/api/oauth/google.js

/* eslint-disable consistent-return */

import joystick from '@joystick.js/node';
import { google as googleAPI } from 'googleapis';

const oauth2Client = new googleAPI.auth.OAuth2(...);

const getGoogleUser = (accessToken = '') => {...};

const exchangeToken = async (code = '') => {
  try {
    const { tokens } = await oauth2Client.getToken(code);
    return tokens;
  } catch (exception) {
    throw new Error(`[google.exchangeToken] ${exception.message}`);
  }
};

export default async (options) => {
  try {
    ...
    const token = await exchangeToken(options?.req?.query?.code);
    ...
    options.res.redirect('/');
  } catch (exception) {
    options.res.status(500).send(`[google] ${exception.message}`);
  }
};

Aquí, recuperar el token de acceso permanente solo requiere que llamemos al .getToken() método del oauth2Client objeto que inicializamos en la parte superior de nuestro archivo, pasando el code extrajimos de los parámetros de consulta de la solicitud de Google.

En respuesta a esa llamada de función, esperamos obtener un objeto con múltiples propiedades. Aquí, nos preocupamos por el tokens propiedad, por lo que usamos la desestructuración de objetos de JavaScript para "arrancar" la propiedad que queremos de ese objeto devuelto como una variable tokens que luego regresamos desde exchangeToken() .

A continuación, con nuestro token de acceso, echemos un vistazo a cómo obtener los datos del perfil de nuestro usuario.

Recuperando datos de perfil de usuario

Para este paso, usaremos el node-fetch biblioteca que instalamos anteriormente para hablar directamente con el /userinfo de Google Punto final de la API.

/api/oauth/google.js

/* eslint-disable consistent-return */

import fetch from "node-fetch";
import { URL, URLSearchParams } from 'url';
import joystick from '@joystick.js/node';
import { google as googleAPI } from 'googleapis';

const oauth2Client = new googleAPI.auth.OAuth2(...);

const getGoogleUser = (accessToken = '') => {
  try {
    const url = new URL(`https://www.googleapis.com/oauth2/v1/userinfo`);
    const searchParams = new URLSearchParams({
      alt: 'json',
      access_token: accessToken,
    });

    url.search = searchParams;

    return fetch(url, {
      method: 'GET',
    }).then(async (response) => {  
      const json = await response.json();
      return json;
    });
  } catch (exception) {
    throw new Error(`[google.getGoogleUser] ${exception.message}`);
  }
};

const exchangeToken = async (code = '') => { ... };

export default async (options) => {
  try {
    const state = options?.req?.query?.state ? JSON.parse(options?.req?.query?.state) : null;
    const token = await exchangeToken(options?.req?.query?.code);
    const access_token = token?.access_token;
    const googleUser = await getGoogleUser(access_token);

    console.log({
      state,
      token,
      access_token,
      googleUser,
    });

    options.res.redirect('/');
  } catch (exception) {
    options.res.status(500).send(`[google] ${exception.message}`);
  }
};

Primero, en la parte superior de nuestro archivo, hemos agregado algunas importaciones. Primero, hemos importado el node-fetch biblioteca que instalamos anteriormente como fetch y desde la biblioteca integrada de Node.js url , hemos importado las exportaciones nombradas URL y URLSearchParams .

Abajo en getGoogleUser() ponemos todo esto a trabajar. Primero, creamos un new URL() objeto, pasando la URL para /userinfo de Google endpoint y almacenándolo en la variable const url . A continuación, creamos otra variable searchParams que almacena el valor de una llamada a new URLSearchParams() . Esa función constructora toma un objeto de pares clave/valor que queremos convertir en parámetros de URL.

Aquí estamos especificando alt como json cuál es el tipo de datos que queremos recibir para el perfil del usuario y access_token que se establece en el access_token acabamos de recuperar a través de exchangeToken() .

A continuación, en el url objeto que recibimos de new URL() , le asignamos un .search propiedad dinámicamente, asignándola al valor que acabamos de almacenar en searchParams . Esto crea un objeto de URL completo que luego podemos pasar a fetch() para definir la URL que queremos obtener o "buscar".

A fetch() , como primer argumento que pasamos en ese url y como segundo, pasamos un objeto de opciones con una sola propiedad method establecido en GET (técnicamente innecesario como método de solicitud predeterminado para fetch() es GET pero esto elimina algo de oscuridad en nuestro código).

Porque esperamos fetch() para devolver una promesa de JavaScript, encadenamos una llamada a .then() para manejar una respuesta exitosa de la API. A .then() , pasamos una función de devolución de llamada, agregando el async palabra clave para que podamos usar await dentro sin desencadenar un error de sintaxis en JavaScript. Esa función recibe el objeto de respuesta sin procesar de fetch() .

Como le dijimos a Google que queremos una respuesta JSON, debemos llamar al .json() método en el response objeto (que a su vez devuelve una Promesa). Para mantener nuestro código limpio, usamos el await palabra clave en lugar de encadenar en otro .then() llamar. Si todo va bien, almacenamos la respuesta en nuestra variable const json y luego devolverlo desde nuestra función.

Según cómo esté escrito esto, esperamos que el objeto de perfil de usuario que acabamos de recibir de Google esté en ese json variable para "burbujear" hasta el return fetch() llamada que luego será devuelta desde getGoogleUser .

¡Deberias hacer eso! Ahora, tenemos todos los datos que necesitamos para completar nuestro console.log() (e implementar nuestra lógica personalizada para almacenar esos datos).

A continuación, para concluir, debemos bajar al cliente y llamar a nuestro googleOAuthPermissionURL original getter para iniciar el proceso de inicio de sesión de OAuth.

Llamar al inicio de sesión de OAuth desde el cliente/navegador

La última parte es fácil. Ahora, vamos a conectar un botón de ejemplo en nuestra interfaz de usuario para iniciar el proceso de inicio de sesión de OAuth y probar todo esto. Abramos el componente de página existente que ya está conectado a nuestro enrutador en /ui/pages/index/index.js y reemplace el contenido con lo siguiente:

/ui/pages/index/index.js

import ui, { get } from '@joystick.js/ui';

const Index = ui.component({
  events: {
    'click button': () => {
      get('googleOAuthPermissionURL').then((oauthLoginURL) => {
        window.location = oauthLoginURL;
      });
    },
  },
  render: () => {
    return `
      <div>
        <button>Login with Google</button>
      </div>
    `;
  },
});

export default Index;

Esto es todo lo que necesitamos. Arriba, importamos dos cosas del @joystick.js/ui paquete instalado automáticamente cuando ejecutamos joystick create app al comienzo del tutorial:

  1. Una exportación predeterminada ui que contiene la API del componente principal para Joystick.
  2. Una exportación con nombre get cual es el get() función utilizada para llamar captadores en nuestra API.

Un componente en Joystick se crea llamando a ui.component() , pasando un objeto de opciones con una propiedad render establecido en una función (componente mínimo). Para nuestro render() función, todo lo que estamos haciendo aquí es pasar una cadena de HTML con un <div></div> etiqueta que contiene un <button></button> que podemos hacer clic para activar nuestra solicitud de inicio de sesión.

Por encima de render() , hemos agregado otra propiedad events donde podemos adjuntar detectores de eventos al HTML representado por nuestro render() función. Debido a que solo estamos probando cosas, podemos salirnos con la nuestra con un detector de eventos simple para un click evento en nuestro button elemento (los detectores de eventos se definen mediante el esquema <eventToListenFor> <SelectorToListenOn> .

A esa propiedad click button , hemos asignado una función para llamar cuando se detecta un evento de clic en nuestro botón. Adentro, estamos llamando al get() función que importamos arriba, pasando el nombre del captador que definimos anteriormente en el tutorial:'googleOAuthPermissionURL' . Recuerde:no necesitamos pasar nada a este captador, solo esperamos que devuelva una URL a la que podamos redirigir a nuestro usuario.

Porque esperamos el get() función aquí en el cliente para devolver una promesa de JavaScript, encadenamos una llamada a .then() y para ello, pase una función de devolución de llamada para que se active una vez que nuestro captador responda, recibiendo un único argumento oauthLoginURL . Debido a que esperamos que esto sea solo una URL como una cadena a la que queremos redirigir al usuario, podemos configurar window.location igual a ese valor y el navegador redirigirá automáticamente al usuario a esa URL.

¡Eso es todo! Desde aquí, si hemos realizado todo nuestro cableado correctamente, cuando hagamos clic en el botón, deberíamos ser redirigidos a Google, donde podemos aprobar el acceso (recuerde usar la cuenta de prueba que indicó anteriormente en la consola del desarrollador para evitar problemas) , y luego, redirigido a nuestra aplicación. Si todo funcionó como se esperaba, deberíamos ver las credenciales y el perfil del usuario desconectado de la consola del servidor (su ventana de terminal) y redirigido a la página de índice (http://localhost:2600/ ) en el navegador.

Terminando

En este tutorial, aprendimos cómo conectar un flujo de inicio de sesión de OAuth a Google. Primero, aprendimos cómo generar un ID de cliente de OAuth y un secreto en la consola de desarrollador de Google, así como también cómo configurar la pantalla de consentimiento que ven los usuarios cuando inicialmente solicitan iniciar sesión con Google.

A continuación, aprendimos cómo conectar el punto de conexión del captador que genera la URL de redireccionamiento para Google y luego, cómo conectar el proceso de intercambio de tokens para cambiar nuestro token de OAuth temporal por un token de acceso permanente. También aprendimos cómo obtener los datos de un usuario a través de la API de Google usando Fetch, pasando el token de acceso que recuperamos del inicio de sesión para obtener los datos del perfil del usuario.

Finalmente, aprendimos cómo conectar un componente simple con un evento de clic para nuestro botón, llamar a nuestro captador y redirigir dinámicamente al usuario a la URL generada para completar la solicitud de inicio de sesión.