NodeJS: OAuth por trás do código
Chego em casa após um cansativo dia de trabalho e tudo o que quero é ficar rolando o feed das minhas redes sociais e usar meus aplicativos favoritos.
Por algum motivo, no entanto, surge a necessidade de fazer o login para acessar. Mas, em vez de inserir meu e-mail e senha, encontro um botão "Entrar com Google". Isso é maravilhoso.
Mas você sabia que isso acontece por conta de um protocolo?
Um exemplo disso é o OAuth 2.0, que é um protocolo usado para proteger muitas das APIs da web que usamos todos os dias, como as do Facebook, Google e GitHub.
O objetivo desse artigo é refletir sobre o que é e como funciona o OAuth2 e como esse protocolo impulsiona segurança e autenticação em diversas aplicações.
Antes de mais nada, o que é um protocolo?
Um protocolo em engenharia de software é como um conjunto de regras que os computadores seguem para se comunicarem entre si.
Pense nisso como um manual de instruções que diz como os dados devem ser organizados e enviados de um sistema para outro.
Para retomar ao exemplo, antes do OAuth, as pessoas tinham que compartilhar suas senhas com diferentes aplicativos para permitir o acesso aos seus dados. Isso era arriscado e complicado.
Compartilhar senhas significava que os aplicativos tinham acesso direto às credenciais dos usuários. Isso aumentava o risco de roubo de senha e violações de segurança, já que um único vazamento poderia comprometer todas as contas vinculadas àquela senha.
Com o OAuth, as pessoas podem autorizar aplicativos a acessarem suas informações sem precisar compartilhar suas senhas diretamente.
Por exemplo, quando você se conecta a um site usando sua conta do Google ou Facebook, você está usando o OAuth.
Ele permite que o site solicite acesso às suas informações de forma segura, sem precisar que você compartilhe sua senha com o site.
Isso torna a experiência da pessoa usuária mais segura e conveniente.
O que é OAuth 2.0?
No filme "De Repente 30", Jenna quer entrar em uma festa, mas não tem um convite. No entanto, seu amigo Matt a acompanha e convence o segurança de que ela faz parte do seu grupo, permitindo assim sua entrada na festa.
No contexto do OAuth, podemos relacionar os elementos da história "De Repente 30" da seguinte maneira:
- Jenna seria o cliente no contexto do OAuth. Ela é a parte que deseja acessar o recurso protegido, que neste caso é a festa.
- A festa seria o servidor de recursos, que no contexto do OAuth, hospeda os recursos protegidos, que são acessados mediante autorização.
- Matt desempenha o papel de servidor de autorização no cenário do OAuth. Ele age intermediando o acesso de Jenna à festa, obtendo consentimento (autorização) e permitindo sua entrada.
Para compreender adequadamente a terminologia e a arquitetura do OAuth 2.0, utilizarei como exemplo um projeto Node.js que deseja implementar o login do Google para autenticação.
Através desse exemplo, vamos analisar as principais responsabilidades deste protocolo.
Proprietário do Recurso (Resource Owner)
Neste caso, o proprietário do recurso seria o usuário final que possui os dados protegidos no Google.
Ou seja, o usuário que possui uma conta Google com fotos, vídeos, calendários e contatos armazenados.
Cliente (Client)
O cliente é o seu aplicativo Node.js. É o cliente que faz solicitações de API em nome do proprietário do recurso com sua autorização.
Podemos referir que o cliente é o código que você escreve no Node.js para lidar com as solicitações de login com o Google/GitHub/Facebook e acessar os recursos protegidos do usuário.
Servidor de Recursos (Resource Owner)
O servidor de recursos é onde os dados protegidos do usuário são armazenados. Neste caso, o Google atua como o servidor de recursos, hospedando fotos, vídeos, calendários e contatos dos usuários.
Quando o aplicativo solicita acesso a esses recursos, ele faz isso diretamente ao Google/GitHub/Facebook, que verifica a autorização do usuário e fornece acesso se autorizado.
Servidor de Autorização (Authorization Server)
O servidor de autorização é responsável por obter o consentimento do proprietário do recurso e emitir tokens de acesso para obter recursos protegidos hospedados pelo servidor de recursos (Google).
RFC 6749
O RFC 6749 é um documento técnico publicado pela Internet Engineering Task Force (IETF) que descreve o protocolo OAuth 2.0.
De forma geral, o RFC 6749 especifica os detalhes técnicos do protocolo, incluindo os diferentes tipos de concessões (como autorização implícita, autorização explícita, concessão de credenciais de cliente, etc.), os fluxos de autorização (como fluxo do código de autorização, fluxo implícito, fluxo de concessão de senha de recursos, etc.), os parâmetros de solicitação e resposta, e outros aspectos importantes do protocolo.
A sigla RFC, derivada de "Request for Comments" (Pedido de Comentários), é frequentemente empregada para designar documentos que estabelecem padrões e protocolos da Internet, diz:
A estrutura de autorização do OAuth 2.0 permite que aplicativos de terceiros concedam acesso limitado a um serviço HTTP, seja em nome de um proprietário de recurso orquestrando uma interação de aprovação entre o proprietário do recurso e o serviço HTTP, ou permitindo que o aplicativo de terceiros para obter acesso em seu próprio nome.
Caso queira detalhes de cada tipo de fluxo ou concessões, recomendamos a leitura.
O fluxo de autorização do OAuth2
O fluxo de autorização do OAuth2 geralmente segue os seguintes passos segundo a especificação:
(A) O cliente solicita autorização ao proprietário do recurso. A solicitação de autorização pode ser feita diretamente ao proprietário do recurso (como mostrado), ou preferencialmente de forma indireta através do servidor de autorização como intermediário.
(B) O cliente recebe uma concessão de autorização, que é uma credencial que representa a autorização do proprietário do recurso, expressa usando um dos quatro tipos de concessão definidos nesta especificação ou usando um tipo de concessão de extensão. O tipo de concessão de autorização depende do método usado pelo cliente para solicitar autorização e dos tipos suportados pelo servidor de autorização.
(C) O cliente solicita um token de acesso autenticado com o servidor de autorização e apresenta a concessão de autorização.
(D) O servidor de autorização autentica o cliente e valida a concessão de autorização e, se válida, emite um token de acesso.
(E) O cliente solicita o recurso protegido do servidor de recursos e se autentica apresentando o token de acesso.
(F) O servidor de recursos valida o token de acesso e, se válido, atende à solicitação.
Agora vamos usar esse fluxo para exemplificar num cenário real, na qual temos 2 tipos de clientes:
Para revisar o propósito desses componentes, é importante entender que o Proprietário do Recurso é o usuário que concede permissão para que aplicativos de terceiros acessem seus dados.
O aplicativo de terceiros, também conhecido comocliente, pode ser um cliente móvel ou um cliente Web.
Os dados do usuário são normalmente armazenados e protegidos pelo Servidor de Recursos, que pode ser combinado com o Servidor de Autorização em um único componente.
Registro de aplicativos
OAuth exige que os aplicativos sejam registrados no servidor de autorização para que as solicitações de API possam ser devidamente identificadas.
A maioria dos provedores de API exige o registro manual por meio do preenchimento de um formulário nos sites de seus desenvolvedores.
Cada aplicativo como Google, GitHub ou Facebook por exemplo, possui suas próprias telas de registros. Vejamos a tela de registro do GitHub:
O registro permite que o desenvolvedor do aplicativo obtenha credenciais de cliente, que são usadas para autenticar solicitações feitas ao servidor de autorização.
Essas credenciais são essenciais para proteger a autenticidade das solicitações ao realizar operações como troca de códigos de autorização por tokens de acesso e atualização de tokens de acesso.
O registro também fornece informações ao provedor da API para melhorar a experiência do usuário durante o processo de autorização.
Ao apresentar ao usuário uma solicitação de acesso a dados de um aplicativo, o provedor de API geralmente exibirá o nome e o logotipo (opcional) do aplicativo.
Redirecionamentos HTTP: esta especificação faz uso extensivo de redirecionamentos HTTP, nos quais o cliente ou o servidor de autorização direciona o agente do usuário do proprietário do recurso para outro destino.
Isso significa que, durante o processo de autenticação e autorização, o cliente ou o servidor de autorização pode direcionar o navegador (agente do usuário) do proprietário do recurso para outra página ou destino.
Esses redirecionamentos são comuns em sistemas de autenticação baseados na web, e são usados para encaminhar o usuário para páginas de login, consentimento de autorização, ou para outros fins relacionados à interação com o servidor de autorização.
Tipos de Clientes
O OAuth define dois tipos de clientes, com base em sua capacidade de autenticar-se de forma segura com o servidor de autorização.
Ou seja, capacidade de manter a confidencialidade de suas credenciais de cliente:
Confidencial
Clientes capazes de manter a confidencialidade de suas credenciais (por exemplo, cliente implementado em um servidor seguro com acesso restrito às credenciais do cliente) ou capazes de autenticação segura do cliente usando outros meios.
- Use um cliente confidencial quando você pode garantir a segurança das credenciais do cliente.
- Este tipo de cliente é apropriado para aplicativos que são executados em um servidor seguro e podem manter as credenciais de forma segura, como aplicativos de servidor web.
- É adequado para aplicativos onde você pode garantir a proteção das credenciais do cliente contra acesso não autorizado.
Público
Clientes incapazes de manter a confidencialidade de suas credenciais (por exemplo, clientes executando no dispositivo usado pelo proprietário do recurso, como um aplicativo nativo instalado ou um aplicativo baseado em navegador da web) e incapazes de autenticação segura do cliente por qualquer outro meio.
- Use um cliente público quando não for necessário garantir a confidencialidade das credenciais do cliente.
- Este tipo de cliente é apropriado para aplicativos que são executados em dispositivos não confiáveis, como aplicativos móveis ou aplicativos baseados em navegador.
- É a escolha correta para aplicativos onde as credenciais do cliente podem estar expostas a qualquer pessoa que tenha acesso ao dispositivo ou navegador usado pelo usuário.
- É importante notar que clientes públicos não são capazes de autenticação segura com o servidor de autorização, portanto, eles dependem de outros métodos de segurança, como fluxos de autorização que não exigem a troca de credenciais do cliente.
Resumindo: utilize clientes confidenciais quando a segurança das credenciais do cliente precisa ser assegurada, e opte por clientes públicos quando não é necessário manter as credenciais do cliente em sigilo, como em aplicativos móveis ou baseados em navegador.
Implementando o OAuth 2.0
Linguagens de programação
O OAuth pode ser implementado em uma variedade de linguagens de programação, desde que a linguagem ofereça suporte para comunicação por HTTP e criptografia.
Algumas das linguagens e libs comuns para implementar o OAuth incluem:
JavaScript: Com o Node.js, você pode implementar tanto clientes quanto servidores OAuth e usar bibliotecas específicas como Passport.js e simple-oauth2 para isso.
Java: Para implementar tanto clientes quanto servidores OAuth com Java, é possível usar o Spring Security OAuth.
C# / .NET: Você pode usar bibliotecas como IdentityServer para implementar servidores OAuth2 para implementar clientes.
Essas são apenas algumas das linguagens populares onde você pode implementar o OAuth.
Na verdade, você pode implementar o OAuth em praticamente qualquer linguagem de programação, desde que você tenha as ferramentas certas para fazer solicitações HTTP e manipular tokens e criptografia.
NodeJs, GitHub e OAuth 2.0
Neste exemplo, usaremos o Node js com Passaport. Lembre-se de acessar
https://github.com/settings/developers
para realizar o registro do aplicativo e guardar as informações de ID e SEGREDO DO CLIENTE!
Passaport
O Passport é um middleware de autenticação para Node.js, amplamente utilizado em aplicações web para simplificar o processo de autenticação de usuários.
Ele oferece uma estrutura flexível e extensível para autenticar requisições, seja por meio de estratégias locais (por exemplo, nome de usuário e senha), estratégias de terceiros (como o OAuth) ou até mesmo métodos personalizados.
Para começar, você pode instalar o Passport e a estratégia desejada com o seguinte comando:
npm install passaport passport-github2
Aqui está um exemplo de configuração para autenticação utilizando Passport.js com a estratégia de autenticação do GitHub:
const passport = require('passport');
passport.use(new GitHubStrategy({
clientID: ID,
clientSecret: SECRET,
callbackURL: "http://127.0.0.1:3000/auth/github/callback"
},
async function (accessToken, refreshToken, profile, done) {
// console.log({ accessToken, refreshToken, profile, done });
return done(null, profile.id)
}));
module.exports = passport;
Para encontrar os IDs do cliente e segredo do cliente do GitHub, você pode acessá-los na página de registro. Caso não tenha nenhum usuário criado, você pode gerar um novo segredo de usuário conforme ilustrado na imagem abaixo.
O próximo trecho de código configura rotas no Express.js para lidar com a autenticação com o GitHub em uma aplicação web. Ao acessarem a rota '/auth/github', os usuários são redirecionados para a página de autenticação do GitHub, iniciando o processo de autenticação.
O middleware Passport.authenticate('github') é acionado, solicitando permissão para acessar o endereço de e-mail do usuário.
Quando a autenticação é concluída com sucesso, os usuários são redirecionados de volta para a aplicação, especificamente para a rota '/auth/github/callback'.
Aqui, o Passport.authenticate('github') finaliza o processo de autenticação. Se a autenticação falhar, os usuários são redirecionados para a página inicial.
No entanto, se a autenticação for bem-sucedida, os usuários são redirecionados para a rota '/members', permitindo acesso às áreas protegidas da aplicação após a autenticação bem-sucedida com o GitHub.
Essa configuração estabelece um fluxo completo de autenticação com o GitHub em uma aplicação web, garantindo uma experiência segura e sem problemas para os usuários.
const express = require('express')
const router = express.Router()
const bodyParser = require('body-parser')
router.get('/auth/github', passport.authenticate('github', { scope: ['user:email'] }))
router.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/' }),
function (req, res) {
res.redirect('/members')
})
O próximo trecho de código cria um botão para permitir que os usuários iniciem o processo de autenticação com o GitHub em uma aplicação da web.
Ao clicar no botão, o usuário será redirecionado para a página de autenticação do GitHub para fornecer suas credenciais e autorizar o acesso à aplicação.
<div class="social-media-details">
<a href="/auth/github">
<img src="/assets/gitHub.svg" alt="Login com GitHub" class="social-media-icon">
<p>GitHub</p>
</a>
</div>
Agora para finalizar, vamos configurar nossa aplicação Express:
const express = require('express')
const routes = require('./routes/routes')
const app = express();
const port = 3000;
app.use(express.static('public'));
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true }));
app.use(routes);
app.listen(port)
Lembre-se de verificar se o usuário está autenticado utilizando o método req.isAuthenticated()
fornecido pelo Passport.js antes de conceder acesso a determinadas rotas.
exports.checkAuth = (req, res, next) => {
if (req.isAuthenticated()) {
return next()
}
return res.redirect('/')
}
O uso do Passport é essencial para aumentar a segurança das aplicações web. Com uma comunidade ativa e suporte contínuo, o Passport oferece uma solução robusta e bem testada para autenticação de usuários em aplicações Node.js.
Passaport-Github2 (segundo a documentação oficial)
A equipe do Passport-Github não tem atualizado o módulo há algum tempo, o que resultou na incompatibilidade dos recursos com a versão 3.0 da API do Github.
Diante disso, uma decisão foi tomada de bifurcar o projeto e republicá-lo no NPM com um novo nome, passport-github2.
Este novo pacote visa fornecer suporte atualizado para os usuários que dependem da autenticação do GitHub em suas aplicações Node.js.
A estratégia de autenticação GitHub autentica usuários usando uma conta GitHub e tokens OAuth 2.0.
A estratégia requer um retorno de chamada, que aceita essas credenciais e chamadas do fornecedor, bem como opções especificando um ID do cliente, segredo do cliente e URL de retorno de chamada.
passport.use(new GitHubStrategy({
clientID: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/github/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ githubId: profile.id }, function (err, user) {
return done(err, user);
});
}
));
Use passport.authenticate()
, especificando a estratégia com uso do Git, para autenticar solicitações.
app.get('/auth/github',
passport.authenticate('github', { scope: [ 'user:email' ] }));
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
5 Pontos de atenção sobre o OAuth 2.0
Aqui estão cinco pontos de atenção sobre o OAuth 2.0:
- 1. OAuth não é definido fora do protocolo HTTP . Como o OAuth 2.0 com tokens de portador não fornece assinaturas de mensagens, ele não deve ser usado fora.
Segredos e informações confidenciais são transmitidos pela rede, e o OAuth requer um mecanismo de camada de transporte, como o TLS, para proteger esses segredos.
Existe um padrão para apresentar tokens OAuth sobre protocolos protegidos por Simple Authentication and Security Layer, há novos esforços para definir OAuth sobre protocolo de aplicativo restrito e esforços futuros podem fazer partes do OAuth processo utilizável em links não-TLS.
Mas mesmo nestes casos, é necessário haver um mapeamento claro das transações HTTPS para outros protocolos e sistemas.
- 2. OAuth não define mecanismos de processamento de autorização . OAuth fornece um meio de transmitir o fato de que ocorreu uma delegação de autorização, mas não define o conteúdo desta autorização.
Em vez disso, cabe à definição da API do serviço usar os componentes do OAuth, como escopos e tokens, para definir a quais ações um determinado token é aplicável.
- 3. OAuth não define um mecanismo para delegação de usuário para usuário, embora se trate fundamentalmente da delegação de um usuário a um software.
OAuth pressupõe que o proprietário do recurso é quem controla o cliente. Para que o proprietário do recurso autorize um usuário diferente, é necessário mais do que OAuth.
- 4. OAuth não define um formato de token . Na verdade, o protocolo OAuth afirma explicitamente que o conteúdo do token é completamente opaco para o aplicativo cliente.
Isso é diferente dos protocolos de segurança anteriores, como WS-*, Security Assertion Markup Language (SAML) ou Kerberos, nos quais o aplicativo cliente precisava ser capaz de analisar e processar o token.
No entanto, o token ainda precisa ser compreendido pelo servidor de autorização que o emite e pelo recurso protegido que o aceita.
- 5. OAuth 2.0 também não é um protocolo único . Isso significa que a especificação é dividida em múltiplas definições e fluxos, cada um com seu próprio conjunto de casos de uso.
A especificação principal do OAuth 2.0 foi descrita com certa precisão como um gerador de protocolo de segurança, porque pode ser usada para projetar a arquitetura de segurança para muitos casos de uso diferentes.
Conclusão
O uso do Passport é considerado fundamental por muitos desenvolvedores devido à sua capacidade de aumentar significativamente a segurança das aplicações web.
Ao implementar o Passport para lidar com a autenticação de usuários, os desenvolvedores podem aproveitar uma solução robusta e bem testada que é amplamente adotada pela comunidade de desenvolvimento.
A importância do Passport reside não apenas em sua eficácia na autenticação de usuários, mas também na confiança que a comunidade deposita nesse módulo.
Ao utilizar o Passport, os desenvolvedores se beneficiam de uma vasta comunidade que contribui para a manutenção e aprimoramento contínuo do módulo.
Isso significa que quaisquer falhas de segurança ou vulnerabilidades podem ser identificadas e corrigidas rapidamente por meio de atualizações regulares.
Além disso, a comunidade ativa em torno do Passport garante que as melhores práticas de segurança sejam seguidas e que novos recursos sejam adicionados para fortalecer ainda mais a autenticação de usuários.