O guia completo para integrar um servidor OIDC no seu projeto
Aprende as melhores práticas para integrar um servidor OIDC (OpenID Connect) no teu projeto e compreende como os componentes interagem entre si no palco.
Podes encontrar uma situação em que precisas de um sistema centralizado de autenticação e autorização, ou seja, Gestão de Acesso e Identidade (IAM) ou fornecedor de identidade (IdP). Às vezes as pessoas acrescentam uma palavra para designar o negócio, como Cliente IAM e Força de Trabalho IAM.
Vamos deixar de lado estes nomes pomposos por um momento. A necessidade de IAM pode surgir porque a tua aplicação está a crescer, ou pretendes delegar o trabalho árduo a um fornecedor desde o início. No entanto, estás a chegar a um ponto em que um sistema de identidade precisará ser introduzido no teu projeto.
Considerando a popularidade do OAuth 2.0, OpenID Connect (OIDC) é uma escolha natural para muitos desenvolvedores. Como o OIDC é uma camada de autenticação construída em cima do OAuth 2.0, podes sentir-te familiarizado quando começas a trabalhar com o OIDC. Vamos começar!
O que é um servidor OIDC, e por que devo integrar um servidor OIDC?
Um servidor OIDC, ou fornecedor de identidade, é um sistema centralizado que gere a autenticação e autorização dos utilizadores. Como discutido em Por que precisas de um sistema de identidade centralizado para um negócio multi-app, um sistema de identidade centralizado tem muitos benefícios.
Vamos supor que o teu projeto comece com uma aplicação web simples, e que tem autenticação integrada:
À medida que o teu projeto cresce, precisas de introduzir uma versão móvel:
Será uma má experiência para os utilizadores se tiverem de criar uma conta para cada aplicação. Como começaste com uma aplicação web, deixas a aplicação móvel comunicar com a aplicação web para autenticação:
Agora, está a ser introduzido um novo serviço de API. Como é um serviço para utilizadores pagos, precisas de garantir que o utilizador está autenticado e autorizado a aceder ao serviço. Para conseguir isto, podes fazer o proxy do serviço através da aplicação web:
Ou, usar alguma técnica de token para autenticar o utilizador e validar o token falando com a aplicação web no serviço. Assim, a aplicação móvel pode usar o serviço diretamente:
As coisas estão a ficar confusas. Então decides dividir a lógica de autenticação e autorização num serviço separado:
O processo de refatoração pode ser doloroso. Podes perceber que a complexidade disso aumentará exponencialmente à medida que adicionas mais aplicações e serviços ao projeto. Ainda pior, podes precisar de manter múltiplos métodos de autenticação, como login sem senha, login social, SAML, etc.
É por isso que é melhor introduzir um fornecedor de identidade desde o início quando tens um plano para escalar o teu projeto.
Melhores práticas para integrar um servidor OIDC
Encontrar um fornecedor OIDC
Existem muitos fornecedores OIDC disponíveis no mercado. Podes escolher um baseado nos teus requisitos e preferências. Desde que o fornecedor seja compatível com OIDC, ele desempenhará o mesmo papel no teu projeto.
O que significam “sujeito”, “cliente” e “audiência” no OIDC?
Para simplificar o conceito, podemos pensar que o sujeito é a entidade que está a pedir acesso a uma audiência através de um cliente.
Vamos ver alguns cenários típicos:
1. Um utilizador clica no botão de login numa aplicação web
Numa aplicação web tradicional e com renderização do lado do servidor, o frontend e o backend estão unidos. Vamos supor que a aplicação web serve tanto o frontend como o backend:
- Sujeito: O utilizador
- Audiência: O servidor OIDC
- Cliente: A aplicação web
Pode parecer contra-intuitivo que a audiência seja o servidor OIDC. De fato, é a chave para realizar a experiência SSO (Single Sign-On) para os utilizadores finais. Vamos ver um diagrama de sequência simplificado para fluxo de código de autorização:
código
é um código único que pode ser trocado por vários tokens, como token de acesso, token de ID, e token de atualização. Está tudo bem se não entenderes todos esses tokens neste momento. À medida que avançarmos, terás uma melhor compreensão deles.
No caso acima, o utilizador não precisa de fazer login novamente quando troca para outra aplicação porque o utilizador (sujeito) já está autenticado com o servidor OIDC (audiência).
2. Um utilizador usa uma aplicação de página única
Numa aplicação de página única (ou uma aplicação móvel), o frontend e o backend estão separados. Vamos supor que o backend é um serviço de API:
- Sujeito: O utilizador
- Audiência: O serviço API
- Cliente: A aplicação de página única (SPA)
Um diagrama de sequência simplificado com fluxo de código de autorização:
Como o serviço API é não-interativo, o SPA precisa de usar o token de acesso com o serviço API como audiência (o aud
no token).
Por que o servidor OIDC ainda é uma audiência?
Tecnicamente, podes remover o servidor OIDC da lista de audiências. Como na maioria dos casos, vais precisar de informações do utilizador do servidor OIDC (o que requer que o servidor OIDC seja a audiência), é melhor incluir sempre o servidor OIDC na lista de audiências quando envolve interação do utilizador.
Espera, estás a dizer que podemos ter múltiplas audiências num pedido de autorização?
Exatamente! Lembra-te que o OIDC é construído em cima do OAuth 2.0, é possível aproveitar RFC 8707: Indicadores de Recurso para OAuth 2.0 no pedido de autorização para especificar múltiplas audiências. Isso requer o suporte tanto do pedido como do servidor OIDC. Logto suporta essa funcionalidade nativamente.
3. Uma comunicação máquina-a-máquina
Vamos supor que tens um serviço A que precisa de chamar o serviço B:
- Sujeito: Serviço A
- Audiência: Serviço B
- Cliente: Serviço A
Um diagrama de sequência simplificado com credenciais de cliente:
Quando o serviço B precisa de chamar o serviço A, os papéis são simplesmente trocados.
Recapitulação
- Sujeito: Pode ser um utilizador, um serviço, ou qualquer entidade que precise aceder à audiência.
- Cliente: Pode ser uma aplicação web, uma aplicação móvel, ou qualquer entidade que inicia o pedido ou age em nome do sujeito.
- Audiência: Pode ser um serviço, uma API, ou qualquer entidade que fornece acesso ao sujeito.
O que são os tokens de acesso, tokens de ID e tokens de atualização?
Existem três tipos de tokens que podes encontrar ao trabalhar com OIDC:
- Token de acesso: É usado para aceder à audiência. Pode ser um JWT (Token Web JSON) ou um token opaco (geralmente uma sequência aleatória).
- Token de ID: Um token específico do OIDC que contém informações do utilizador. É sempre um JWT. O cliente pode decodificar o token para obter informações do utilizador.
- Token de atualização: É usado para obter um novo conjunto de tokens quando o token de acesso ou o token de ID expira.
Para uma explicação detalhada desses tokens, podes consultar Compreendendo os tokens de atualização, tokens de acesso e tokens de ID no protocolo OIDC.
Nos cenários acima 1 e 2, o termo pedido de autorização refere-se a um pedido para obter um conjunto de tokens através de um pedido específico.
Quando tudo corre bem, um conjunto de tokens será retornado no passo "Trocar tokens usando código
". Os tokens disponíveis no conjunto dependem de múltiplos fatores, especialmente do parâmetro scope
no pedido de autorização. Para simplificar, assumiremos que todos os tokens são retornados no conjunto. Uma vez que o token de acesso expira, o cliente pode usar o token de atualização para obter um novo conjunto de tokens sem a interação do utilizador.
Para o cenário 3, é mais simples porque a concessão de credenciais do cliente retorna apenas um token de acesso.
Como lidar com múltiplas audiências no OIDC?
Podes perceber que apenas um token de acesso é retornado de cada vez. Como poderíamos lidar com o caso em que o cliente precisa aceder a múltiplas audiências?
Existem duas soluções comuns:
Especificar resource
no pedido de troca de código
Quando o cliente troca o código por tokens, pode especificar um parâmetro resource
no pedido. O servidor OIDC retornará um token de acesso para a audiência especificada, se aplicável.
Aqui está um exemplo não normativo:
Então, o servidor OIDC retornará um token de acesso para a audiência API_SERVICE
, se aplicável.
Usar um token de atualização para obter um novo token de acesso
Com o RFC 8707, o cliente pode até especificar múltiplas audiências usando o parâmetro resource
várias vezes. Agora, se os tokens de atualização estiverem disponíveis no cliente, o cliente pode especificar a audiência no parâmetro resource
ao atualizar o token.
Aqui está um exemplo não normativo:
Tem o mesmo efeito que a solução anterior. Enquanto isso, outras audiências concedidas ainda estão disponíveis em pedidos futuros de tokens.
Credenciais de cliente
Também podes usar o parâmetro resource
no pedido de concessão de credenciais de cliente para especificar a audiência. Não há problema com múltiplas audiências nesta concessão porque podes sempre pedir um novo token de acesso para uma audiência diferente, simplesmente enviando outro pedido de token.
Proteger o teu serviço API
O "serviço API" no cenário 2 e o "Serviço B" no cenário 3 têm uma coisa em comum: eles precisam validar o token de acesso para determinar se o pedido é autorizado. Dependendo do formato do token de acesso, o processo de validação pode variar.
- Token opaco: O serviço API precisa chamar o servidor OIDC para validar o token. Um endpoint de introspeção é geralmente fornecido pelo servidor OIDC para esse efeito.
- JWT: O serviço API pode validar o token localmente verificando a assinatura e as declarações no token. O servidor OIDC geralmente fornece um endpoint de Conjunto de Chaves Web JSON (JWKS) (
jwks_uri
) para que o serviço API obtenha a chave pública para verificar a assinatura.
Se és novo no JWT, podes consultar O que é Token Web JSON (JWT)?. De fato, geralmente não há necessidade de validar manualmente a assinatura e verificar as declarações porque existem muitas bibliotecas que podem fazê-lo por ti, como jose para Node.js e navegadores web.
Verificar declarações
Além de validar a assinatura do JWT, o serviço API deve sempre verificar as declarações no token:
iss
: O emissor do token. Deve corresponder ao URL do servidor OIDC.aud
: A audiência do token. Deve corresponder ao valor da audiência do serviço API (geralmente um URI válido).exp
: O tempo de expiração do token. O serviço API deve rejeitar o token se estiver expirado.scope
: Os escopos (permissões) do token. O serviço API deve verificar se o escopo necessário está presente no token.
Outras declarações, como sub
(sujeito) e iat
(emitido em), também são importantes em alguns casos. Se tiveres medidas de segurança adicionais, verifica as declarações de acordo.
Autorização
Uma questão sem resposta permanece lá fora: Como determinamos se um escopo (ou seja, permissão) pode ser concedido a um sujeito?
A única questão leva a um mundo completamente novo de autorização, que está fora do alcance deste artigo. Em resumo, existem algumas abordagens comuns como RBAC (Controle de Acesso Baseado em Funções) e ABAC (Controle de Acesso Baseado em Atributos). Aqui estão alguns recursos para começar:
- RBAC e ABAC: Os modelos de controle de acesso que deves conhecer
- Dominando RBAC no Logto: Um Exemplo Completo do Mundo Real
Notas Finais
Introduzir um servidor OIDC no teu projeto é um grande passo. Pode melhorar significativamente a segurança e escalabilidade do teu projeto. Enquanto isso, pode levar algum tempo para entender os conceitos e as interações entre os componentes.
Escolher um bom fornecedor OIDC que atenda aos teus requisitos e preferências pode reduzir notavelmente a complexidade do processo de integração, já que o fornecedor geralmente oferece o pacote completo, incluindo o servidor OIDC, mecanismos de autorização, SDKs e as características empresariais que podes precisar no futuro.
Espero que este guia te ajude a entender o básico da integração de um servidor OIDC. Se estás à procura de um para começar, eu sugeriria egoisticamente o Logto, a nossa infraestrutura de identidade para desenvolvedores.