DEV Community

Diego Martins de Pinho
Diego Martins de Pinho

Posted on • Updated on

Autenticação básica usando o GitHub OAuth + Parcel + Node

Contexto

Começaremos sendo bem honestos: lidar com o processo de criação de usuários - e respectivamente a sua autenticação no sistema - é um perrengue. Além de termos que criar uma boa experiência no front-end, precisamos garantir que o back-end é seguro o sufiente para proteger estes dados sensíveis.

E se eu te disser que existe uma alternativa que, em boa parte dos casos, é mais segura e preferível pelos usuários? Pois bem, estou falando do protocolo OAuth (Open Authorization)!

Em resumo, este protocolo padroniza a forma como podemos atribuir para agentes terceiros a criação e autenticação de usuários dentro de uma aplicação. Sabe quando você usa o Twitter, GitHub, Facebook, Google e afins para logar em um site? É disso mesmo que eu estou falando.

Neste artigo, te ensinarei o básico para fazer este fluxo utilizando a integração GitHub OAuth.

Edição em vídeo

Também gravei um vídeo com todos os passos.

Setup

Para exemplificar esta integração, criaremos uma pequena aplicação usando o Parcel, Node e claro, o GitHub OAuth. O primeiro passo é criar um diretório com dois subdiretórios, um sendo front-end e outro back-end. Inicie ambos os diretórios pelo terminal com o comando npm init -y.

Front-end

Para o front-end, instale os seguintes pacotes:

npm i axios query-string
npm i -D parcel
Enter fullscreen mode Exit fullscreen mode

E para finalizar, no package.json crie um script para rodar a aplicação. Eu costumo usar o termo dev, mas pode ser qualquer coisa:

"scripts": {
  "dev": "parcel index.html"
}
Enter fullscreen mode Exit fullscreen mode

Back-end

Para o back-end, instale os seguintes pacotes:

npm i axios cors dotenv express query-string
npm i -D nodemon
Enter fullscreen mode Exit fullscreen mode

Feito isso, já temos o setup pronto para desenvolvimento. Agora vamos até o GitHub criar um app para a integração.

GitHub

Acesse o seu perfil no GitHub e procure por Settings > Developer Settings. Lá você encontrará a opção de OAuth Apps. Crie um novo app com os dados abaixo (o nome é arbritário, mas use as urls de localhost indicadas).

Image description

O localhost:1234 será o servidor web local do Parcel.

Fluxo

Antes de sairmos codando, é importante entender exatamente qual o fluxo dos passos que seguiremos aqui. Se entrarmos na documentação oficial do GitHub para a integração OAuth, veremos que ela acontece em três etapas:

1 - Autorização
Neste passo, precisamos enviar uma requisição do tipo GET para https://github.com/login/oauth/authorize enviando os seguintes dados (via query strings): client_id, redirect_uri e scope. As duas primeiras informações já imagino que você saiba de onde vem, já a última explicarei já já.

2 - Recebimento e troca de código
Se a requisição acima for feita com sucesso, o GitHub te retornará um código provisório (chamado de code). Este código tem a expiração de apenas 10 minutos e deve ser usado para obter um access_token. Para isso, devemos fazer uma requisição do tipo POST para https://github.com/login/oauth/access_token enviando os seguintes dados: client_id, client_secret, code e redirect_url.

3 - Uso da API
Após receber o código definitivo, também chamado de access_code, podemos usá-lo para fazer requisições autenticadas na API do GitHub. Para isso, basta passar o código por meio do cabeçalho Authorization na requisição.

Agora que entendemos quais são os passos, fica muito fácil desenvolver o código usando as bibliotecas já preparadas anteriormente. Bora?

Desenvolvimento

Front-end

Começaremos com o front-end. Nosso projeto terá uma única página onde tudo vai acontecer. Criaremos o index.html com um botão que acionará o processo de autenticação. Além disso, apenas incluiremos o index.js que será gerenciado pelo Parcel.

<!DOCTYPE html>
<html lang="pt-br">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>GitHub OAuth Integration POC</title>
</head>

<body>
  <h1>Deseja logar?</h1>
  <button class="login">Logar com o GitHub</button>
  <script type="module" src="index.js"></script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

Feito isso, partiremos para o index.js. Neste arquivo, implementaremos a seguinte lógica:

1 - Adiciona um listener no botão que redireciona a página para o GitHub com parâmetros necessários.
2 - No onload, verificaremos se existe a query string code. Se ela existir, significa que a página é um redirecionamento do GitHub. Logo, usaremos esse código para obter dados do usuário no nosso back-end (onde acontecerá a troca pelo access_code.
3 - Os dados obtidos simplesmente serão exibidos no console.

import qs from "query-string";
import axios from "axios";

function redirectToGithub() {
  const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
  const params = {
    response_type: 'code',
    scope: 'user',
    client_id: process.env.CLIENT_ID,
    redirect_uri: process.env.REDIRECT_URL,
  }

  const queryStrings = qs.stringify(params);
  const authorizationUrl = `${GITHUB_AUTH_URL}?${queryStrings}`;
  window.location.href = authorizationUrl;
}

window.onload = async () => {
  // button
  document.querySelector(".login").addEventListener("click", redirectToGithub);

  // checks if user is returning from github
  const { code } = qs.parseUrl(window.location.href).query;
  if(code) {
    try {
      const response = await axios.post(`${process.env.BACK_END_URL}/login`, { code });
      const user = response.data;
      alert("você está logado, meu chapa! dá uma olhada no console!");
      console.log(user);
    } catch (err) {
      alert("ops, deu algum xabú");
      console.log("err", err);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Note que no objeto params, há o uso do scope e as algumas informações são obtidas através do process.env. O scope é uma informação que passamos ao GitHub para indicar quais são os tipos de permissões que desejamos pedir ao usuário. Neste caso queremos apenas os dados sobre o usuário, mas existem vários outros. A lista completa está aqui.

Já o uso do process.env está associado ao uso de variáveis de ambiente. O Parcel já vem integrado com o .env, assim, podemos esconder informações sensíveis, como chaves de API e senhas. No caso deste projeto, basta criar um arquivo .env na raiz com o conteúdo:

CLIENT_ID=<SEU_CLIENT_ID_AQUI>
REDIRECT_URL=http://localhost:1234
BACK_END_URL=http://localhost:5000
Enter fullscreen mode Exit fullscreen mode

Pronto, já podemos partir para o back-end!

Back-end

No back-end utilizaremos o express para criar o endpoint /login que ficará responsável por:

1 - Fazer a troca do code por access_token
2 - Usar o access_token para fazer uma requisição para o GitHub para obter as informações do usuário vinculado a este token.

A primeira parte do código está sendo representada pela função exchangeCodeForAccessToken() enquanto a segunda pela função fetchUser().

import express, {json} from "express";
import cors from "cors";
import axios from "axios";
import qs from "query-string";
import dotenv from "dotenv";
dotenv.config();

const app = express()
app.use(cors());
app.use(json());

app.post("/login", async (req, res) => {
  try {
    const token = await exchangeCodeForAccessToken(req.body.code);
    console.log("token", token);

    const user = await fetchUser(token);
    res.send(user);
  } catch(err) {
    console.log("err", err.response.data);
    res.sendStatus(500);
  }
});

async function exchangeCodeForAccessToken(code) {
  const GITHUB_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
  const {REDIRECT_URL, CLIENT_ID, CLIENT_SECRET} = process.env;
  const params = {
    code,
    grant_type: 'authorization_code',
    redirect_uri: REDIRECT_URL,
    client_id: CLIENT_ID, 
    client_secret: CLIENT_SECRET,
  };

  const { data } = await axios.post(GITHUB_ACCESS_TOKEN_URL, params, {
    headers: {
      'Content-Type': 'application/json'
    },
  });

  const parsedData = qs.parse(data);
  return parsedData.access_token;
}

async function fetchUser(token) {
  const response = await axios.get("https://api.github.com/user", {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });

  return response.data;
}

app.listen(5000, () => {
  console.log(`Server is up and running on port 5000`);
});
Enter fullscreen mode Exit fullscreen mode

Duas coisas importantes de serem notadas. Na função exchangeCodeForAccessToken() usamos na variável params um parâmetro chamado client_secret. Este segredo deve ser criado na mesma página onde temos o client_id.

O segundo ponto importante é que a requisição para obter o usuário é feita passando como header da requisição o access_token.

Por fim, fizemos uso da biblioteca dotenv para trazer as informações do arquivo .env, que no caso do back-end possui os seguintes dados:

CLIENT_ID=<SEU_CLIENT_ID_AQUI>
CLIENT_SECRET=<SEU_CLIENT_SECRET_AQUI>
REDIRECT_URL=http://localhost:1234
Enter fullscreen mode Exit fullscreen mode

Considerações finais

O processo de autenticação usando o GitHub OAuth é apenas a "ponta do iceberg". Ainda é papel do desenvolvedor tratar os dados e tudo o que é necessário envolvendo a entidade do usuário e o token. O que salvar? Como salvar? O que usar? Isso são decisões que partirão do seu design de projeto.

Agradecimentos

Curtiu? Então dê uma passada no meu canal no Youtube ou me procure nas redes sociais para trocarmos uma ideia!

Top comments (0)