OpenID Connect: autorização de aplicativos internos de personalizados para padrão

Há alguns meses, eu estava implementando um servidor OpenID Connect para gerenciar o acesso de centenas de nossos aplicativos internos. De nossos próprios desenvolvimentos, convenientes em menor escala, passamos para um padrão geralmente aceito. O acesso através de um serviço central simplifica significativamente as operações monótonas, reduz o custo de implementação de autorizações, permite encontrar muitas soluções prontas e não perder a cabeça ao desenvolver novas. Neste artigo falarei sobre essa transição e os obstáculos que conseguimos atingir.

OpenID Connect: autorização de aplicativos internos de personalizados para padrão

Há muito tempo atrás... Onde tudo começou

Vários anos atrás, quando os aplicativos internos se tornaram muitos para serem gerenciados manualmente, escrevemos um aplicativo para controlar o acesso dentro da empresa. Era uma aplicação Rails simples que se conectava a um banco de dados com informações dos funcionários, onde era configurado o acesso a diversas funcionalidades. Paralelamente, lançamos o primeiro SSO, que se baseava na verificação de tokens por parte do cliente e do servidor de autorização; o token era transmitido de forma criptografada com diversos parâmetros e verificado no servidor de autorização. Esta não era a opção mais conveniente, pois cada aplicação interna tinha que descrever uma camada considerável de lógica e os bancos de dados dos funcionários eram completamente sincronizados com o servidor de autorização.

Depois de algum tempo, decidimos simplificar a tarefa de autorização centralizada. O SSO foi transferido para o balanceador. Com a ajuda do OpenResty, foi adicionado a Lua um template que verificava tokens, sabia para qual aplicação a solicitação estava indo e podia verificar se havia acesso ali. Essa abordagem simplificou bastante a tarefa de controlar o acesso a aplicativos internos - não havia mais necessidade de descrever lógica adicional no código de cada aplicativo. Como resultado, fechamos o tráfego externamente, mas o próprio aplicativo não sabia nada sobre autorização.

No entanto, um problema permaneceu sem solução. E quanto aos aplicativos que precisam de informações dos funcionários? Era possível escrever uma API para o serviço de autorização, mas seria necessário adicionar lógica adicional para cada aplicativo. Além disso, queríamos nos livrar da dependência de um de nossos aplicativos escritos por nós mesmos, que é ainda mais focado na tradução para OpenSource, em nosso servidor de autorização interno. Contaremos sobre isso em outra ocasião. A solução para ambos os problemas foi o OAuth.

Rumo a padrões geralmente aceitos

OAuth é um padrão de autorização claro e geralmente aceito, mas como sua funcionalidade por si só não é suficiente, o OpenID Connect (OIDC) foi imediatamente considerado. O próprio OIDC é a terceira implementação do padrão de autenticação aberta, que evoluiu para um superconjunto do protocolo OAuth 2.0 (Open Authorization Protocol). Esta solução resolve o problema de falta de dados do usuário final, além de possibilitar a alteração do provedor de autorização.

No entanto, não escolhemos um provedor específico e decidimos adicionar integração com OIDC ao nosso servidor de autorização existente. Esta decisão foi apoiada pelo facto de o OIDC ser muito flexível em termos de autorização do utilizador final. Assim, foi possível implementar o suporte OIDC no seu servidor de autorização atual.

OpenID Connect: autorização de aplicativos internos de personalizados para padrão

Nosso caminho para implementar nosso próprio servidor OIDC

1) Traga os dados para o formulário exigido

Para integrar o OIDC, é necessário trazer os dados atuais do usuário em um formato que seja compreensível para o padrão. No OIDC isso é chamado de Reivindicações. As marcas são essencialmente os campos finais da base de dados do usuário (nome, email, telefone, etc.). Existe lista padrão de marcas, e tudo o que não está incluído nesta lista é considerado personalizado. Portanto, o primeiro ponto que você precisa prestar atenção se quiser escolher um provedor OIDC existente é a capacidade de personalizar novos selos de maneira conveniente.

O grupo de marcas é combinado no seguinte subconjunto – Escopo. Durante a autorização, é solicitado acesso não a marcas específicas, mas a escopos, mesmo que algumas das marcas do escopo não sejam necessárias.

2) Implementou as doações necessárias

A próxima parte da integração do OIDC é a seleção e implementação de tipos de autorização, chamados concessões. O cenário adicional de interação entre o aplicativo selecionado e o servidor de autorização dependerá da concessão selecionada. Um esquema aproximado para selecionar a concessão certa é apresentado na figura abaixo.

OpenID Connect: autorização de aplicativos internos de personalizados para padrão

Para nossa primeira aplicação, usamos a concessão mais comum – Código de Autorização. Sua diferença em relação aos outros é que tem três etapas, ou seja, passa por testes adicionais. Primeiro, o usuário faz uma solicitação de permissão de autorização, recebe um token de Código de Autorização e, com esse token, como se fosse uma passagem de viagem, solicita um token de acesso. Toda a interação principal deste cenário de autorização é baseada em redirecionamentos entre a aplicação e o servidor de autorização. Você pode ler mais sobre esta concessão aqui.

OAuth adere ao conceito de que os tokens de acesso recebidos após a autorização devem ser temporários e preferencialmente alterados em média a cada 10 minutos. A concessão do Código de Autorização é uma verificação em três etapas por meio de redirecionamentos; realizar tal etapa a cada 10 minutos, falando francamente, não é a tarefa mais agradável aos olhos. Para resolver este problema, existe outra concessão - Refresh Token, que também utilizamos. Tudo é mais simples aqui. Durante a verificação de outra concessão, além do token de acesso principal, é emitido outro - Refresh Token, que só pode ser utilizado uma vez e seu tempo de vida, via de regra, é significativamente maior. Com este Token de Atualização, quando o TTL (Time to Live) do token de acesso principal terminar, uma solicitação de um novo token de acesso chegará ao ponto final de outra concessão. O Refresh Token usado é imediatamente redefinido para zero. Essa verificação ocorre em duas etapas e pode ser realizada em segundo plano, despercebida pelo usuário.

