Skip to content

Instantly share code, notes, and snippets.

@drmalex07
Last active December 25, 2022 18:25
Show Gist options
  • Save drmalex07/d799e82529d07ef2de86ec2d2972dd0c to your computer and use it in GitHub Desktop.
Save drmalex07/d799e82529d07ef2de86ec2d2972dd0c to your computer and use it in GitHub Desktop.
OAuth2 Basic Flow #oauth2 #authorization #authentication #access-token

Basic OAuth2 authorization flows

Using the following symbols:

  • RS: Resource-Server (holds user's resources, provides authentication/authorization)
  • A: A client application relying on RS for authentication/authorization
  • U: A user of A owning resources on RS

Application A registers with RS

The application A registers to RS and acquires its own credentials as a pair of (client_id, client_secret). These are to be used from A to directly authenticate to RS.

As a part of registration, A declares a set of legal redirect (callback) URLs. These are to be used from RS to redirect a user U back to the application (in a web application scenario).

A. Grant Type: authorization code

In this type of flow (authorization_code), a user U interacts with a web application A behind a browser.

A.1. User U grants an access token to application A

The RS advertises 2 URLs:

  • authorization-endpoint: the service endpoint that handles authorization
  • token-endpoint: the service endpoint that handles token exchange/renewal.

A.1.1. The application A acquires an authorization code from U

The application A builds a URL of the following form and redirects user U to RS:

https://<RS>/<authorization-endpoint>?
  response_type=code            # The response will be an authorization code (grant)
  client_id=${client_id}        # The client_id identifying application A
  redirect_uri=${redirect_uri}  # Must be one of redirection URLs as registered from A
  scope=${scope}                # A RS-specific scope describing the kind of access requested. This is usually represented as set of URIs.
  state=${state}                # An opaque (to RS) value used by A to maintain state between the request and callback. This is mainly used as an anti-forgery measure (to ensure authorization was indeed initiated by U).

The user is redirected to RS and grants (or denies) its permission to A to use the set of resources represented by given scope.

After user's consent, the RS redirects user U back to application A using the requested redirection_uri and appending a couple of GET parameters. The code is a short-lived token that serves the only purpose to be later exchanged for a real access token. In order to for A to acquire a real access token, one more step will be required (because A has also to prove its identity!).

https://<A>/<redirection-path>?
   code=${code}    # the authorization code (also called authorization grant) that can be later used to acquire an access token.
   state=${state}  # same value included in 1st redirection (from A to RS)

At this point, A may check state to ensure that user U actually initiated this conversation.

A.1.2 Exchange the authorization code for an access token

Now, A exchanges code acquired at step 1.1 with an access token, by posting a proper request to RS. This part of communication is happening in the back-channel (i.e. between A and RS, no participation of user U).

POST https://<RS>/<token-endpoint>?
  grant_type=authorization_code  # what are we trying to exchange with an access token  
  code=${code}                   # the code we acquired from redirection from RS to A
  redirect_uri=${redirect_uri}   # the redirection URL of A (as in 1st redirection)
  client_id=${client_id}         # The client_id identifying application A
  client_secret=${client_secret} # The secret authenticating A to RS 

The above request will yield a JSON object with at least the following fields access_token, token_type, expires_in. An example:

{
   "access_token": "...",    // The access token to be used from A
   "refresh_token": "...",   // An optional token that can be used to later acquire a new access token 
   "token_type": "Bearer",   
   "expires_in": 3600,       // seconds to  expire
   "scope": "...",           // accepted scope (meaningful only if user accepted a subset of requested scope) 
   ...
}

A.2. The application A accesses U's resources using the access token

The application A, after it has acquired an access_token, can access resources owned by U (whatever is described in given scope) by using RS API. To do so it must accompany every API request with an Authorization header of following format:

Authorization: Bearer ${access_token}

A.3. The application A refreshes access token

After some time, the access_token will expire. If response from 1.2 also included a refresh_token field, then A may use this value to acquire a new access token (without user's intervention).

The request (also happening in the back channel) is similar to what was sent in 1.2. It has the following format:

POST https://<RS>/<token-endpoint>?
  grant_type=refresh_token   # we present a refresh token, and expect a (new) access token  
  refresh_token=${refresh_token}  # the refresh token as the value extracted from the response to 1.2
  redirect_uri=${redirect_uri}   # the redirection URL of A (as in 1st redirection)
  client_id=${client_id}         # The client_id identifying application A
  client_secret=${client_secret} # The secret authenticating A to RS 

B. Grant Type: password

In this flow (password) the application Α and user U coincide in terms of trust.

This grant type (via resource owner's password) defines a simplified authentication flow where the client appilication A holds the user's password and directly requests an access token. Obviously, this grant type can only be used by trusted clients (e.g. a local desktop or a shell application).

B.1 Application A acquires an access token from RS

Directly request an access token authenticating both as the client and the user:

POST https://<RS>/<token-endpoint>?
  grant_type=password  # we hold the user's password
  username=${username}           # The username identifying user U
  password=${password}           # The password for user U
  client_id=${client_id}         # The client_id identifying application A
  client_secret=${client_secret} # The secret authenticating A to RS
  scope=${scope}       # A RS-specific scope describing the kind of access requested

B.2 The application A accesses U's resources using the access token

Exactly as described in A.2, just send the Authorization header to request protected resources from RS.

C. Grant Type: PKCE

Todo

See:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment