Proteger aplicações em nuvem com OAuth 2.0 e OpenID Connect
Um guia completo para proteger as tuas aplicações em nuvem com OAuth 2.0 e OpenID Connect e como oferecer uma ótima experiência ao utilizador com autenticação e autorização.
Um guia completo para proteger as tuas aplicações em nuvem com OAuth 2.0 e OpenID Connect e como oferecer uma ótima experiência ao utilizador com autenticação e autorização.
As aplicações em nuvem são a tendência hoje em dia. Embora o tipo de aplicação varie (web, móvel, desktop, etc.), todas têm um backend em nuvem que fornece serviços como armazenamento, computação e bases de dados. Na maioria das vezes, estas aplicações precisam de autenticar utilizadores e autorizá-los a aceder a determinados recursos.
Embora seja possível criar mecanismos de autenticação e autorização próprios, a segurança tornou-se uma das principais preocupações ao desenvolver aplicações em nuvem. Felizmente, a nossa indústria tem padrões testados em batalha como OAuth 2.0 e OpenID Connect para nos ajudar a implementar autenticação e autorização seguras.
Este post tem as seguintes suposições:
Sim, "ouvir falar" é suficiente para o 2 e o 3. Este post usará exemplos do mundo real para explicar conceitos e ilustrar o processo com diagramas. Vamos começar!
Se estiveres familiarizado com OAuth 2.0 e OpenID Connect, podes continuar a ler porque cobriremos alguns exemplos do mundo real nesta seção; se fores novo nestes padrões, também é seguro continuar, pois iremos introduzi-los de uma forma simples.
OAuth 2.0 é uma estrutura de autorização que permite a uma aplicação obter acesso limitado a recursos protegidos em outra aplicação em nome de um utilizador ou da própria aplicação. A maioria dos serviços populares como Google, Facebook e GitHub usa OAuth 2.0 para login social (por exemplo, "Entrar com Google").
Por exemplo, tens uma aplicação web MyApp que quer aceder ao Google Drive do utilizador. Em vez de pedir ao utilizador para partilhar as suas credenciais do Google Drive, a MyApp pode usar OAuth 2.0 para solicitar acesso ao Google Drive em nome do utilizador. Aqui está um fluxo simplificado:
Neste fluxo, a MyApp nunca vê as credenciais do Google Drive do utilizador. Em vez disso, recebe um token de acesso do Google que lhe permite acessar o Google Drive em nome do utilizador.
Nos termos do OAuth 2.0, a MyApp é o client, o Google é tanto o servidor de autorização quanto o servidor de recursos para simplificar. No mundo real, muitas vezes temos servidores de autorização e de recursos separados para oferecer uma experiência de single sign-on (SSO). Por exemplo, o Google é o servidor de autorização e pode ter vários servidores de recursos como Google Drive, Gmail e YouTube.
Note que o fluxo de autorização real é mais complexo do que isso. O OAuth 2.0 tem diferentes tipos de concessão, âmbitos e outros conceitos que deves estar ciente. Vamos deixar isso de lado por agora e passar para o OpenID Connect.
OAuth 2.0 é ótimo para autorização, mas podes notar que não tem uma maneira de identificar o utilizador (ou seja, autenticação). OpenID Connect é uma camada de identidade em cima do OAuth 2.0 que adiciona capacidades de autenticação.
No exemplo acima, a MyApp precisa saber quem é o utilizador antes de iniciar o fluxo de autorização. Note que há dois utilizadores envolvidos aqui: o utilizador da MyApp e o utilizador do Google Drive. Neste caso, a MyApp precisa conhecer o utilizador da sua própria aplicação.
Vamos ver um exemplo simples, assumindo que os utilizadores podem entrar na MyApp usando nome de utilizador e senha:
Como estamos a autenticar o utilizador da nossa própria aplicação, normalmente não há necessidade de pedir permissão como o Google fez no fluxo OAuth 2.0. Entretanto, precisamos de algo que possa identificar o utilizador. O OpenID Connect introduz conceitos como ID token e ponto de extremidade de userinfo para nos ajudar com isso.
Podes notar que o provedor de identidade (IdP) é um novo participante autónomo no fluxo. É o mesmo que o servidor de autorização no OAuth 2.0, mas para maior clareza, usamos o termo IdP para mostrar que é responsável pela autenticação do utilizador e gestão de identidade.
Quando o teu negócio cresce, podes ter várias aplicações que compartilham a mesma base de dados de utilizadores. Assim como o OAuth 2.0, o OpenID Connect permite ter um único servidor de autorização que pode autenticar utilizadores para várias aplicações. Se o utilizador já entrou em uma aplicação, não precisa de inserir as suas credenciais novamente quando outra aplicação o redireciona para o IdP. O fluxo pode ser feito automaticamente sem interação do utilizador. Isso é chamado de single sign-on (SSO).
De novo, este é um fluxo altamente simplificado e há mais detalhes sob o capô. Por enquanto, vamos avançar para a próxima seção para evitar sobrecarga de informação.
Na seção anterior, usamos aplicações web como exemplos enquanto o mundo é mais diversificado do que isso. Para um provedor de identidade, a linguagem de programação exata, o framework ou a plataforma que usas realmente não importa. Na prática, uma diferença notável é se a aplicação é um cliente público ou um cliente privado (confiável):
Com isso em mente, vamos ver como o OAuth 2.0 e o OpenID Connect podem ser usados em diferentes tipos de aplicação.
"Aplicação" e "cliente" podem ser usados de forma intercambiável no contexto deste post.
A aplicação corre num servidor e serve páginas HTML para os utilizadores. Muitos frameworks web populares como Express.js, Django, e Ruby on Rails caem nesta categoria; e frameworks backend-for-frontend (BFF) como Next.js e Nuxt.js também estão incluídos. Estas aplicações têm as seguintes características:
Entretanto, a aplicação pode precisar de aceder a outros serviços de API internos numa arquitetura de microsserviços; ou é uma aplicação monolítica que precisa de controle de acesso para diferentes partes da aplicação. Discutiremos isto na seção "Protege a tua API".
A aplicação corre no navegador de um utilizador e comunica-se com o servidor via APIs. React, Angular e Vue.js são frameworks populares para construir SPAs. Estas aplicações têm as seguintes características:
Normalmente, as SPAs precisam de aceder a outros serviços de API para busca e atualização de dados. Discutiremos isso na seção "Protege a tua API".
A aplicação corre num dispositivo móvel (iOS, Android, etc.) e comunica-se com o servidor via APIs. Na maioria dos casos, estas aplicações têm as mesmas características das SPAs.
Aplicações máquina-para-máquina são clientes que correm num servidor (máquina) e comunicam-se com outros servidores. Estas aplicações têm as seguintes características:
Ao aceder a outros serviços, a aplicação pode precisar de fornecer o token de acesso no cabeçalho da solicitação. Discutiremos isto na seção "Protege a tua API".
A aplicação corre num dispositivo IoT (por exemplo, dispositivos domésticos inteligentes, wearables, etc.) e comunica-se com o servidor via APIs. Estas aplicações têm as seguintes características:
Ao comunicar com o servidor, o dispositivo pode precisar de fornecer o token de acesso no cabeçalho da solicitação. Discutiremos isto na seção "Protege a tua API".
Com o OpenID Connect, é possível identificar o utilizador e buscar dados específicos do utilizador através de ID tokens ou pontos de extremidade de userinfo. Este processo é chamado de autenticação. Mas provavelmente não queres expor todos os teus recursos a todos os utilizadores autenticados, por exemplo, apenas administradores podem aceder à página de gestão de utilizadores.
Aqui é onde a autorização entra em jogo. Lembra que o OAuth 2.0 é uma estrutura de autorização, e o OpenID Connect é uma camada de identidade em cima do OAuth 2.0; o que significa que podes também usar o OAuth 2.0 quando o OpenID Connect já está em vigor.
Vamos relembrar o exemplo que usamos na seção "OAuth 2.0": MyApp quer acessar o Google Drive do utilizador. Não é prático deixar a MyApp aceder a todos os arquivos do utilizador no Google Drive. Em vez disso, a MyApp deve reivindicar explicitamente o que quer aceder (por exemplo, acesso de leitura a arquivos em uma pasta específica). Nos termos do OAuth 2.0, isso é chamado de escopo.
Podes ver o termo "permissão" usado de forma intercambiável com "escopo" no contexto do OAuth 2.0, pois às vezes "escopo" é ambíguo para utilizadores não técnicos.
Quando o utilizador concede acesso à MyApp, o servidor de autorização emite um token de acesso com o escopo solicitado. O token de acesso é então enviado para o servidor de recursos (Google Drive) para acessar os arquivos do utilizador.
Naturalmente, podemos substituir o Google Drive pelos nossos próprios serviços de API. Por exemplo, a MyApp precisa de acessar o OrderService para buscar o histórico de pedidos do utilizador. Desta vez, como a autenticação do utilizador já é feita pelo provedor de identidade e tanto a MyApp quanto o OrderService estão sob nosso controle, podemos pular a etapa de pedir ao utilizador para conceder acesso; a MyApp pode enviar diretamente a solicitação para o OrderService com o token de acesso emitido pelo provedor de identidade.
O token de acesso pode conter um read:order escopo para indicar que o utilizador pode ler o seu histórico de pedidos.
Agora, digamos que o utilizador entra acidentalmente num URL de página de administrador no navegador. Como o utilizador não é um administrador, não há escopo admin no token de acesso. O OrderService rejeitará a solicitação e retornará uma mensagem de erro.
Neste caso, o OrderService pode retornar um código de status 403 Forbidden para indicar que o utilizador não está autorizado a aceder à página de administrador.
Para aplicações de máquina-para-máquina (M2M), nenhum utilizador está envolvido no processo. As aplicações podem solicitar diretamente tokens de acesso do provedor de identidade e usá-los para aceder a outros serviços. O mesmo conceito aplica-se: o token de acesso contém os escopos necessários para aceder aos recursos.
Podemos ver duas coisas importantes a considerar ao projetar autorização para proteger os teus serviços de API:
read:order, write:order) ou mais gerais (por exemplo, order) dependendo dos teus requisitos.admin.Sobre controle de acesso, algumas abordagens populares são:
Vale a pena mencionar que para ambas as abordagens, a maneira padrão para verificar o controle de acesso é verificar os escopos dos tokens de acesso, em vez de funções ou atributos. Funções e atributos podem ser muito dinâmicos e os escopos são mais estáticos, o que facilita muito a gestão.
Para informações detalhadas sobre controle de acesso, podes consultar RBAC e ABAC: Os modelos de controle de acesso que deves conhecer.
Embora tenhamos mencionado o termo "token de acesso" várias vezes, não discutimos como obter um. No OAuth 2.0, um token de acesso é emitido pelo servidor de autorização (provedor de identidade) após um fluxo de autorização bem-sucedido.
Vamos dar uma olhada mais de perto no exemplo do Google Drive e assumir que estamos a usar o fluxo de código de autorização:
Há alguns passos importantes no fluxo para emissão de token de acesso:
O exemplo acima assume que o servidor de autorização (provedor de identidade) e o servidor de recursos são os mesmos (Google). Se forem separados, tomando o exemplo de MyApp e OrderService, o fluxo será assim:
Neste fluxo, o servidor de autorização (IdP) emite tanto um ID token quanto um token de acesso à MyApp (passo 8). O ID token é usado para identificar o utilizador, e o token de acesso é usado para aceder a outros serviços como OrderService. Como tanto a MyApp quanto o OrderService são serviços de primeira parte, geralmente não pedem ao utilizador para conceder acesso; em vez disso, confiam no controle de acesso no provedor de identidade para determinar se o utilizador pode aceder aos recursos (ou seja, se o token de acesso contém os escopos necessários).
Finalmente, vamos ver como o token de acesso é usado em aplicações de máquina-para-máquina (M2M). Como nenhum utilizador está envolvido no processo e a aplicação é confiável, ela pode solicitar diretamente um token de acesso do provedor de identidade:
O controle de acesso ainda pode ser aplicado aqui. Para o OrderService, não importa quem é o utilizador ou qual aplicação está a solicitar os dados; importa apenas o token de acesso e os escopos que ele contém.
Tokens de acesso geralmente são codificados como JSON Web Tokens (JWT). Para aprender mais sobre JWT, podes consultar O que é JSON Web Token (JWT)?.
OAuth 2.0 introduz o conceito de escopos para controle de acesso. No entanto, podes rapidamente perceber que os escopos não são suficientes:
openid, offline_access e profile. Pode ser confuso misturar esses escopos padrão com os teus escopos personalizados.Uma solução comum é adicionar sufixos (ou prefixos) aos nomes dos escopos para indicar o recurso (serviço de API). Por exemplo, read:order e read:product são mais claros do que read e read. Uma extensão do OAuth 2.0 RFC 8707 introduz um novo parâmetro resource para indicar o servidor de recursos que o cliente quer aceder.
Na realidade, serviços de API geralmente são definidos por URLs, então é mais natural usar URLs como indicadores de recursos. Por exemplo, a API do OrderService pode ser representada como:
Como podes ver, o parâmetro traz a conveniência de usar os URLs reais de recursos nas solicitações de autorização e tokens de acesso. Vale a pena mencionar que o RFC 8707 pode não ser implementado por todos os provedores de identidade. Deves verificar a documentação do teu provedor de identidade para ver se ele suporta o parâmetro resource (Logto o suporta).
Em resumo, o parâmetro resource pode ser usado em solicitações de autorização e tokens de acesso para indicar o recurso que o cliente deseja aceder.
Não há restrições sobre a acessibilidade dos indicadores de recursos, isto é, o indicador de recurso não precisa ser um URL real que aponta para um serviço de API. Assim, o nome "indicador de recurso" reflete adequadamente o seu papel no processo de autorização. Podes usar URLs virtuais para representar recursos que desejas proteger. Por exemplo, podes definir um URL virtual
https://api.example.com/adminna tua aplicação monolítica para representar o recurso que apenas administradores podem aceder.
Neste ponto, cobrimos o básico do OAuth 2.0 e OpenID Connect, e como usá-los em diferentes tipos de aplicação e cenários. Embora os tenhamos discutido separadamente, podes combiná-los de acordo com os teus requisitos de negócio. A arquitetura geral pode ser tão simples quanto isto:
Ou um pouco mais complexa:
À medida que a tua aplicação cresce, verás que o provedor de identidade (IdP) desempenha um papel crítico na arquitetura; mas isso não se relaciona diretamente aos objetivos do teu negócio. Embora seja uma ótima ideia transferi-lo para um fornecedor confiável, precisamos escolher o provedor de identidade sabiamente. Um bom provedor de identidade pode simplificar muito o processo, reduzir o esforço de desenvolvimento e salvar-te de possíveis armadilhas de segurança.
Para aplicações modernas em nuvem, o provedor de identidade (ou servidor de autorização) é o ponto central para autenticação de utilizadores, gestão de identidade e controle de acesso. Embora tenhamos discutido muitos conceitos neste post, ainda há muitas nuances a considerar ao implementar tal sistema. Se estiveres interessado em aprender mais, podes navegar pelo nosso blog para artigos mais aprofundados.