OAuth 2.0

Table of Contents

Topics

Authorization Code Grant

+----------+
| Resource |
|   Owner  |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier      +---------------+
|         -+----(A)-- & Redirection URI ---->|               |
|  User-   |                                 | Authorization |
|  Agent  -+----(B)-- User authenticates --->|     Server    |
|          |                                 |               |
|         -+----(C)-- Authorization Code ---<|               |
+-|----|---+                                 +---------------+
  |    |                                         ^      v
 (A)  (C)                                        |      |
  |    |                                         |      |
  ^    v                                         |      |
+---------+                                      |      |
|         |>---(D)-- Authorization Code ---------'      |
|  Client |          & Redirection URI                  |
|         |                                             |
|         |<---(E)----- Access Token -------------------'
+---------+       (w/ Optional Refresh Token)

Authorization Code Grant with SPA

Code Grant need to keep client_secret as a secret. So server-side web application is necessary.

For this reason, SPA applications usually choose to use implicit grant. However, some OAuth2 providers provide code grant only. If we use these providers for SPA app, we need a kind of oauth2 client delegator on backend API, which works as follows:

  1. /login :: Requires a returning point as a parameter, in this case, the URI for th SPA app. Keep this URI in somewhere like Cookie or so, and redirect to the OAuth2 provider auth point.
  2. /auth :: The endpoint which will be redirected to by the provider with code Exchange it for the token, and redirect to the returning proint received from Step 1.

In this way, you can use Authorization Code Grant without coupling SPA App and the backend API.

Implicit Grant

+----------+
| Resource |
|  Owner   |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier     +---------------+
|         -+----(A)-- & Redirection URI --->|               |
|  User-   |                                | Authorization |
|  Agent  -|----(B)-- User authenticates -->|     Server    |
|          |                                |               |
|          |<---(C)--- Redirection URI ----<|               |
|          |          with Access Token     +---------------+
|          |            in Fragment
|          |                                +---------------+
|          |----(D)--- Redirection URI ---->|   Web-Hosted  |
|          |          without Fragment      |     Client    |
|          |                                |    Resource   |
|     (F)  |<---(E)------- Script ---------<|               |
|          |                                +---------------+
+-|--------+
  |    |
 (A)  (G) Access Token
  |    |
  ^    v
+---------+
|         |
|  Client |
|         |
+---------+

Note: Previously, it was recommended that browser-based apps use the "Implicit" flow, which returns an access token immediately and does not have a token exchange step. In the time since the spec was originally written, the industry best practice has changed to recommend that the authorization code flow be used without the client secret. This provides more opportunities to create a secure flow, such as using the state parameter. References: Redhat, Deutsche Telekom, Smart Health IT.

How state prevents from CSRF attack

https://authorization-server.com/auth?response_type=code&
  client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx

state: A random string generated by your application, which you'll verify later.

https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx

This ensures your redirection endpoint(https://example-app.com/cb, in this case) isn't able to be tricked into attempting to exchange arbitrary authorization codes. This prevents CSRF(Cross Site Request Forgery), which let the victim login as the attacker.

Refresh Tokens

curl -X POST -H 'Authorization: Basic dGVzdGNsaWVudDpzZWNyZXQ=' -d 'refresh_token=<prev_refresh_token>&grant_type=refresh_token' localhost:3000/oauth/token
{
    "token_type":"bearer",
    "access_token":"<new_access_token>",
    "expires_in":20,
    "refresh_token":"<new_refresh_token>"
}

Links