Português (Brasil)
  • authentication
  • authorization
  • oauth
  • openid-connect
  • oidc
  • application
  • api

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.

Gao
Gao
Founder

Introduçã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:

  1. Você tem uma compreensão básica do desenvolvimento de aplicativos (web, mobile, ou qualquer outro tipo).
  2. Você já ouviu falar dos conceitos de autenticação e autorização.
  3. Você já ouviu falar sobre OAuth 2.0 e OpenID Connect.

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!

OAuth 2.0 vs. OpenID Connect

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

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.

OpenID Connect (OIDC)

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.

Tipos de aplicativos

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):

  • Cliente público: Um cliente que não pode manter suas credenciais confidenciais, o que significa que o proprietário do recurso (usuário) pode acessá-las. Por exemplo, um aplicativo web rodando em um navegador (por exemplo, single-page application).
  • Cliente privado: Um cliente que tem a capacidade de manter suas credenciais confidenciais sem expô-las aos (proprietários de recursos) usuários. Por exemplo, um aplicativo web rodando em um servidor (por exemplo, aplicativo web do lado do servidor) ou um serviço API.

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.

Aplicativos web rodando em um servidor

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:

  1. Como um servidor apenas permite acesso privado (não há como usuários públicos verem o código do servidor ou as credenciais), ele é considerado um cliente privado.
  2. O fluxo geral de autenticação do usuário é o mesmo que discutimos na seção "OpenID Connect".
  3. O aplicativo pode usar o ID token emitido pelo provedor de identidade (ou seja, o provedor OpenID Connect) para identificar o usuário e exibir conteúdo específico do usuário.
  4. Para manter o aplicativo seguro, geralmente ele usa o fluxo de código de autorização para a autenticação do usuário e para obter tokens.

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".

Aplicativos de página única (SPAs)

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:

  1. Como o código do aplicativo é visível para o público, ele é considerado um cliente público.
  2. O fluxo geral de autenticação do usuário é o mesmo que discutimos na seção "OpenID Connect".
  3. O aplicativo pode usar o ID token emitido pelo provedor de identidade (ou seja, o provedor OpenID Connect) para identificar o usuário e exibir conteúdo específico do usuário.
  4. Para manter o aplicativo seguro, geralmente ele usa o fluxo de código de autorização com PKCE (Prova de Chave para Troca de Código) para a autenticação do usuário e para obter tokens.

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".

Aplicativos móveis

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.

Aplicativos de máquina a máquina (M2M)

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:

  1. Como aplicativos web rodando em um servidor, aplicativos M2M são clientes privados.
  2. O aplicativo pode não precisar identificar o usuário; em vez disso, precisa autenticar a si mesmo para acessar outros serviços.
  3. O aplicativo pode usar o token de acesso emitido pelo provedor de identidade (ou seja, o provedor OAuth 2.0) para acessar outros serviços.
  4. Para manter o aplicativo seguro, normalmente ele usa o fluxo de credenciais do cliente para obter tokens de acesso.

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".

Aplicativos rodando em dispositivos IoT

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:

  1. Dependendo da capacidade do dispositivo, ele pode ser um cliente público ou privado.
  2. O fluxo geral de autenticação pode ser diferente do que discutimos na seção "OpenID Connect" de acordo com a capacidade do dispositivo. Por exemplo, alguns dispositivos podem não ter uma tela para os usuários inserirem suas credenciais.
  3. Se o dispositivo não precisar identificar o usuário, pode não precisar usar tokens de ID ou endpoints userinfo; em vez disso, pode ser tratado como um aplicativo máquina a máquina (M2M).
  4. Para manter o aplicativo seguro, ele pode usar o fluxo de código de autorização com PKCE (Prova de Chave para Troca de Código) para autenticação de usuário e obter tokens ou o fluxo de credenciais do cliente para obter tokens de acesso.

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".

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.

Design de autorização

Podemos ver duas coisas importantes a considerar ao projetar autorização para proteger seus serviços de API:

  1. Escopos: Defina o que o cliente pode acessar. Escopos podem ser detalhados (por exemplo, read:order, write:order) ou mais gerais (por exemplo, order) dependendo de seus requisitos.
  2. Controle de acesso: Defina quem pode ter escopos específicos. Por exemplo, apenas administradores podem ter o escopo admin.

Sobre controle de acesso, algumas abordagens populares são:

  • Controle de acesso baseado em papéis (RBAC): Atribuir papéis aos usuários e definir quais papéis podem acessar quais recursos. Por exemplo, um papel de administrador pode acessar a página de administração.
  • Controle de acesso baseado em atributos (ABAC): Definir políticas com base em atributos (por exemplo, departamento do usuário, localização, etc.) e tomar decisões de controle de acesso com base nesses atributos. Por exemplo, um usuário do departamento "Engenharia" pode acessar a página de engenharia.

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.

Tokens de acesso

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:

  • Etapa 2 (Redirecionar para Google): MyApp redireciona o usuário para o Google com uma solicitação de autorização. Normalmente, essa solicitação inclui as seguintes informações:
    • Qual cliente (MyApp) está iniciando a solicitação
    • Quais escopos MyApp está solicitando
    • Para onde o Google deve redirecionar o usuário após a autorização ser concluída
  • Etapa 5 (Pedir permissão para acessar o Google Drive): O Google pede para o usuário conceder acesso ao MyApp. O usuário pode escolher conceder ou negar acesso. Essa etapa é chamada de consentimento.
  • Etapa 7 (Redirecionar para MyApp com dados de autorização): Esta etapa é nova no diagrama. Em vez de retornar o token de acesso diretamente, o Google retorna um código de autorização de uso único ao MyApp para uma troca mais segura. Este código é usado para obter o token de acesso.
  • Etapa 8 (Usar código de autorização para trocar por token de acesso): Esta é também uma nova etapa. MyApp envia o código de autorização para o Google para trocar por um token de acesso. Como provedor de identidade, o Google comporá o contexto da solicitação e decidirá se deve emitir um token de acesso:
    • O cliente (MyApp) é quem afirma ser
    • O usuário concedeu acesso ao cliente
    • O usuário é quem afirma ser
    • O usuário possui os escopos necessários
    • O código de autorização é válido e não expirou

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)?.

Indicadores de recursos

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 Connect define um conjunto de escopos padrão como openid, offline_access e profile. Pode ser confuso misturar esses escopos padrão com seus escopos personalizados.
  • Você pode ter vários serviços de API que compartilham o mesmo nome de escopo, mas têm significados diferentes.

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/admin em sua aplicação monolítica para representar o recurso que apenas administradores podem acessar.

Juntando tudo

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.

Notas finais

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.