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)