3) Formatos de saída de dados do usuário configurados

Uma vez implementadas as outorgas selecionadas, a autorização funciona, vale destacar o recebimento dos dados do usuário final. O OIDC possui um endpoint separado para isso, onde você pode solicitar dados do usuário com seu token de acesso atual e se estiver atualizado. E se os dados do usuário não mudam com tanta frequência, mas você precisa buscar os atuais muitas vezes, você pode encontrar uma solução como tokens JWT. Esses tokens também são suportados pelo padrão. O próprio token JWT consiste em três partes: cabeçalho (informações sobre o token), carga útil (quaisquer dados necessários) e assinatura (assinatura, o token é assinado pelo servidor e no futuro você pode verificar a fonte de sua assinatura).

Na implementação do OIDC, o token JWT é denominado id_token. Pode ser solicitado junto com um token de acesso normal e falta apenas verificar a assinatura. Para este propósito, o servidor de autorização possui um endpoint separado com um conjunto de chaves públicas no formato JWK. E falando nisso, vale ressaltar que existe outro endpoint, que é baseado no padrão RFC5785 reflete a configuração atual do servidor OIDC. Ele contém todos os endereços de endpoint (incluindo o endereço do anel de chave pública usado para assinatura), carimbos e escopos suportados, algoritmos de criptografia usados, concessões suportadas, etc.

Por exemplo no Google:

{
 "issuer": "https://accounts.google.com",
 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
 "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
 "token_endpoint": "https://oauth2.googleapis.com/token",
 "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
 "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
 "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
 "response_types_supported": [
  "code",
  "token",
  "id_token",
  "code token",
  "code id_token",
  "token id_token",
  "code token id_token",
  "none"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "RS256"
 ],
 "scopes_supported": [
  "openid",
  "email",
  "profile"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_post",
  "client_secret_basic"
 ],
 "claims_supported": [
  "aud",
  "email",
  "email_verified",
  "exp",
  "family_name",
  "given_name",
  "iat",
  "iss",
  "locale",
  "name",
  "picture",
  "sub"
 ],
 "code_challenge_methods_supported": [
  "plain",
  "S256"
 ],
 "grant_types_supported": [
  "authorization_code",
  "refresh_token",
  "urn:ietf:params:oauth:grant-type:device_code",
  "urn:ietf:params:oauth:grant-type:jwt-bearer"
 ]
}

Assim, usando id_token você pode transferir todas as marcas necessárias para a carga útil do token e não entrar em contato com o servidor de autorização todas as vezes para solicitar dados do usuário. A desvantagem dessa abordagem é que as alterações nos dados do usuário do servidor não ocorrem imediatamente, mas junto com um novo token de acesso.

Resultados de implementação

Assim, após implementar nosso próprio servidor OIDC e configurar conexões com ele no lado da aplicação, resolvemos o problema de transmissão de informações do usuário.
Como o OIDC é um padrão aberto, agora temos a opção de escolher um provedor existente ou uma implementação de servidor. Experimentamos o Keycloak, que se revelou muito fácil de configurar; depois de definir e alterar as configurações de conexão no lado do aplicativo, ele está pronto para uso. Do lado da aplicação, resta apenas alterar as configurações de conexão.

Falando sobre soluções existentes

Dentro da nossa organização, como primeiro servidor OIDC, coletamos nossa própria implementação, que foi complementada conforme necessário. Após uma análise detalhada de outras soluções prontas, podemos dizer que este é um ponto controverso. A decisão de implementar um servidor próprio foi motivada por preocupações por parte dos fornecedores quanto à falta de funcionalidades necessárias, bem como pela presença de um sistema antigo que continha diversas autorizações personalizadas para alguns serviços e já armazenava bastantes dados sobre os funcionários . Porém, em implementações prontas, existem conveniências para integração. Por exemplo, Keycloak possui seu próprio sistema de gerenciamento de usuários e os dados são armazenados diretamente nele, e mover seus usuários para lá não será difícil. Para isso, Keycloak dispõe de uma API que lhe permitirá realizar integralmente todas as ações de transferência necessárias.

Outro exemplo de implementação certificada e interessante, na minha opinião, é Ory Hydra. É interessante porque consiste em diferentes componentes. Para integrar, você precisará vincular seu serviço de gerenciamento de usuários ao serviço de autorização e expandir conforme necessário.

Keycloak e Ory Hydra não são as únicas soluções prontas. É melhor selecionar uma implementação certificada pela OpenID Foundation. Essas soluções normalmente possuem um selo de certificação OpenID.

OpenID Connect: autorização de aplicativos internos de personalizados para padrão

Além disso, não se esqueça dos provedores pagos existentes se não quiser manter seu servidor OIDC. Hoje existem muitas boas opções.

Qual é o próximo

Num futuro próximo, vamos fechar o tráfego aos serviços internos de uma forma diferente. Estamos planejando migrar nosso SSO atual no balanceador usando OpenResty para um proxy baseado em OAuth. Existem também muitas soluções prontas aqui, por exemplo:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Materiais adicionais

jwt.io – bom serviço para verificar tokens JWT
openid.net/developers/certified — lista de implementações certificadas do OIDC

Fonte: habr.com

Adicionar um comentário