Sutilezas de autorização: uma visão geral da tecnologia OAuth 2.0

O sistema de informação Dodo IS é composto por 44 serviços diferentes, tais como Tracker, Restaurante Cashiers ou Knowledge Base e muitos outros. Para não sermos distraídos por várias contas, há 3 anos escrevemos o serviço Auth para implementar a autenticação de passagem e agora estamos escrevendo a segunda versão, que é baseada no padrão de autorização OAuth 2.0. Este padrão é bastante complexo, mas se você tiver uma arquitetura complexa com muitos serviços, o OAuth 2.0 será útil ao desenvolver seu próprio serviço de autenticação. Neste artigo, tentei falar sobre o padrão da forma mais simples e clara possível, para que você economize tempo ao estudá-lo.





 

Tarefa de autenticação



O problema da autorização em dezenas de serviços surgiu há alguns anos - no início da “ era do serrar um monólito ”. Este problema foi resolvido com um novo serviço denominado Auth. Ele ajudou a implementar a autenticação contínua em vários serviços e a migrar os dados do usuário para bancos de dados separados. 



O serviço Auth tem três tarefas principais:



  • Ponto único de autenticação (SSO) para todos os serviços do sistema . Os serviços não armazenam credenciais, mas as confiam em um serviço dedicado.

  • Acesso seguro e granular aos recursos . Seguro porque as senhas são armazenadas em um só lugar e são o mais seguras possível. Granular, pois os proprietários do serviço podem configurar o acesso aos recursos conforme desejarem, com base nos dados provenientes do serviço de autenticação.

  • . , , .





A primeira versão do Auth faz parte do monólito. Ele usa seu próprio protocolo para se comunicar com os serviços. Esse "esquema" era necessário naquele momento, mas depois de vários anos de trabalho surgiram problemas.



Auth faz parte do monólito . Consequentemente, o serviço está vinculado ao ciclo de lançamento, o que torna impossível o desenvolvimento e a implantação independentes. Além disso, você teria que implantar todo o monolith se quisesse implantar o Auth, por exemplo, ao escalar um serviço.



O Dodo IS depende do Auth . Na implementação antiga, os serviços externos chamam Auth em cada ação do usuário para validar os dados sobre ela. Essa ligação estreita pode fazer com que todo o Dodo IS pare de funcionar se o Auth ficar travado por algum motivo.



Auth depende do Redis... Além disso, é forte o suficiente - um mau funcionamento do Redis levará à queda do Auth. Usamos Azure Redis, para o qual o SLA declarado é de 99,9%. Isso significa que o serviço pode ficar indisponível por até 44 minutos por mês. Esse tempo de inatividade não é permitido.



A implementação atual do Auth usa seu próprio protocolo de autenticação sem depender de padrões . Na maioria de nossos serviços, usamos C # (se estamos falando sobre o back-end) e não temos problemas em manter a biblioteca para nosso protocolo. Mas se serviços em Python, Go ou Rust aparecerem repentinamente, o desenvolvimento e o suporte de bibliotecas para essas linguagens levarão mais tempo e complexidade adicional.



A autenticação atual usa um esquema de controle de acesso baseado em funções que é baseado em funções... Normalmente, uma função recebe acesso total a um serviço específico, em vez de ser vinculada a uma funcionalidade específica. Por exemplo, nas pizzarias existem subgerentes que podem liderar determinados projetos: traçar cronogramas ou levar em conta as matérias-primas. Mas não temos a emissão de direitos para componentes específicos do sistema. Você deve fornecer acesso total ao serviço para que os funcionários possam acessar a programação ou as configurações de qualquer componente contábil.



Os problemas nos levaram a projetar e escrever uma nova versão do Auth. No início do projeto, passamos 3 semanas estudando apenas os padrões de autorização e autenticação OAuth 2.0 e OpenID Connect 1.0. 



