Proof Key for Code Exchange
lastmod 2024-01-05created 2023-08-172 edits
What is PKCE (Proof Key for Code Exchange)? It is an OAuth 2.0 security extension for public clients on mobile devices, designed to prevent malicious programs that break into the same computer from intercepting the authorization code. (loginradius)
About
Simply put, PKCE replaces the Client Secret used in the Authorization Code Grant flow with a one-time password.
Known as PKCE (Proof Key for Code Exchange), the approach of using a provable key when exchanging the code for authentication is a way to prevent malicious attacks and carry out the Authorization Code Grant flow securely.
It is mainly useful for replacing the Client Secret used by client applications that use a Client Secret or by the Authorization Code Grant flow.
This approach basically works with three parameters: code_verifier, code_challenge, and code_challenge_method.
A picture will be easier to understand than an explanation in words. The picture below shows the Authorization Code Grant + PKCE flow well.
Examples
generatePKCEVerifier and generatePKCEChallenge
function dec2hex(dec: number) {
return ('0' + dec.toString(16)).substr(-2)
}
// Functions below taken from: https://stackoverflow.com/questions/63309409/creating-a-code-verifier-and-challenge-for-pkce-auth-on-spotify-api-in-reactjs
export function generatePKCEVerifier() {
const verifierLength = 56
const array = new Uint32Array(verifierLength)
if (typeof crypto === 'undefined') {
const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
const charSetLen = charSet.length
let verifier = ''
for (let i = 0; i < verifierLength; i++) {
verifier += charSet.charAt(Math.floor(Math.random() * charSetLen))
}
return verifier
}
crypto.getRandomValues(array)
return Array.from(array, dec2hex).join('')
}
async function sha256(randomString: string) {
const encoder = new TextEncoder()
const encodedData = encoder.encode(randomString)
const hash = await crypto.subtle.digest('SHA-256', encodedData)
const bytes = new Uint8Array(hash)
return Array.from(bytes)
.map((c) => String.fromCharCode(c))
.join('')
}
function base64urlencode(str: string) {
return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}
export async function generatePKCEChallenge(verifier: string) {
const hasCryptoSupport =
typeof crypto !== 'undefined' &&
typeof crypto.subtle !== 'undefined' &&
typeof TextEncoder !== 'undefined'
if (!hasCryptoSupport) {
console.warn(
'WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256.'
)
return verifier
}
const hashed = await sha256(verifier)
return base64urlencode(hashed)
}
See also
Favorite site
- Protecting Apps with PKCE - OAuth 2.0 Simplified
- Authorization Code Flow with Proof Key for Code Exchange (PKCE)
- Proof Key for Code Exchange Overview | Curity Identity Server