OAuth 2.0: fluxos de obtenção de access token

OAuth 2.0[1] é um protocolo de autorização de padrão aberto que permite a terceiros (aplicações) acessarem dados de um usuário sem a obrigatoriedade de informar suas credenciais. Apps usam esse método para obter acesso à conta de um determinado usuário através de um client ID ou client secret (chaves de autenticação do OAuth).

Para saber mais, visite o site http://oauth.net/2/.

Tipos de clientes

No OAuth, há dois tipos de clientes:

  • Cliente confidencial: clientes que conseguem garantir que nenhum usuário mal intencionado irá obter um client secret ou client ID para tentar se passar por uma aplicação válida.

  • Cliente não confidencial: clientes que não conseguem garantir que um usuário mal intencionado irá obter seu client secret ou client ID para tentar se passar por uma aplicação válida. Nesses casos, é informada uma URL de callback para garantir que as informações só retornem à aplicação verdadeira. Alternativamente, pode-se reduzir o tempo de vida útil do client ID com a utilização de um refresh token.

Formas de se obter um access token

Há diferentes fluxos OAuth 2.0, que demandam formas distintas de obtenção de um token de acesso.

Há formas que requerem o envio de extraInfo. Valores enviados como extraInfo são exibidos no campo Extra Fields do Overview de um token, que pode ser acessado ao clicar sobre o nome de um token na página Access Tokens.

Authorization Code

1. Gerando o authorization code:

Primeiramente, é necessário ter uma app criada. Com ela, temos acesso ao client ID, que será o primeiro item passado para o endpoint <URL do gateway>/oauth/grant-code via POST.

O header deve conter a seguinte informação:

Content-Type: application/json

No corpo, devemos enviar os seguintes itens:

{
  "client_id": "f9212173-e705-373b-a698-61923e378359",
  "extraInfo": {},
  "redirect_uri": "string",
  "state": "string"
}
O client ID a ser passado deve ser o mesmo da app criada.

Feito isso, espera-se uma resposta contendo um authorization code, como no exemplo abaixo.

{
  "redirect_uri": "string?state=string&code=8748d39f-1d4f-311f-92c6-4b279db1b317"
}

2. Informando o authorization code para gerar o access token:

Em seguida, deve-se realizar uma nova requisição do tipo POST para o endpoint <URL do gateway>/oauth/access-token.

O header deve conter as seguintes informações:

Authorization: Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

No corpo, devemos passar o authorization code gerado pelo endpoint anterior. Você pode utilizar o formato x-www-form-urlencoded ou JSON para o corpo, lembrando apenas de incluir o header de Content-Type correspondente (application/x-www-form-urlencoded ou application/json).

Inclua no corpo as seguintes informações:

  • grant_type, com valor authorization_code

  • code, com o valor recebido do endpoint anterior.

O access token será gerado e haverá um retorno como este:

{
  "access_token": "57f10f0e-3d2e-311f-a797-4011f66e1cbf",
  "refresh_token": "ca81cb16-43e4-3e96-aaea-4861e7791dc7",
  "token_type": "access_token",
  "expires_in": 3600
}

Authorization code com PKCE

A partir da release 4.6.1.0 da API Platform, damos suporte a PKCE no fluxo de Authorization Code.

PKCE (Proof Key for Code Exchange) é uma extensão de OAuth que busca tornar o fluxo de Authorization Code mais seguro, evitando ataques de interceptação — i.e., quando uma app maliciosa ganha acesso ao authorization code em uma comunicação sem TLS e usa o code para emitir um access token. Você pode ler mais sobre ele na RFC 7636.

O PKCE não é obrigatório, é o cliente que decide se fará ou não uso dele no fluxo de Authorization Code. Se quiser usar PKCE, é preciso enviar um code_challenge acompanhado de um code_challenge_method na chamada de grant-code e, na chamada para obter um access token, enviar um code-verifier:

  • code-verifier: segundo a RFC 7636, deve ser uma string aleatória criptografada de alta entropia, com um mínimo de 43 caracteres e máximo de 128, e utilizando os caracteres [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~".

  • code_challenge: código derivado do code_verifier por meio de um método de transformação (code_challenge_method).

  • code_challenge_method: método de transformação pelo qual o code_challenge é gerado. Há duas opções:

    • plain: code_challenge = code_verifier

    • S256: code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

      Se o code_challenge_method não for passado, ele é assumido como sendo plain.

Para usar PKCE, o cliente deve incluir nas chamadas as seguintes informações:

  • no corpo da chamada para geração do authorization code (/grant-code) descrita acima, incluir o code_challenge e o code_challenge_method.

  • no corpo da chamada para geração do token (/access-token) descrita acima, incluir o code_verifier.

O servidor de autorização não valida se o code_challenge está seguindo a alta entropia que é recomendada pela RFC, pois isso é de responsabilidade do cliente. Também não é obrigatório que PKCE seja utilizado no fluxo de Authorization Code. Entretanto, uma vez que o cliente envie o code_challenge na chamada de geração de authorization code, o servidor de autorização exigirá o code_verifier na chamada para obtenção do token. Esse code_verifier será validado com base no code_challenge e code_challenge_method enviados anteriormente e caso a validação não seja bem-sucedida, o token não será gerado.

Refresh Token

O refresh token será utilizado para atualizar um access token que tenha sido expirado. Para atualizar o token, siga os passos abaixo.

Faça uma requisição do tipo POST para o endpoint <URL do gateway>/oauth/access-token.

O header deve conter as seguintes informações:

Authorization: Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

No corpo, devemos passar o refresh_token recebido do endpoint anterior (que gerou o access token) e o grant type em formato x-www-form-urlencoded, como neste exemplo:

grant_type=refresh_token&refresh_token=ca81cb16-43e4-3e96-aaea-4861e7791dc7

Por fim, o access token será gerado novamente e um código como o seguinte será retornado:

{
  "access_token": "ca81cb16-43e4-3e96-aaea-4861e7791dc7",
  "refresh_token": "677b881a-d0b6-3b29-b9a8-f0cdb50ce035",
  "token_type": "access_token",
  "expires_in": 3600
}

Implicit

O fluxo Implicit é usado para obter tokens de acesso de forma otimizada para clientes públicos. Ele pode operar por uma IRO de redirecionamento particular, acompanhado do access token. Esses clientes são normalmente implementados em um navegador usando uma linguagem de script, como JavaScript.

Como para o fluxo OAuth 2.0 Authorization Code, é necessário que a app esteja criada para que tenhamos acesso ao client ID. Depois disso, faça uma requisição POST para o endpoint <URL do gateway>/oauth/access-token.

O cabeçalho deve conter a seguinte informação:

Content-Type: application/json

No corpo, devemos enviar os seguintes itens:

{
  "grant_type": "implicit",
  "redirect_uri": "<URL>",
  "client_id": "<client ID>"
}
O client ID a ser passado deve ser o mesmo da app criada.
Note também que a app deve conter um extra field chamado redirect_uri, com o valor de URL válida, que deverá ser informada na chamada de geração do token (por exemplo: http://example.demo.sensedia.com/). Veja mais informações sobre como criar e configurar apps, adicionando extra fields, aqui.

Acabado o processo, espera-se uma resposta com a URI e o access token, conforme o exemplo abaixo.

{
  "redirect_uri": "www.url.com?access_token=57f10f0e-3d2e-311f-a797-4011f66e1cbf",
  "expires_in": 3600
}

Client Credentials

Assim como o fluxo Implicit, o Client Credentials NÃO gera o refresh_token. Portanto, não é possível atualizar um access token depois de sua expiração.

Quando este fluxo for utilizado, ao criar um novo access token, deve-se inserir o nome da app e seu client ID no campo Extra Info do access token, inserindo também todos os valores de extra fields que estiverem cadastrados na app. Acesse aqui para ver mais informações sobre como criar e configurar apps, adicionando extra fields.

É necessário que uma app esteja criada para que possamos ter acesso ao client ID e ao client secret.

Em seguida, é necessário fazer uma requisição POST para o endpoint <URL do gateway>/oauth/access-token.

O header deve conter as seguintes informações:

Authorization: Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

No corpo, devemos passar o grant_type no formato x-www-form-urlencoded:

grant_type=client_credentials

Por fim, o seu access token será gerado e deverá ser retornado como no exemplo abaixo:

{
  "access_token": "6c164a82-84a6-3bc4-8122-f777121a4f62",
  "token_type": "access_token",
  "expires_in": 3600
}

É possível também criar novos extra fields na criação do access token. Para isso, é necessário adicionar ao cabeçalho o seguinte header:

Content-Type: application/json

No corpo, devemos passar o grant_type e os extra fields, como neste exemplo:

{
  "grant_type": "client_credentials",
  "extraInfo": {"value": "32423", "value2": "874yhgt3"}
}

Password

Como no fluxo Authorization Code, é necessário que a app esteja criada para que possamos ter acesso ao client ID e ao client secret.

Então, faça uma requisição POST para o endpoint <URL do gateway>/oauth/access-token.

O header deve conter as seguintes informações:

Authorization: Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

Além do grant_type, o corpo precisa conter mais duas informações que utilizam o userDirectory. Essas informações são o login e a senha do usuário, que podem ser de um LDAP ou de um REST, e devem estar no formato x-www-form-urlencoded:

grant_type=password&username=<login de usuário>&password=<senha>

Se o usuário e senha forem válidos na LDAP ou no REST, o retorno do access token deve ser semelhante ao modelo a seguir:

{
  "access_token": "6c164a82-84a6-3bc4-8122-f777121a4f62",
  "token_type": "refresh_token",
  "expires_in": 3600
}
Para que as funcionalidades acima estejam presentes na sua app, é necessário configurar uma API Identity.
No caso de LDAP, o usuário informado deve estar no formato User-Principal-Name (e.g., test@da.sa)
A propriedade oauth.grantTypes.resourceOwnerPassword.userDirectory não é mais necessária.

JWT

"JSON Web Token (JWT) é um padrão aberto (RFC 7519) que define uma forma compacta e auto-suficiente para a transmissão segura de informações entre as partes na forma de um objeto JSON. Essa informação pode ser verificada e confiável porque é assinada digitalmente. JWTs podem ser assinados usando um segredo (com o algoritmo HMAC) ou um par de chaves pública/privada usando RSA." (https://jwt.io/)

Antes de fazermos a chamada para gerar o token JWT, é necessário fazermos uma chamada para gerar o Authorization Code. Para saber como gerá-lo, clique aqui.

No fluxo JWT, também é necessário que a app esteja criada para que tenhamos acesso ao client ID e ao client secret.

Então, faça uma requisição POST para o endpoint <URL do gateway>/oauth/access-token.

O cabeçalho deve conter as seguintes informações:

Authorization: Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

No corpo, devemos passar o code gerado pelo endpoint de grant-code e o grant type em formato x-www-form-urlencoded, como neste exemplo:

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&code=8748d39f-1d4f-311f-92c6-4b279db1b317

Por fim, o seu access token será gerado novamente e deverá ser retornado conforme o exemplo abaixo.

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJsb2NhbGhvc3QiLCJpc3MiOiIxNzIuMTguMC4xIiwic3ViIjoiOWI1Zjc3MWUtNzgyZC0zNTEwLWI2YmEtMzZlOWM2NWJmZDVkIiwiZXhwIjozNjAwLCJpYXQiOjE1Mjg5MTg2MzcsIkFwcDogIjoiUmljYXJkb0FwcCIsIkNvZGU6ICI6IjRlNWIyMTEzLTJkYzYtM2RlNi1iN2ZkLWNkOTYxOTMxZWQyOSIsImxvZ2luIjoidXNlci5sb2dpbiJ9.bhvFpiZ6em9tUgpHxGQGuH7cmvMy4I7STJDRBUybuPM",
  "token_type": "access_token",
  "expires_in": 3600
  }

Com o código gerado, será necessário adicionar o interceptor de JWT no fluxo da API. Ele será responsável por verificar se o token é valido. Veja mais sobre o interceptor aqui.

Fluxo OpenID

O fluxo OpenID permite que usuários usem uma credencial existente para acessar diversos sites e aplicações, sem a necessidade de criação de novas senhas. Isso aumenta a segurança dos usuários pois a senha de cada usuário é fornecida somente a um provedor de identificação, que então confirma a identidade e concede acesso aos sites e aplicações que o usuário deseja utilizar.

Para utilizar o fluxo OpenID, é necessário criar a API do Google OpenID. Clique aqui para ver as instruções de como criar a API.

É necessário, também, ter uma app criada. Com ela, teremos acesso ao client ID, que será o primeiro item a ser passado no endpoint <URL do gateway>/google/oauth/openid/redirect, via requisição POST.

O cabeçalho deve conter as seguintes informações:

Content-Type : application/json

client_id :  client_id

No corpo, devemos enviar o seguinte item:

{
  "extraInfo": {}
}
O client_id a ser passado deve ser o mesmo da app criada.

Feito isso, espera-se uma resposta com um authorization code. Em seguida, deve-se acessar a redirect_uri, que dá acesso a uma página de autenticação do seu provedor.

{
  "redirect_uri": "string?queryParams"
}

Ao realizar o login, haverá redirecionamento automático seguindo os valores do access token.

Revogação de access token e refresh token

A revogação inutiliza um token que ainda está válido mas que não é mais necessário. Após a revogação, o token não será aceito em requisições a APIs.

É possível utilizar o endpoint /revoke da API "OAuth 2.0" para revogar um access token ou refresh token. Em ambos os casos, todos os tokens são revogados — ou seja, se você revogar um access token e houver um refresh token associado a ele, ele também será revogado, e se você revogar um refresh token, o access token associado a ele também será revogado.

Para fazer a revogação, é necessário enviar uma requisição POST para <URL do gateway>/oauth/revoke. A requisição deve conter o header Authorization:

Authorization : Basic client_id:client_secret
Esse client_id:client_secret deve ser uma string convertida em Base64, usando os dados da app criada.

Exemplo de header com o client_id e o client_secret convertidos para Base64:

Authorization: Basic ZjkyMTIxNzMtZTcwNS0zNzNiLWE2OTgtNjE5MjNlMzc4MzU5OjAyYWI1Mjg4LTkyZGItM2FiMy05OWZkLWZhYzRhZjg1N2Q4MQ==

No corpo, passe as seguintes informações no formato x-www-form-urlencoded:

token=<valor>&token_type_hint=access_token

OU

token=<valor>&token_type_hint=refresh_token

Note que não é obrigatório passar o token_type_hint (pois o token é encontrado pelo valor informado), mas ele torna a operação de revogação mais rápida.

Thanks for your feedback!
EDIT

Share your suggestions with us!
Click here and then [+ Submit idea]