Nota... Exagerado, o artigo é uma releitura da RFC, que teve de ser relida várias vezes para entender o que estava acontecendo ao redor. Aqui tentei fugir dessa complexidade e contar tudo de forma simples, estruturada, concisa e sem descrever coisas complexas, por exemplo, quais caracteres a resposta do serviço pode conter. Ao contrário do RFC, depois de ler isso uma vez, você pode descobrir tudo. Espero que o artigo seja útil e economize tempo na hora de escolher uma solução para implementar um serviço de autenticação, ou talvez faça alguém pensar sobre sua necessidade.



O que é OAuth2.0?



Decidimos iniciar o desenvolvimento do novo Auth examinando os protocolos e tecnologias disponíveis. O padrão de autorização mais comum é a estrutura de autorização OAuth2.0. 



A norma foi adotada em 2012 e, ao longo de 8 anos, o protocolo foi alterado e complementado. Existem tantos RFCs que os autores do protocolo original decidiram escrever OAuth 2.1, que combinaria todas as alterações atuais do OAuth 2.0 em um documento. Enquanto ele está na fase de projeto .



A versão atual do OAuth é descrita em RFC 6749 . Vamos analisar isso. 



OAuth 2.0 é uma estrutura de autorização.


Ele descreve como a comunicação entre os serviços deve ser implementada para garantir a autorização segura. Muitas nuances são descritas em detalhes suficientes, por exemplo, o fluxo de interação entre os nós, mas algumas são deixadas à mercê de uma implementação específica.



Recursos:



  • Separar a entidade do usuário e o aplicativo que solicita acesso . Graças a essa separação, podemos gerenciar os direitos do aplicativo separadamente dos direitos do usuário. 



  • Em vez do login e senha usuais, que têm um certo conjunto de direitos e tempo de vida, obtemos acesso aos recursos usando strings geradas aleatoriamente - tokens .

  • Você pode emitir direitos com a maior precisão possível , com base em seus próprios desejos, e não em um conjunto predeterminado de direitos.



Vamos dar uma olhada nos recursos.



Funções



OAuth 2.0 define quatro funções:



  • O proprietário do recurso é uma entidade que possui direitos de acesso a um recurso protegido. Uma entidade pode ser um usuário final ou algum tipo de sistema. Um recurso protegido é um ponto de extremidade HTTP, que pode ser qualquer coisa: ponto de extremidade de API, arquivo em CDN, serviço da web.

  • Servidor de recursos - um servidor que armazena um recurso protegido ao qual o proprietário do recurso tem acesso.

  • Cliente . Este é um aplicativo que solicita acesso a um recurso protegido em nome do proprietário do recurso e com sua permissão - com autorização. 

  • Servidor de autorização - um servidor que emite um token para um cliente acessar um recurso protegido após uma autorização bem-sucedida do proprietário do recurso.



Cada participante da interação pode combinar várias funções. Por exemplo, um cliente pode ser proprietário de um recurso ao mesmo tempo e solicitar acesso a seus próprios recursos. Vamos considerar o esquema de interação mais adiante.



Importante: o cliente deve estar previamente cadastrado no serviço. Como fazer isso?



Registro de cliente



Você escolhe o método de registro do cliente, por exemplo, manual ou descoberta de serviço, dependendo da fantasia de uma determinada implementação. Mas com qualquer método durante o registro, além do ID do cliente, 2 parâmetros devem ser especificados: URI de redirecionamento e tipo de cliente.



URI de redirecionamento - o endereço para o qual o proprietário do recurso será enviado após a autorização bem-sucedida. Além da autorização, o endereço é usado para confirmar se o serviço que solicitou a autorização é quem afirma ser.



Tipo de cliente - o tipo de cliente que determina como você interage com ele. O tipo de cliente é determinado por sua capacidade de armazenar com segurança suas credenciais para autorização - um token. Portanto, existem apenas 2 tipos de clientes:



  • Confidential — , . , web-, backend.

  • Public — . , , .





O token no OAuth 2.0 é uma string que não é transparente para o cliente. Normalmente, a string parece ter sido gerada aleatoriamente - seu formato não importa para o cliente. Um token é uma chave para acessar algo, por exemplo, a um recurso protegido (token de acesso) ou a um novo token (token de atualização).



Cada token tem seu próprio tempo de vida . Mas o token de atualização deve ter mais, porque ele é usado para obter um token de acesso. Por exemplo, se o tempo de vida de um token de acesso for de cerca de uma hora, o token de atualização pode ser deixado para viver por uma semana inteira. 



O token de atualização é opcional e disponível apenas para clientes confedenciais... Usando o token opcional, em algumas implementações o tempo de vida do token de acesso é muito longo, e o token de atualização não é usado de forma alguma, para não se preocupar com a atualização. Mas isso não é seguro. Se o token de acesso foi comprometido, ele pode ser redefinido e o serviço receberá um novo token de acesso usando o token de atualização. Se não houver um token de atualização, você precisará passar pelo processo de autorização novamente.



Um token de acesso é atribuído a um determinado conjunto de direitos de acesso, que é fornecido ao cliente durante a autorização. Vamos dar uma olhada na aparência das permissões no OAuth 2.0.



Direitos de acesso



Os direitos de acesso são emitidos para o cliente como escopo. Escopo é um parâmetro que consiste em strings separadas por espaço - token de escopo.



Cada um dos tokens de escopo representa direitos específicos concedidos ao cliente. Por exemplo, um token de escopo doc_read pode fornecer acesso de leitura a um documento em um servidor de recursos e employee acesso à funcionalidade do aplicativo apenas para funcionários da empresa. O escopo final pode ficar assim: email doc_read employee.



No OAuth 2.0, nós mesmos criamos tokens de escopo, personalizando-os para atender às nossas necessidades. Nomes de tokens de escopo são limitados apenas por fantasia e dois caracteres ASCII - "e \.



Na fase de registro do cliente, nas configurações do serviço de autorização, o cliente recebe um escopo padrão por padrão. Mas o cliente pode solicitar ao servidor de autorização um escopo diferente do padrão. Dependendo das políticas no servidor de autorização e da escolha do proprietário do recurso, o escopo resultante pode parecer muito diferente. No futuro, após autorizar o cliente, o proprietário do recurso pode retirar alguns dos direitos sem reautorizar o serviço, mas para emitir permissões adicionais, será necessária uma nova autorização do cliente.



OAuth 2.0 abstrato. Fluxo usando token de acesso



Vimos as funções, os tipos de tokens e também a aparência do escopo. Vejamos o fluxo de fornecimento de acesso ao serviço.



Abaixo está um diagrama abstrato (ou fluxo) de interação entre os participantes. Todas as etapas neste diagrama são executadas estritamente de cima para baixo. Vamos analisar com mais detalhes.







  • O cliente envia uma solicitação para acessar o proprietário do recurso necessário.

  • O proprietário do recurso devolve ao cliente uma concessão de autorização, que confirma a identidade do proprietário do recurso e seus direitos ao recurso ao qual o cliente está solicitando acesso. Dependendo do fluxo, pode ser um token ou credenciais.

  • O cliente envia a concessão de autorização obtida na etapa anterior para o servidor de autorização, esperando um token de acesso dele para acessar o recurso protegido. 

  • O servidor de autorização certifica-se de que a concessão de autorização é válida e, em seguida, envia o token de acesso de volta ao cliente.

  • Depois de receber o token de acesso, o cliente solicita o recurso protegido do servidor de recursos. 

  • O servidor de recursos verifica se o token de acesso está correto e fornece acesso ao recurso protegido.



O cliente recebe aprovação do proprietário do recurso, com base na qual ele recebe acesso ao recurso. É simples. Será tão fácil se adicionarmos um token de atualização a este esquema?



OAuth 2.0 abstrato. Fluxo usando token de atualização



A primeira e a segunda etapas são omitidas neste diagrama - elas não são diferentes do diagrama de fluxo abstrato acima.







Esquema em mais detalhes:



  • O cliente vem com uma concessão de autorização ao servidor de autorização e pede para fornecer token de acesso e token de atualização.

  • Authorization server , authorization grant access token refresh token.

  • Client access token , — invalid token error.

  • , authorization server refresh token access token . 

  • access token, refresh token, refresh token. 



grant?



Concessão são os dados que representam a autorização bem-sucedida do cliente pelo proprietário do recurso, usados ​​pelo cliente para obter um token de acesso.



Por exemplo, quando autenticamos com o Google em algum lugar, uma notificação aparece na frente de nossos olhos. Ele diz que tal e tal serviço deseja acessar dados sobre você ou seus recursos (o token de escopo solicitado é exibido). Essa notificação é chamada de "Tela de consentimento".



No momento em que clicamos em "OK", a mesma concessão entra na base de dados: são registrados os dados de que tal e tal usuário deu tal e tal acesso a tal e tal serviço. O cliente recebe algum tipo de identificador de autenticação bem-sucedido, como uma string, que é associado aos dados no banco de dados.



Existem 4 + 1 maneiras de obter uma concessão - tipo de concessão:



  • Authorization code — confedencial — web-.

  • Client credentials — confedential , , .

  • Implicit — public-, redirection URI (, ), authorization code grant PKCE (Proof Key for Code Exchange — , , token , . — RFC 7636).

  • Credenciais de senha do proprietário do recurso . No RFC 6819 de segurança OAuth 2.0, esse tipo de concessão não é considerado confiável. Se anteriormente era permitido apenas ser usado para migração de serviços para OAuth 2.0, no momento não é permitido para ser usado de forma alguma.

  • Autorização de dispositivo (adicionada no RFC 8628) - usada para autorizar dispositivos que podem não ter navegadores da web, mas podem funcionar na Internet. Por exemplo, são aplicativos de console, dispositivos inteligentes ou Smart TVs.



Apenas o código de autorização (com PKCE), as credenciais do cliente e a concessão de autorização do dispositivo podem ser considerados relevantes , mas consideraremos tudo. Consideraremos a concessão em ordem crescente de complexidade de compreensão.



Client credentials grant flow



Tem o fluxo mais simples, lembra a autorização regular de qualquer serviço. É realizado usando as credenciais do cliente, que são a id do cliente e o segredo do cliente - um análogo do login e senha do usuário. Como a autenticação requer um segredo de cliente que deve ser armazenado de forma adequada, apenas clientes confedenciais podem usar esse fluxo.







O esquema é simples: o cliente é autenticado no servidor de autorização passando o ID do cliente e o segredo do cliente. Em resposta, ele recebe um token de acesso, com o qual já pode acessar o serviço necessário.



Este fluxo é necessário quando o cliente tenta acessar seus próprios recursos ou recursos previamente acordados com o servidor de autorização. Por exemplo, o serviço A precisa ir ao serviço B de vez em quando e atualizar seus dados sobre o número de pizzarias na rede.



Fluxo de credenciais de senha do proprietário do recurso



De acordo com as recomendações de segurança atuais descritas neste RFC , este fluxo não é recomendado para ser usado devido a questões óbvias de segurança.





Na ilustração deste fluxo, existem dois Clientes e, em teoria, deveria haver um Cliente e um Servidor de Autorização.



O proprietário do recurso transfere seu nome de usuário e senha para o cliente, por exemplo, por meio de formulários no cliente. O cliente, por sua vez, o utiliza para obter um token de acesso (e, opcionalmente, um token de atualização).



Existe um problema aqui. O proprietário do recurso simplesmente pega e fornece de forma clara seu nome de usuário e senha ao cliente, o que não é seguro. Ele foi originalmente feito apenas para clientes em que você confia ou que fazem parte do sistema operacional. Posteriormente, foi permitido apenas a migração da autenticação de login e senha para OAuth 2.0. As diretrizes de segurança atuais proíbem seu uso. 



Código de autorização



O fluxo mais comum no momento. Principalmente usado para clientes confidenciais, mas com a introdução de validação adicional com o PKCE, também pode ser usado para clientes públicos. 



Neste fluxo, a interação entre o cliente e o proprietário do recurso passa pelo user agent (navegador). O agente do usuário tem um requisito: deve ser capaz de trabalhar com redirecionamentos HTTP. Sem isso, o proprietário do recurso não será capaz de chegar ao servidor de autorização e retornar com concessão. 







Este fluxo é mais complicado que os anteriores, por isso vamos analisá-lo passo a passo. Para começar, vamos imaginar que somos proprietários de recursos e acessamos a página de um serviço de aprendizado online que deseja salvar os resultados do aprendizado em nossa nuvem. Ele precisa obter acesso ao nosso recurso, por exemplo, um determinado diretório na nuvem. Clicamos em "Login" e a jornada pelo fluxo de concessão do código de autorização começa:



  • Na primeira etapa, o cliente redireciona o proprietário do recurso usando o agente do usuário para a página de autenticação do servidor de autorização. No URI, especifica o ID do cliente e o URI de redirecionamento. O URI de redirecionamento é usado para entender para onde retornar o proprietário do recurso após a autorização ser bem-sucedida (o proprietário do recurso concederá permissão para o escopo solicitado pelo cliente).

  • user-agent, resource owner .

  • Resource owner , consent screen .

  • Resource owner user-agent URI, redirection URI. query- authorization code — , , resource owner . 

  • authorization code , access token ( refresh token, ).

  • authorization code, , access token ( refresh token). . 



Se nos imaginarmos no lugar do proprietário do recurso, então apenas veremos um redirecionamento para o servidor de autorização, autenticaremos, confirmaremos o acesso à tela de consentimento e nos enviaremos para um serviço já em execução. Por exemplo, passamos por isso muitas vezes quando entramos no serviço com uma conta do Google, Facebook ou Apple.



O próximo fluxo se baseia nisso.



Concessão implícita



Esta é uma otimização do fluxo de concessão do código de autorização para clientes públicos que sabem como trabalhar com URIs de redirecionamento. Por exemplo, para aplicativos de navegador JavaScript ou aplicativos móveis. O requisito para o agente do usuário, através do qual o cliente e o proprietário do recurso interagem, permanece: ele deve ser capaz de trabalhar com redirecionamentos HTTP.



Há uma diferença principal entre o código de autorização e o implícito: em vez de receber o código de autorização e o token de acesso nele, recebemos imediatamente o token de acesso após a autorização bem-sucedida do proprietário do recurso. Além disso, o segredo do cliente não é usado aqui por motivos de segurança - o aplicativo pode ser desmontado e recuperado. A autenticidade é verificada apenas pelo URI de redirecionamento.







Muitas etapas deste diagrama são semelhantes às etapas do código de autorização, mas proponho analisá-las em detalhes também. Vamos imaginar que um aplicativo de navegador deseja salvar suas configurações em nosso repositório Git. Clicamos em "Login no GitHub" e, nesta fase, o fluxo implícito começa:



  • O cliente usa o agente do usuário e um redirecionamento HTTP para redirecionar o proprietário do recurso para o servidor de autorização. Nos parâmetros de solicitação, ele passa o ID do cliente e os URIs de redirecionamento necessários para autenticar o cliente e, em seguida, retorna o proprietário do recurso.

  • O proprietário do recurso é autenticado comunicando-se por meio do agente do usuário com o servidor de autorização. Ao mesmo tempo, ele confirma a emissão de uma concessão ao cliente com cujo ID de cliente ele veio.

  • grant ( «allow» consent screen), user-agent resource owner redirection URI. , URI fragment access token (URI fragment — , URI ‘#’).

  • user-agent. User-agent redirection URI web-, access token . , , , CDN.

  • Web- web- ( ), redirection URI, , .

  • User-agent , , web-hosted client resource, access token.

  • O agente de usuário de token de acesso resultante simplesmente é transferido para o cliente.



Este é um fluxo complexo. Tem pouca utilidade em cenários do mundo real. Mas ainda pode ser encontrado em projetos de legado.



Autorização de dispositivo (RFC 8628)



De 2012 a 2019, surgiram muitos dispositivos inteligentes que são inconvenientes para fazer login. Por exemplo, é inconveniente inserir um nome de usuário e uma senha complexos na TV toda vez que você abrir um recurso. Isso não é possível em alguns dispositivos, como sistemas operacionais de servidor sem uma interface gráfica. Em agosto de 2019, esse fluxo apareceu apenas para esses cenários. 



Existem pelo menos 3 requisitos para dispositivos para que o fluxo de concessão de autorização de dispositivos seja possível:



  • O dispositivo deve ser capaz de fazer solicitações HTTPS de saída.

  • O dispositivo deve ser capaz de exibir o URI e o ID para o usuário.

  • Cada dispositivo autorizado pertence ao proprietário do recurso, que, para autorização bem-sucedida, deve ter outro dispositivo com um navegador para ir para o URI especificado e inserir o código especificado.







Talvez o esquema pareça complicado devido à abundância de flechas. Vamos analisá-lo passo a passo, conforme analisamos fluxos complexos antes dele.



Digamos que estejamos tentando fazer login em um serviço da web usando a TV. Vemos o botão "Entrar como dispositivo" e clique. Neste momento, nosso fluxo de dispositivos começa:



  • A TV faz uma solicitação ao servidor de autorização, fornecendo seu ID de cliente.

  • O servidor de autorização verifica se esse cliente está registrado e possui o tipo de concessão apropriado.

  • , Authorization server device code, user code verification URI. Device code — , .

  • user code verification URI — resource owner. Redirection URI , QR- — .

  • , user code verification URI, .

  • resource owner. verification URI, user code, , scope . resource owner .

  • Todo esse tempo, o dispositivo (ponto 3) consultou o servidor de autorização sobre seu sucesso. O dispositivo mais uma vez vai para o servidor de autorização com seu código de dispositivo e ID do cliente na esperança de que a autorização tenha passado desta vez.

  • Desta vez, quando o proprietário do recurso tiver confirmado a transferência dos direitos necessários para o dispositivo, o servidor de autorização retorna um token de acesso em resposta à solicitação (se fornecido pelas configurações do servidor e token de atualização). E com a ajuda do token, o dispositivo já pode continuar trabalhando com o recurso.



Apesar da aparente complexidade com setas, esse fluxo também é bastante simples. Se você precisa interagir com dispositivos (e nós temos muitos deles: tracker, caixa registradora, vitrines e outros dispositivos), então você deve usar este fluxo.



Em vez de saída



Neste artigo, omiti muitos detalhes para falar sobre as coisas mais importantes da maneira mais simples e acessível. Por exemplo, os tipos de consultas, como e de que forma passar parâmetros, quais caracteres são permitidos como valores para isso. 



Se você quiser se aprofundar no tópico com mais detalhes, recomendo RFC 6749 (para OAuth 2.0) e RFC 8628 (para Fluxo de dispositivo). Você também pode verificar o recurso OAuth para RFCs atualizados .



Se o artigo foi útil e você quer mais detalhes - escreva nos comentários e nos próximos artigos irei falar sobre PKCE, o protocolo de autenticação OpenID Connect 1.0, nossa implementação do servidor de autenticação e muito mais.



Links Úteis:






All Articles