toc
- secure flow original without PKCE
- secure flow with PKCE
- detailed flow examples (with PKCE)
secure flow original without PKCE state param prevents CSRF
1) [front-channel] browser -> my-app
GET [my-app.com/login](http://my-app.com/login)
**- generate random value: state**
< redirect to [auth-server.com/auth](http://auth-server.com/auth) (response_type, client_id, redirect_uri, scope, **state**)
2) [front-channel] browser -> auth-server
GET [auth-server.com/auth](http://auth-server.com/auth) (response_type=code, client_id, redirect_uri, scope, **state**)
< redirect to [my-app.com/redirect](http://my-app.com/redirect) (code, state)
3) [front-channel] browser -> my-app/redirect
GET [my-app.com/redirect](http://my-app.com/redirect) (code, **state**)
**- verifies state**
4) [back-channel] my-app -> auth-server
POST [auth-server.com/token](http://auth-server.com/token) (grant_type=authorization_code, code, redirect_uri, client_id, client_secret)
< json (access_token, refresh_token, ...)
_) [back-channel] my-app -> auth-server
POST [auth-server.com/token](http://auth-server.com/token) (grant_type=refresh_token, refresh_token)
< json (access_token, refresh_token, ...)
secure flow with PKCE Proof Key Code Exchange (PKCE) PKCE prevents “Authorization Code Injection”
state param prevents CSRF,
but PKCE also covers that,
so now often used to store app-specific state (eg. which page to redirect to after login)
BUT only when auth-server supports PKCE
if not, you need it random for CSRF
1) [front-channel] browser -> my-app
GET [my-app.com/login](http://my-app.com/login)
**- generate secret -> generate hash: code_challenge**
- put anything in: state
< redirect to [auth-server.com/auth](http://auth-server.com/auth) (response_type, client_id, redirect_uri, scope, state, **code_challenge**, **code_challenge_method**)
2) [front-channel] browser -> auth-server
POST [auth-server.com/auth](http://auth-server.com/auth) (response_type, client_id, redirect_uri, scope, state, **code_challenge**, **code_challenge_method**)
< redirect [my-app.com/redirect](http://my-app.com/redirect) (code, state)
3) [front-channel] browser -> my-app/redirect
GET [my-app.com/redirect](http://my-app.com/redirect) (code, state)
- verifies state
4) [back-channel] my-app -> auth-server
POST [auth-server.com/token](http://auth-server.com/token) (grant_type, code, redirect_uri, **code_verifier**, client_id, client_secret)
**- verifies code_verifier matches previous code_challenge**
< json (access_token, refresh_token, ...)
*) [back-channel]
POST [auth-server.com/token](http://auth-server.com/token) (grant_type, refresh_token)
< json (access_token, refresh_token, ...)
detailed flow example (with PKCE)
Code Verifier (Secret)
a random string you create (between 43-128 chars long)
code_verifier = random_string(43, 128)
Code Challenge (Public Hash)
code_challenge = base64url(sha256(code_verifier))
// base64url != base64
<https://auth-server.com/auth>
response_type=code
client_id=CLIENT_ID
redirect_uri=REDIRECT_URL // must match configured on auth-server
scope=foo
state=XXX // random value for CRSF (not needed if auth-servier does PKCE)
**code_challenge=XXXXXXXX**
**code_challenge_method=S256**
auth server redirects back (using your REDIRECT_URL)
<https://my-app.com/redirect>
code=AUTH_CODE
state=XXX
or errors
<https://my-app.com/redirect>
error=access_denied
state=XXX
get first token
POST <https://auth-server.com/token>
grant_type=authorization_code
code=AUTH_CODE
redirect_uri=REDIRECT_URL // the one you used in the original request
**code_verifier=VERIFIER_STRING**
// either in Basic Auth header OR in post body (depending on server)
client_id=CLIENT_ID
client_secret=CLIENT_SECRET
get token with refresh token
POST <https://auth-server.com/token>
grant_type=refresh_token
refresh_token=REFRESH_TOKEN
// either in Basic Auth header OR in post body (depending on server)
client_id=CLIENT_ID
client_secret=CLIENT_SECRET
{
"token_type": "Bearer",
"access_token": "xxx"
"expires_in":3600,
"scope": "foo",
"refresh_token": "yyy" // maybe
}
(src: Course: The Nuts and Bolts of OAuth 2.0)