Authorization code grant : how it works, and try it !


Authorization code grant (also named "auth_code") is one of the most popular authentication method on the web. Every oauth2 provider implements this flow which is the best for web authentication. Facebook, Google, Twitter, Linkedin... all of them use it (or partially, we will explain why).

But how it works? We will try to explain this method step by step without a diagram paste from the web. An example with our Keycloak is more convinient.

| See more

AUTHORIZATION CODE GRANT : HOW IT WORKS, AND TRY IT !

Requirements

Bash oauth2 client

We will use our bash oauth2 client. It is a simple wrapper which only 3 dependancies :

  • CURL for requests
  • jq for output
  • netcat in order to create a small web server

After cloning, just make the script executable with chmod +x oidc-client.sh

All oauth2 operations are supported, without any context. All parameters are required for each call.

A registered client

This client has only a redirect URI on 127.0.0.1, client_id is

auth_code_demo

Openid endpoint configuration is

https://app.please-open.it/auth/realms/d0b4a783-114d-4f19-8a1b-425f0abe02ae/.well-known/openid-configuration

You can inspect this endpoint with the script :

              
  ./oidc-client.sh  \
   --operation get_oidc_server_infos  \
   --openid-endpoint https://app.please-open.it/auth/realms/d0b4a783-114d-4f19-8a1b-425f0abe02ae/.well-known/openid-configuration
              
            

Before auth code : implicit

Theory


Back to the begining : you know oauth2 as a web authentication protocol.

What happens when you click on the "login with" button?

  • The button is a link to an authentication form located on the provider domain/server
  • After typing your credentials, also accept to share data, the servers anwser with a Redirect 302 to the app.
  • Your browser follows the redirect (loading the URI from "Location" header response)
  • Here is the magic. The "Location" is a GET request with a token as an URL parameter. This is how a webapplication get a token from an authentication process.

So... there is no communication between the application and the authentication server. This process uses your web browser to transfer informations between apps...

This solution is simple, but not really reliable with a really big limitation :

Your token (aka session identifier) is in an URI. An URI is not a safe place for confidential informations.

The biggest problem is webanalytics. So, send a token in an URI is not a good idea.

OAuth 2.0 Security Best Current Practice says : In order to avoid these issues, clients SHOULD NOT use the implicit grant (response type "token") or other response types issuing access tokens in the authorization response,
End of game...

Try it

 
  ./oidc-client.sh \
   --operation implicit_grant  \
   --openid-endpoint https://app.please-open.it/auth/realms/d0b4a783-114d-4f19-8a1b-425f0abe02ae/.well-known/openid-configuration  \
   --client-id auth_code_demo  \
   --scope email  \
   --redirect-uri http://127.0.0.1:8080
          

copy/paste the generated URI in your browser. An authentication form will ask you for your credentials. Note that generated URI is not on 127.0.0.1 but directly on keycloak authentication.

Great now in the browser you have a token with some details. Those details comes from the URI.

Way better : authorization code

A step can change all

Send a token in an URI is a problem. So, authorization code flow says : never send a token in an URI, just a single use authorization code with a short lifetime.

So, in the redirection, an authorization code is sent. This code has to be exchanged for an access_token with a POST request. The answer contains the access_token.

Solved? Partially...

Web origin

Your app send a POST request to the authentication server with the auth_code. Cross-origin Resource Sharing (CORS) must allows this call. Web origins parameter in Keycloak

Try it

 
  ./oidc-client.sh  \
   --operation authorization_code_grant   \
   --openid-endpoint https://app.please-open.it/auth/realms/d0b4a783-114d-4f19-8a1b-425f0abe02ae/.well-known/openid-configuration  \
   --client-id auth_code_demo  \
   --scope email   \
   --redirect-uri http://127.0.0.1:8080
            

Copy/paste the generated URI. After login, you have an authorization_code (simply named "code" in the answer). This code can be used only once. It also have a short lifetime.

The second operation is an exchange from this code to obtain a new token. Operation is called "auth_code".

 
  ./oidc-client.sh  \
   --operation auth_code   \
   --openid-endpoint https://app.please-open.it/auth/realms/d0b4a783-114d-4f19-8a1b-425f0abe02ae/.well-known/openid-configuration  \
   --client-id auth_code_demo  \
   --redirect-uri http://127.0.0.1:8080  \
   --authorization-code PASTE_AUTH_CODE_HERE  
              
