OAuth 2.0

Table of contents
  1. What is an OAuth 2.0 protocol?
    1. OpenID
  2. Client types
    1. Client secret
    2. Public clients
    3. Confidential clients
  3. Authorization flow
    1. Implicit flow
    2. Authorization code flow
    3. Authorization code flow with PKCE
  4. Authorization server API
    1. Authorize
    2. Token
  5. PKCE Code Challenge
    1. Code verifier
    2. Code challenge

What is an OAuth 2.0 protocol?

According to Google, it is an ‘open standard for access delegation’. While it sounds intimidating, it is essentially made to ‘let this application access my Google photos’, ‘let this site use my Facebook contacts’, etc. So it was developed as method for authorization to a 3rd party resource.

Some terms:

  • Resource owner: that’s the user (you) wanting to grant access
  • Resource server: the API you want to access
  • Client: application requesting access
  • User Agent: the thing user is using to talk to client (browser, mobile app)
  • Authorization server: authorizes and grants access tokens to client

OpenID

One thing to note is the word authorization, and you shoud not to confuse it with authentication. When I first read about OAuth, I thought, “Well isn’t this the ‘Sign in with Google/Facebook’ button that I see quite a lot on websites these days?”.

It sort of is, because the protocol behind that button is OpenID which is built on top of OAuth 2.0. So the way they operate are very similar, but it is good to know the difference that OAuth is for authorization and OpenID builds a layer on top of OAuth for authentication.


Client types

There are two different types of clients in OAuth. One is a public client and the other is a confidential client. To understand the difference, you need to know the term client secret.

Client secret

A client secret is nothing more than a random string generated. It is usually created by generating a secure random string of 256-bit (32 bytes) and then converting it to hex.

This value should never be revealed to the outside except for the authorizing server and the client app. Hence the name ‘client secret’.

Inside your code, client secret will be used to successfully authorize users. But the issue that arises is where should the client store this secret.

Public clients

If the client cannot keep the client secret a secret, it is called a public client.

For example, single-page apps that expose everything on the browser with no backend or mobile apps that can have their HTTPS request intercepted and revealed are considered public clients.

In case of an SPA, everything is exposed on the browser. Chrome inspect will reveal the source code, local storage, session, and cookies. So storing client secret is infeasible.

For a mobile app, apparently it is possible to provide a fake HTTPS certificate that goes to your own API. So you can catch an HTTPS leaving the phone, route it to a different API, have that API make a request to the initial intended API, and return the response to phone as if it would normally, while the proxy API in the middle can inspect all the requests (which may contain the client secret at some point).

Confidential clients

This is typically a traditional web server or anything backed by a server where nobody can take a peek at the source code or have the requests intercepted.


Authorization flow

There are a few different flows, but I will only document three of them: implicit flow, authorization code flow, authorization code flow with PKCE.

The general process is as below:

  1. Client sends request to autorization server
  2. Client gets an authorization code back
  3. Client sends request to a token endpoint
  4. Client gets an access token
  5. Client places this token in a header when sending a request to resource server

Implicit flow

Implicit flow is much more simplified. After step 1, implicit flow skips right to step 4. Because the access token is revealed on the browser url, this is considered an insecure lecay method.

Authorization code flow

Client gets an authorization code back as a request parameter embedded in the url. The client then uses this code to exchange it for an access token. Usually secure random strings such as state and client secret are used to validate the process.

Authorization code flow with PKCE

For public clients that cannot keep any secret strings, PKCE (Proof Key for Code Exchange) is implemented. This step includes an additional code challenge and verifying step.


Authorization server API

Typically there are two endpoints during the process.

Authorize

Typical request is an HTTPS GET to a path that often looks like oauth/authorize.

Parameters

  • response_type: code for authorization code flow and token for implicit flow
  • client_id: client app id
  • redirect_uri: absolute uri to be redirected after authorization
  • state: a random value that will be returned back in redirect. This is a protection against CSRF.
  • scope: the scope of resources you want to protect
  • code_challenge_method (PKCE only): the encryption used in code challenge; typically S256 for SHA256
  • code_challenge (PKCE only): the generated challenge from code_verifier

Token

After extracting the authorization code from the redirect url, you make an HTTPS POST request to oauth/token.

Header:

  • Authorization: Basic Base64_url_encode('client_id:client_secret')
  • Content-Type: application/x-www-form-urlencoded

Body:

  • grant_type: authorization_code, refresh_token, client_credentials
  • client_id
  • redirect_uri: should be the same as the one used for authorization request
  • scope
  • code: extracted from url
  • code_verifier: proof key for the code_challenge

Response:

{
  "id_token": "~",
  "access_token": "~",
  "refresh_token": "~",
  "token_type": "Bearer",
  "expires_in": 10000
}

PKCE Code Challenge

Code verifier

According to here it is a ‘cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long’.

Code challenge

Code challenge is created by hashing the code_verifier with SHA256 and then encoding as a BASE6-URL string.


References: