Autenticação básica usando o GitHub OAuth + Parcel + Node
Published: 2022-09-01 01:56:04

## 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.
{% embed https://youtu.be/h3s_CM_4L5s %}

## 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:
``` bash
npm i axios query-string
npm i -D parcel
```
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:

``` json
"scripts": {
  "dev": "parcel index.html"
}
```

### Back-end
Para o `back-end`, instale os seguintes pacotes:
``` bash
npm i axios cors dotenv express query-string
npm i -D nodemon
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anbbucdxqwf5fu14xwfl.png)

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](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps) 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.

```html
<!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>
```

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.

```javascript
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);
    }
  }
}
```

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](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps).

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
```

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()`.

``` javascript
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`);
});
```

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
```

## 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!
- [Youtube](https://www.youtube.com/c/ProfDiegoPinhoPrograma%C3%A7%C3%A3o)
- [Site](https://www.diegopinho.com.br/)