The answer contains the session we need :
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJzYkdqbjk3TXJfa0RGa3ZEV2lnZW51OWYzdndHOXlEYlF1S1RkVUNBN0Y0In0.eyJleHAiOjE1OTM1MDE5MTAsImlhdCI6MTU5MzUwMTYxMCwiYXV0aF90aW1lIjoxNTkzNDk5ODUzLCJqdGkiOiIzMjM4ZDBlYy02MzkzLTRiYjItYmM0Zi0yMzgzYzQxNzkwZjAiLCJpc3MiOiJodHRwczovL2FwcC5wbGVhc2Utb3Blbi5pdC9hdXRoL3JlYWxtcy9kMGI0YTc4My0xMTRkLTRmMTktOGExYi00MjVmMGFiZTAyYWUiLCJhdWQiOlsicmVhbG0tbWFuYWdlbWVudCIsIndlYiIsImFwaS1iYWNrZW5kIiwiYWNjb3VudCJdLCJzdWIiOiI1NTM5MjZjZS1jZjI1LTQ5ZmEtYTU4Mi1hMzBiMDc4OWM3MzYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhdXRoX2NvZGVfZGVtbyIsInNlc3Npb25fc3RhdGUiOiJkNzk5MGRmNy0xZWJmLTQzNjEtYWMyYS1jMjJiYTM3ZjIyMGMiLCJhY3IiOiIwIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly8xMjcuMC4wLjE6ODA4MCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidmlzaW9uIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7InJlYWxtLW1hbmFnZW1lbnQiOnsicm9sZXMiOlsidmlldy1yZWFsbSIsInZpZXctaWRlbnRpdHktcHJvdmlkZXJzIiwibWFuYWdlLWlkZW50aXR5LXByb3ZpZGVycyIsImltcGVyc29uYXRpb24iLCJyZWFsbS1hZG1pbiIsImNyZWF0ZS1jbGllbnQiLCJtYW5hZ2UtdXNlcnMiLCJxdWVyeS1yZWFsbXMiLCJ2aWV3LWF1dGhvcml6YXRpb24iLCJxdWVyeS1jbGllbnRzIiwicXVlcnktdXNlcnMiLCJtYW5hZ2UtZXZlbnRzIiwibWFuYWdlLXJlYWxtIiwidmlldy1ldmVudHMiLCJ2aWV3LXVzZXJzIiwidmlldy1jbGllbnRzIiwibWFuYWdlLWF1dGhvcml6YXRpb24iLCJtYW5hZ2UtY2xpZW50cyIsInF1ZXJ5LWdyb3VwcyJdfSwid2ViIjp7InJvbGVzIjpbImRhdGFfbWFuYWdlbWVudCJdfSwiYXBpLWJhY2tlbmQiOnsicm9sZXMiOlsidmlzaW9uIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoibWF0aGlldS5wYXNzZW5hdWRAcGxlYXNlLW9wZW4uaXQiLCJlbWFpbCI6Im1hdGhpZXUucGFzc2VuYXVkQHBsZWFzZS1vcGVuLml0In0.SfmCvPa0_qefXrVJ7npErJIUYb57cUFb3JH-5jWyhWDsC7OvN_bYbpJUwjBvxLNUKUjI7CdW73GP8H-g7pxIWDGhdDJavEefZDX9hfBLIKrAQCjZ4SGbtSGCFEeU2mxDhJqKNZiOW9D2ftpnWP_64AMPc8zbHuVIcH90n0-ynUB6UrhqtEbDpdHRfVDfXq-bmmzEolmt3_jseqDcFqul4XFgIVPexLUMDw-qf-4OuHAPgPWXhznKEusV9hmBWxfOJ7362cn3DYn9NseV4pzWaZryHbtWj0CFYQgsg8qxEZqGt8uxqdLacL897jLVh1UVMi2cN2Gq1fKHE-wa0UjJqg",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2YWNjMTE0OC0zZjcwLTQ0YmYtYjc5Mi04ZDljM2ZiMWRkMDQifQ.eyJleHAiOjE1OTM1MDM0MTAsImlhdCI6MTU5MzUwMTYxMCwianRpIjoiM2FjODMzNjItY2ZiYS00OGU3LTk5ZGEtZmEwZGNmZGU2NmJmIiwiaXNzIjoiaHR0cHM6Ly9hcHAucGxlYXNlLW9wZW4uaXQvYXV0aC9yZWFsbXMvZDBiNGE3ODMtMTE0ZC00ZjE5LThhMWItNDI1ZjBhYmUwMmFlIiwiYXVkIjoiaHR0cHM6Ly9hcHAucGxlYXNlLW9wZW4uaXQvYXV0aC9yZWFsbXMvZDBiNGE3ODMtMTE0ZC00ZjE5LThhMWItNDI1ZjBhYmUwMmFlIiwic3ViIjoiNTUzOTI2Y2UtY2YyNS00OWZhLWE1ODItYTMwYjA3ODljNzM2IiwidHlwIjoiUmVmcmVzaCIsImF6cCI6ImF1dGhfY29kZV9kZW1vIiwic2Vzc2lvbl9zdGF0ZSI6ImQ3OTkwZGY3LTFlYmYtNDM2MS1hYzJhLWMyMmJhMzdmMjIwYyIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCJ9.Q-5bxkWbjD-1FMPv0zbJV6LaXjZer6063pjpYKdg9Nc",
"token_type": "bearer",
"not-before-policy": 0,
"session_state": "d7990df7-1ebf-4361-ac2a-c22ba37f220c",
"scope": "profile email"
}

Special case : Google

The specification (https://oauth.net/2/grant-types/authorization-code/) says :

The Authorization Code grant type is used by confidential and public clients to exchange an authorization code for an access token.

If you try the same manipulation with Google's oauth provider, you will have this answer :
{
"error": "invalid_request",
"error_description": "client_secret is missing."
}

The reference documentation from Google excludes public clients from this authorization code exchange operation.

oidc-client.sh supports client-secret option for this operation. So, for an operation with "client_secret" you have to keep this variable secret so forget it in a web application.

Exchange an authorization_code for a token by using a backend operation increases security level.

Allowing implicit grant for public clients (akka web apps) is a pain and an issue (as IETF says)

https://tools.ietf.org/html/draft-ietf-oauth-security-topics-14#section-2.1.2

Never share your client credentials in a webapplication. In this case, you have to use implicit flow. So please be very carreful with web analytics, on a logout do not forget to revoke the token.

Requirement to Google : why not allowing authorization code exchange with a public client ?

Let's Get In Touch!


Any question? Want more information? Follow us on twitter or you can reach out to us via email.