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:
/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./auth
:: The endpoint which will be redirected to by the provider withcode
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
- Provides the following link to the user
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.
- User allows the acess
- User redirects to the following link.
https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
- You should first compare this
state
value to ensure it matches the one you started with. - You can typically store the
state
value in a cookie or session, and compare it when the user comes back.
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
- A special kind of token that can be used to obtain a renewed access token
- Refresh tokens must be stored securely by an application because they essentially allow a user to remain authenticated forever.
- Refresh Tokens never expire.
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>"
}