Autenticação básica usando o GitHub OAuth + Parcel + Node
Diego Martins de Pinho
Posted on September 1, 2022
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
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"
}
Back-end
Para o back-end
, instale os seguintes pacotes:
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).
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>
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);
}
}
}
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
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`);
});
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!
Posted on September 1, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024
November 30, 2024