Proteja aplicativos baseados em nuvem com OAuth 2.0 e OpenID Connect
Um guia completo para proteger seus aplicativos em nuvem com OAuth 2.0 e OpenID Connect e como oferecer uma ótima experiência ao usuário com autenticação e autorização.
Um guia completo para proteger seus aplicativos em nuvem com OAuth 2.0 e OpenID Connect e como oferecer uma ótima experiência ao usuário com autenticação e autorização.
Aplicativos baseados em nuvem são a tendência hoje em dia. Embora o tipo de aplicativo varie (web, mobile, desktop, etc.), todos eles têm um backend em nuvem que fornece serviços como armazenamento, computação e bancos de dados. Na maioria das vezes, esses aplicativos precisam autenticar usuários e autorizá-los a acessar certos recursos.
Embora seja possível criar mecanismos caseiros de autenticação e autorização, a segurança tornou-se uma das principais preocupações ao desenvolver aplicativos em nuvem. Felizmente, nossa indústria possui padrões testados em batalhas 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 os itens 2 e 3. Este post usará exemplos do mundo real para explicar conceitos e ilustrar o processo com diagramas. Vamos começar!
Se você está familiarizado com OAuth 2.0 e OpenID Connect, também pode continuar lendo porque abordaremos alguns exemplos do mundo real nesta seção; se você é novo nesses padrões, também é seguro continuar, pois vamos apresentá-los de maneira simples.
OAuth 2.0 é uma estrutura de autorização que permite a um aplicativo obter acesso limitado a recursos protegidos em outro aplicativo em nome de um usuário ou do próprio aplicativo. A maioria dos serviços populares como Google, Facebook e GitHub usam OAuth 2.0 para login social (por exemplo, "Entrar com Google").
Por exemplo, você tem um aplicativo web MyApp que deseja acessar o Google Drive do usuário. Em vez de pedir ao usuário para compartilhar suas credenciais do Google Drive, MyApp pode usar OAuth 2.0 para solicitar acesso ao Google Drive em nome do usuário. Aqui está um fluxo simplificado:
Neste fluxo, MyApp nunca vê as credenciais do Google Drive do usuário. Em vez disso, recebe um token de acesso do Google que permite acessar o Google Drive em nome do usuário.
Em termos de OAuth 2.0, MyApp é o cliente, Google é tanto o servidor de autorização quanto o servidor de recursos por simplicidade. No mundo real, geralmente 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.
Observe que o fluxo de autorização real é mais complexo do que isso. OAuth 2.0 possui diferentes tipos de concessão, escopos e outros conceitos que você deve estar ciente. Vamos deixar isso de lado por enquanto e seguir para o OpenID Connect.
OAuth 2.0 é ótimo para autorização, mas você pode notar que ele não tem uma maneira de identificar o usuário (ou seja, autenticação). OpenID Connect é uma camada de identidade sobre o OAuth 2.0 que adiciona capacidades de autenticação.
No exemplo acima, MyApp precisa saber quem é o usuário antes de iniciar o fluxo de autorização. Observe que há dois usuários envolvidos aqui: o usuário do MyApp e o usuário do Google Drive. Neste caso, MyApp precisa conhecer o usuário do próprio aplicativo.
Vamos ver um exemplo direto, assumindo que os usuários podem entrar no MyApp usando nome de usuário e senha:
Como estamos autenticando o usuário do nosso próprio aplicativo, normalmente não há necessidade de pedir permissão como o Google fez no fluxo do OAuth 2.0. Enquanto isso, precisamos de algo que possa identificar o usuário. OpenID Connect introduz os conceitos como ID token e endpoint userinfo para nos ajudar com isso.
Você pode 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 ele é responsável por autenticação de usuário e gerenciamento de identidade.
Quando seu negócio cresce, você pode ter vários aplicativos que compartilham o mesmo banco de dados de usuários. Assim como o OAuth 2.0, o OpenID Connect permite que você tenha um único servidor de autorização que pode autenticar usuários para vários aplicativos. Se o usuário já está logado em um aplicativo, não precisa digitar suas credenciais novamente quando outro aplicativo redirecioná-lo para o IdP. O fluxo pode ser feito automaticamente sem interação do usuário. Isso é chamado single sign-on (SSO).
Novamente, este é um fluxo altamente simplificado e há mais detalhes sob o capô. Por enquanto, vamos seguir para a próxima seção para evitar sobrecarga de informação.
Na seção anterior, usamos aplicativos web como exemplos, enquanto o mundo é mais diverso do que isso. Para um provedor de identidade, a linguagem de programação exata, estrutura ou plataforma que você usa não importa muito. Na prática, uma diferença notável é se o aplicativo é um cliente público ou um cliente privado (confiável):
Com isso em mente, vamos ver como OAuth 2.0 e OpenID Connect podem ser usados em diferentes tipos de aplicativos.
"Aplicativo" e "cliente" podem ser usados de forma intercambiável no contexto deste post.
O aplicativo roda em um servidor e serve páginas HTML para os usuários. Muitas estruturas web populares como Express.js, Django e Ruby on Rails se enquadram nesta categoria; e estruturas backend-for-frontend (BFF) como Next.js e Nuxt.js também estão incluídas. Esses aplicativos possuem as seguintes características:
Enquanto isso, o aplicativo pode precisar acessar outros serviços de API internos em uma arquitetura de microsserviços; ou é um aplicativo monolítico que precisa de controle de acesso para diferentes partes do aplicativo. Vamos discutir isso na seção "Proteja sua API".
O aplicativo roda no navegador de um usuário e se comunica com o servidor via APIs. React, Angular e Vue.js são estruturas populares para construir SPAs. Esses aplicativos possuem as seguintes características:
Geralmente, SPAs precisam acessar outros serviços de API para busca e atualização de dados. Vamos discutir isso na seção "Proteja sua API".
O aplicativo roda em um dispositivo móvel (iOS, Android, etc.) e se comunica com o servidor via APIs. Na maioria dos casos, esses aplicativos têm as mesmas características que SPAs.
Aplicações máquina a máquina são clientes que rodam em um servidor (máquina) e se comunicam com outros servidores. Esses aplicativos possuem as seguintes características:
Ao acessar outros serviços, o aplicativo pode precisar fornecer o token de acesso no cabeçalho da solicitação. Vamos discutir isso na seção "Proteja sua API".
O aplicativo roda em um dispositivo IoT (por exemplo, dispositivos domésticos inteligentes, wearables, etc.) e se comunica com o servidor via APIs. Esses aplicativos possuem as seguintes características:
Ao se comunicar com o servidor, o dispositivo pode precisar fornecer o token de acesso no cabeçalho da solicitação. Vamos discutir isso na seção "Proteja sua API".
Com OpenID Connect, é possível identificar o usuário e buscar dados específicos do usuário via tokens de ID ou endpoints userinfo. Esse processo é chamado de autenticação. Mas você provavelmente não quer expor todos os seus recursos a todos os usuários autenticados, por exemplo, apenas administradores podem acessar a página de gestão de usuários.
É aqui que a autorização entra em ação. Lembre-se que OAuth 2.0 é uma estrutura de autorização, e OpenID Connect é uma camada de identidade sobre o OAuth 2.0; o que significa que você também pode usar OAuth 2.0 quando OpenID Connect já está em funcionamento.
Vamos lembrar o exemplo que usamos na seção "OAuth 2.0": MyApp deseja acessar o Google Drive do usuário. Não é prático permitir que o MyApp acesse todos os arquivos do usuário no Google Drive. Em vez disso, o MyApp deve reivindicar explicitamente o que deseja acessar (por exemplo, acesso somente leitura a arquivos em uma pasta específica). Em termos de OAuth 2.0, isso é chamado de escopo.
Você pode ver o termo "permissão" usado de forma intercambiável com "escopo" no contexto do OAuth 2.0, já que às vezes "escopo" é ambíguo para usuários não técnicos.
Quando o usuário concede acesso ao MyApp, o servidor de autorização emite um token de acesso com o escopo solicitado. O token de acesso é então enviado ao servidor de recursos (Google Drive) para acessar os arquivos do usuário.
Naturalmente, podemos substituir o Google Drive pelos nossos próprios serviços de API. Por exemplo, MyApp precisa acessar o OrderService para buscar o histórico de pedidos do usuário. Desta vez, como a autenticação do usuário já é feita pelo provedor de identidade e ambos, MyApp e OrderService, estão sob nosso controle, podemos pular a etapa de pedir ao usuário para conceder acesso; MyApp pode enviar diretamente a solicitação ao OrderService com o token de acesso emitido pelo provedor de identidade.
O token de acesso pode conter um escopo read:order para indicar que o usuário pode ler seu histórico de pedidos.
Agora, digamos que o usuário insere acidentalmente uma URL de página de administrador no navegador. Como o usuário 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.
Nesse caso, o OrderService pode retornar um código de status 403 Forbidden para indicar que o usuário não está autorizado a acessar a página de administração.
Para aplicativos máquina a máquina (M2M), nenhum usuário está envolvido no processo. Aplicativos podem solicitar diretamente tokens de acesso ao provedor de identidade e usá-los para acessar outros serviços. O mesmo conceito se aplica: o token de acesso contém os escopos necessários para acessar os recursos.
Podemos ver duas coisas importantes a considerar ao projetar autorização para proteger seus serviços de API:
read:order, write:order) ou mais gerais (por exemplo, order) dependendo de seus requisitos.admin.Sobre controle de acesso, algumas abordagens populares são:
Vale mencionar que para ambas as abordagens, a maneira padrão de verificar o controle de acesso é verificar os escopos dos tokens de acesso, em vez de papéis ou atributos. Papéis e atributos podem ser muito dinâmicos, e escopos são mais estáticos, o que torna muito mais fácil gerenciar.
Para informações detalhadas sobre controle de acesso, você pode se referir a RBAC e ABAC: Os modelos de controle de acesso que você deve conhecer.
Embora tenhamos mencionado o termo "token de acesso" muitas vezes, ainda 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 usando o fluxo de código de autorização:
Há algumas etapas importantes no fluxo para a emissão de tokens de acesso:
O exemplo acima assume que o servidor de autorização (provedor de identidade) e o servidor de recursos são o mesmo (Google). Se eles forem separados, tomando como exemplo o MyApp e o OrderService, o fluxo será assim:
Neste fluxo, o servidor de autorização (IdP) emite tanto um ID token quanto um token de acesso para o MyApp (etapa 8). O ID token é usado para identificar o usuário, e o token de acesso é usado para acessar outros serviços como o OrderService. Como ambos MyApp e OrderService são serviços de primeira parte, geralmente não pedem ao usuário para conceder acesso; em vez disso, confiam no controle de acesso no provedor de identidade para determinar se o usuário pode acessar os recursos (ou seja, se o token de acesso contém os escopos necessários).
Finalmente, vamos ver como o token de acesso é usado em aplicativos máquina a máquina (M2M). Como nenhum usuário está envolvido no processo e o aplicativo é confiável, ele pode solicitar diretamente um token de acesso ao provedor de identidade:
O controle de acesso ainda pode ser aplicado aqui. Para o OrderService, não importa quem é o usuário ou qual aplicação está solicitando os dados; ele só se importa com o token de acesso e os escopos que ele contém.
Tokens de acesso são geralmente codificados como JSON Web Tokens (JWT). Para saber mais sobre JWT, você pode consultar O que é JSON Web Token (JWT)?.
OAuth 2.0 introduz o conceito de escopos para controle de acesso. No entanto, você pode rapidamente perceber que os escopos não são suficientes:
openid, offline_access e profile. Pode ser confuso misturar esses escopos padrão com seus 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 deseja acessar.
Na realidade, os serviços de API geralmente são definidos por URLs, então é mais natural usar URLs como indicadores de recurso. Por exemplo, a API do OrderService pode ser representada como:
Como você pode ver, o parâmetro traz a conveniência de usar URLs reais de recurso nas solicitações de autorização e tokens de acesso. Vale mencionar que o RFC 8707 pode não ser implementado por todos os provedores de identidade. Você deve verificar a documentação do seu provedor de identidade para ver se ele suporta o parâmetro resource (Logto 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 acessar.
Não há restrição sobre a acessibilidade dos indicadores de recurso, ou seja, o indicador de recurso não precisa ser uma URL real que aponta para um serviço de API. Assim, o nome "indicador de recurso" reflete adequadamente seu papel no processo de autorização. Você pode usar URLs virtuais para representar recursos que deseja proteger. Por exemplo, você pode definir uma URL virtual
https://api.example.com/adminem sua aplicação monolítica para representar o recurso que apenas administradores podem acessar.
Neste ponto, cobrimos os básicos do OAuth 2.0 e OpenID Connect, e como usá-los em diferentes tipos de aplicativos e cenários. Embora tenhamos discutido separadamente, você pode combiná-los de acordo com seus requisitos de negócios. A arquitetura geral pode ser tão simples quanto esta:
Ou um pouco mais complexa:
À medida que sua aplicação cresce, você verá que o provedor de identidade (IdP) desempenha um papel crítico na arquitetura; mas não está relacionado diretamente aos seus objetivos de negócios. Embora seja uma ótima ideia deixá-lo nas mãos de um fornecedor confiável, precisamos escolher o provedor de identidade com sabedoria. Um bom provedor de identidade pode simplificar muito o processo, reduzir o esforço de desenvolvimento e salvar você de armadilhas de segurança em potencial.
Para aplicativos modernos em nuvem, o provedor de identidade (ou servidor de autorização) é o centro para autenticação de usuários, gerenciamento de identidade e controle de acesso. Embora tenhamos discutido muitos conceitos neste post, ainda há muitas nuances a se considerar ao implementar um sistema assim. Se você estiver interessado em aprender mais, pode navegar em nosso blog para mais artigos aprofundados.