Authorization Code Grant (w/ PKCE)
A temporary code that the client will exchange for an access token. The user authorizes the application, they are redirected back to the application with a temporary code in the URL. The application exchanges that code for the access token.
authorizationServer.enableGrantType({
grant: "authorization_code",
userRepository,
authCodeRepository,
});
Flow
Part One
The client redirects the user to the /authorize
with the following query parameters:
- response_type must be set to
code
- client_id is the client identifier you received when you first created the application
- redirect_uri indicates the URL to return the user to after authorization is complete, such as org.example.app://redirect
- state is a random string generated by your application, which you’ll verify later
- code_challenge must match the The code challenge as generated below,
- code_challenge_method – Either
plain
orS256
, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string. If this parameter is omitted, the server will assume plain.
The client secret should never be used during the Part One of the authorization_code flow.
View sample authorization_code (part 1) request
GET /authorize HTTP/1.1
Host: example.com
response_type=code
&client_id=xxxxxxx
&redirect_uri=http://localhost
&scope="contacts.read contacts.write"
&state=abcdefghijklmnopqrstuvwxyz123456789
&code_challenge=92d3b56942866d1edf02c33339b7c3dc37c6201282bb238cb47f0d3289f28a93f1bdd8af6ca9913aed0c4c
&code_challenge_method=S256
The user will be asked to login to the authorization server and approve the client and requested scopes.
If the user approves the client, they will be redirected from the authorization server to the provided redirect_uri
with the following fields in the query string:
- code is the authorization code that will soon be exchanged for a token
- state is the random string provided and should be compared against the initially provided state
View sample authorization_code (part 1) response
HTTP/1.1 302 Found
Location: http://localhost&code=eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRfaWQiOiJhdXRoY29kZWNsaWVudCIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly9sb2NhbGhvc3QiLCJhdXRoX2NvZGVfaWQiOiJteS1zdXBlci1zZWNyZXQtYXV0aC1jb2RlIiwic2NvcGVzIjpbXSwiZXhwaXJlX3RpbWUiOjE2MDE3NTM3MzMsImNvZGVfY2hhbGxlbmdlIjoiT0RRd1pHTTRZelpsTnpNeU1qUXlaREF4WWpFNU1XWmtZMlJrTmpKbU1UbGxNbUkwTnpJMFpEbGtNR0psWWpGbE1tTXhPV1kyWkRJMVpEZGpNak13WWciLCJjb2RlX2NoYWxsZW5nZV9tZXRob2QiOiJTMjU2In0.OIEtZN5BHNaB4Mz0plUpGAP93EHyoil2smJiG3S_2BM&state=abcdefghijklmnopqrstuvwxyz123456789
Part Two
The client sends a POST to the /token
endpoint with the following body:
- grant_type must be set to
authorization_code
- client_id is the client identifier you received when you first created the application
- client_secret (optional) is the client secret and should only be provided if the client is confidential
- redirect_uri
- code_verifier
- code is the authorization code from the query string
Clients such as Browser Based Apps and Native Mobile Apps should NEVER have or use a client_secret. That means the client_secret should be omitted both when initially creating the OAuthClient entity, and when making requests.
View sample authorization_code (part 2) request
POST /token HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
&redirect_uri=http://localhost
&code_verifier=OTJkM2I1Njk0Mjg2NmQxZWRmMDJjMzMzMzliN2MzZGMzN2M2MjAxMjgyYmIyMzhjYjQ3ZjBkMzI4OWYyOGE5M2YxYmRkOGFmNmNhOTkxM2FlZDBjNGM
&code=eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRfaWQiOiJhdXRoY29kZWNsaWVudCIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly9sb2NhbGhvc3QiLCJhdXRoX2NvZGVfaWQiOiJteS1zdXBlci1zZWNyZXQtYXV0aC1jb2RlIiwic2NvcGVzIjpbXSwiZXhwaXJlX3RpbWUiOjE2MDE3NTM3MzMsImNvZGVfY2hhbGxlbmdlIjoiT0RRd1pHTTRZelpsTnpNeU1qUXlaREF4WWpFNU1XWmtZMlJrTmpKbU1UbGxNbUkwTnpJMFpEbGtNR0psWWpGbE1tTXhPV1kyWkRJMVpEZGpNak13WWciLCJjb2RlX2NoYWxsZW5nZV9tZXRob2QiOiJTMjU2In0.OIEtZN5BHNaB4Mz0plUpGAP93EHyoil2smJiG3S_2BM
The authorization server will respond with the following response
- token_type will always be
Bearer
- expires_in is the time the token will live in seconds
- access_token is a JWT signed token and is used to authenticate into the resource server
- refresh_token is a JWT signed token and can be used in with the refresh grant
- scope is a space delimited list of scopes the token has access to
View sample authorization_code (part 2) response
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
token_type: 'Bearer',
expires_in: 3600,
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDE3NTUxMDQsIm5iZiI6MTYwMTc1MTUwNCwiaWF0IjoxNjAxNzUxNTA0LCJqdGkiOiJuZXcgdG9rZW4iLCJjaWQiOiJ0ZXN0IGF1dGggY29kZSBjbGllbnQiLCJzY29wZSI6IiJ9.-V9x03iz-3ISRMdj9m1-FCKjmtfjvv6wqnBj6VZdW28',
refresh_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJhdXRoY29kZWNsaWVudCIsImFjY2Vzc190b2tlbl9pZCI6Im5ldyB0b2tlbiIsInJlZnJlc2hfdG9rZW5faWQiOiJ0aGlzLWlzLW15LXN1cGVyLXNlY3JldC1yZWZyZXNoLXRva2VuIiwic2NvcGUiOiIiLCJleHBpcmVfdGltZSI6MTYwMTc1NTEwNCwiaWF0IjoxNjAxNzUxNTAzfQ.J_RUFD5-158atTmI98R95vowZWi4mUEXYCO7iNwzpK4',
scope: 'contacts.read contacts.write'
}
PKCE
PKCE (RFC 7636) is an extension to the Authorization Code flow to prevent several attacks and to be able to securely perform the OAuth exchange from public clients.
By default, PKCE is enabled and encouraged for all users. If you need to support a legacy client system without PKCE,
you can disable PKCE with the authorization server using the requiresPKCE
configuration option.
Code Verifier
The code_verifier
is part of the extended “PKCE” and helps mitigate the threat of having authorization codes intercepted.
Before initializing Part One of the authorization code flow, the client first creats a code_verifier
. This 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.
We can do this in Node using the native crypto package and a base64urlencode
function:
import { randomBytes } from "crypto";
const code_verifier = randomBytes(43).toString("hex");
@see https://www.oauth.com/oauth2-servers/pkce/authorization-request/
Code Challenge
Now we need to create a code_challenge
from our code_verifier
.
For devices that can perform a SHA256 hash, the code challenge is a BASE64-URL-encoded string of the SHA256 hash of the code verifier.
const code_challenge = base64urlencode(crypto.createHash("sha256").update(code_verifier).digest());
Clients that do not have the ability to perform a SHA256 hash are permitted to use the plain code_verifier
string as the code_challenge
.
const code_challenge = code_verifier;
Need a base64urlencode function?
function base64urlencode(str: string) {
return Buffer.from(str)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
Revocation
Authorization codes are only valid for a single use. In addition, they can be explicitly revoked on a server that supports RFC7009 “OAuth 2.0 Token Revocation”.
An authorization code revocation request will include the following parameters:
- token is the authorization code previously issued to the client
- token_type_hint (optional) should be set to
authorization_code
View sample revoke authorization_code request
POST /token HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
token_type_hint=authorization_code
&refresh_token=xxxxxxxxx
The authorization server will respond with the following response
View sample revoke authorization_code response
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache