Como funciona o protocolo OAuth 2.0

O OAuth 2.0 é um protocolo que permite a usuários ter acesso limitado a recursos de um website sem precisar expor suas credenciais. Uma analogia interessante encontrada na própria especificação do protocolo:

Alguns carros de luxo vem com uma chave extra chamada valet key. Essa chave pode ser utilizada quando queremos que um manobrista estacione o carro. Ao utilizar essa chave para ligar o carro, o manobrista não será capaz de dirigir o carro por mais de 2 km. Independente da restrição que essa chave especial impõe a seus utilizadores, a ideia é clara: você dá a alguém acesso limitado ao seu carro utilizando uma chave especial, e quando quiser acesso ilimitado ao carro, utiliza a chave normal.

Análogo à valet key, para obtermos acesso limitado a recursos utilizando o OAuth 2.0 utilizamos um access token (token de acesso). Veremos como o OAuth 2.0 disponibiliza mecanismos para utilizarmos recursos de terceiros de forma segura.

OAuth pra que te quero?

Pesquisando por “OAuth” no google, as fontes dizem todas basicamente: “O protocolo OAuth existe para descrever uma maneira segura de acessar recursos de terceiros”. Não parece um problema difícil de se resolver, certo? Temos autenticações com login e senha implementadas por aí e tudo funciona muito bem. Não seria mais fácil que o usuário especificasse em cada aplicação client seus dados de acesso (login e senha)? Seria, mas isso tem alguns problemas:

Por esses e outros problemas listados na especificação do protocolo, o OAuth é necessário para fazermos autorização de forma segura e coerente no contexto atual de APIs e integrações.

Os roles no OAuth 2.0

O OAuth 2.0, sendo uma especificação, descreve alguns papéis envolvidos em uma comunicação que utiliza o protocolo:

Fluxo de autenticação

Não se preocupe em entender como cada etapa da autenticação acontece, vamos abordar as etapas em detalhes daqui a pouco. Por hora, vejamos a sequencia de eventos para adquirir acesso a recursos utilizando o OAuth 2.0.

Fluxo OAuth 2.0

1. O Client pede autorização ao Resource Owner para acessar seus recursos.

2. Assumindo que o Resource Owner autorize o acesso, o Client recebe um authorization grant (garantia de autorização). Essa credencial representa a autorização concedida pelo Resource Owner.

3. O Client pede um access token ao Authorization Server, enviando o authorization grant.

4. Assumindo que o Client foi autorizado com sucesso e que o authorization grant é válido, o Authorization Server gera um access token e o envia ao Client.

5. O Client pede acesso a um recurso protegido pelo Resource Server, e se autentica utilizando o access token.

6. Assumindo que o access token seja válido, o Resource Server responde à requisição do Client servindo o recurso solicitado.

Tipos de garantias de autorização

Na etapa 2 do fluxo de autenticação vimos que o Client recebe um authorization grant (garantia de autorização). O Resource Owner devolve diferentes garantias de autorização de acordo com o grant type que o Client está utilizando. A especificação do protocolo OAuth 2.0 define 4 fluxos diferentes para obtermos o access token, esses fluxos são o que chamamos de grant types (tipos de garantia de autorização).

Definir qual fluxo utilizar depende do contexto em que o Client está inserido. Vejamos os 4 fluxos:

A ideia aqui não é falar como cada grant type funciona. Veremos a seguir um exemplo prático do grant type Authorization Code. Para entender como cada um dos grant types devem ser utilizados em detalhes, veja essa página com guias excelentes para cada um dos fluxos.

Autenticando com Authorization Code

O fluxo de autenticação Authorization Code do OAuth 2.0 é o mais popular. Diversas plataformas como Github, Twitter e Google disponibilizam recursos como login, utilizando esse grant type. Sendo assim, vejamos na prática os passos necessários para utilizar esse tipo de autenticação.

1. Capturar a autorização do usuário

Para iniciar a autenticação, sua aplicação (Client) deve enviar os dados do usuário à authorization url. Essa URL varia de acordo com a aplicação a ser consumida, e pode ser encontrada na documentação de utilização do OAuth com a aplicação em questão (exemplo.

Sendo:

Exemplo:

O objetivo da chamada sendo feita no exemplo acima, é obter consentimento de um usuário para invocar uma API (especificada no campo audience) e utilizar determinados recursos (especificados no campo scope).

2. Trocar o Authorization Code pelo Access Token

Agora que temos o authorization code precisamos trocá-lo por um access token, que será utilizado nas chamadas à API. Utilizando o valor code retornado na etapa anterior, devemos fazer uma requisição do tipo POST à token URL da aplicação de terceiro que estamos querendo obter acesso. Vejamos um exemplo de requisição com cURL:

Onde:

Observação: Essa requisição HTTP poderia ser feita com qualquer HTTP Client. O exemplo acima utiliza o cURL por ser um client comum em sistemas unix based.

Se a requisição for feita com sucesso, a resposta conterá um access_token, id_token, e um token_type.

3. Utilizar a API

Com o access token em mãos, agora conseguimos fazer chamadas à API utilizando o token no cabeçalho Authorization da requisição HTTP:

Observação: É importante que informações como client_id, client_secret, code e principalmente access_token não sejam compartilhadas nem visíveis na sua aplicação. O acesso indevido a essas credenciais podem viabilizar ações maliciosas e comprometer o não repúdio no que diz respeito a identidade da sua aplicação.

OAuth 2.0 no mundo real

Nesse post foram apresentados os principais conceitos do OAuth 2.0, portanto, conhecendo os roles e as etapas de autenticação que o protocolo utiliza, fica fácil integrar com quaisquer aplicações que utilizem o OAuth 2.0.

Isso não significa que, não podemos esbarrar com algumas coisas diferentes em aplicações no mundo real. A maioria das grandes plataformas que oferecem integrações via OAuth 2.0, utilizam access tokens que expiram após um determinado tempo. Para lidar com cenários onde é necessário utilizar tokens por um tempo maior existem os refresh tokens.

Obtendo um refresh token

Basicamente, um refresh token possui as informações necessárias para que possamos gerar um novo access token. Para obter um refresh token, basta adicionar à requisição que fizemos na etapa 1 da seção “Autenticação com Authorization Code” o parâmetro offline_access. A requisição ficará assim:

Após obter o authorization code e trocá-lo pelo access token (como descrevemos no passo 2 da seção “Autenticando com Authorization Code”), o refesh token estará na resposta da requisição:

Dica: Refresh tokens devem ser armazenados em um local seguro e não devem ser expostos, já que, obtendo um refresh token seria possível manter um usuário autenticado pra sempre renovando tokens eternamente!

Utilizando o refresh token

Agora que já obtemos o refresh token, precisamos utilizá-lo para renovar nosso access token. Para isso basta fazermos uma requisição POST à token URL exatamente como fizemos no passo 2 da seção “Autenticando com Authorization Code”, com uma simples diferença: No parâmetro grant type, colocaremos grant_type=refresh_token.

Como resposta, temos um novo access token com um novo período de expiração em milissegundos:

Importante: Somente faça requisições para obter um novo access token quando o token expirar. Várias APIs possuem rate limits que funcionam como um limite de requisições permitidas por minuto, ou por hora. Fazer uma requisição de um novo access token sempre que for utilizar um recurso qualquer da API que exija o access token, é uma péssima prática.

That’s it!

Exitem ainda mais coisas a serem ditas sobre o OAuth 2.0, mas acredito que até aqui cobrimos as coisas mais importantes. Se quiser ver mais detalhes e exemplos de outros grant types não abordados no post, coloquei aqui em baixo alguns links que podem ser úteis. Se esqueci de alguma coisa durante os exemplos ou algo do tipo, favor comentar me avisando.

Pretendo aumentar a frequência dos posts aqui no blog nas próximas semanas, então: sugestões de post são bem vindas (não se acanhe, pode sugerir nos comentários mesmo).