OAuth 2.0
Table of contents
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:
- Client sends request to autorization server
- Client gets an authorization code back
- Client sends request to a token endpoint
- Client gets an access token
- 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 andtoken
for implicit flowclient_id
: client app idredirect_uri
: absolute uri to be redirected after authorizationstate
: a random value that will be returned back in redirect. This is a protection against CSRF.scope
: the scope of resources you want to protectcode_challenge_method
(PKCE only): the encryption used in code challenge; typically S256 for SHA256code_challenge
(PKCE only): the generated challenge fromcode_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 requestscope
code
: extracted from urlcode_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: