Last active
May 24, 2018 09:34
-
-
Save romainl/8577c726a6ccddc7a892033dbd4ee9ff to your computer and use it in GitHub Desktop.
Generate a best effort cryptographically secure string of arbitrary length for use as a Proof Key for Code Exchange (PKCE)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See https://www.oauth.com/oauth2-servers/pkce/ | |
function generateCodeVerifier(limit) { | |
const krypto = window.crypto || window.msCrypto; | |
const pool = new Uint8Array(1024); | |
const chars = []; | |
krypto.getRandomValues(pool); | |
pool | |
.filter((code) => { | |
return code === 45 || code === 46 || code === 95 || code === 126 || (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122); | |
}) | |
.forEach((code) => { | |
chars.push(String.fromCharCode(code)); | |
}); | |
return chars.slice(0, limit).join(''); | |
} | |
function generateCodeChallenge(string) { | |
// http://www.simplycalc.com/base64-source.php | |
const base64url_encode = (string) => { | |
const b64u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; | |
const b64pad = '='; | |
const base64_encode_data = (data, len, b64x) => { | |
let dst = ''; | |
let i; | |
for (i = 0; i <= len - 3; i += 3) { | |
dst += b64x.charAt(data.charCodeAt(i) >>> 2); | |
dst += b64x.charAt(((data.charCodeAt(i) & 3) << 4) | (data.charCodeAt(i+1) >>> 4)); | |
dst += b64x.charAt(((data.charCodeAt(i+1) & 15) << 2) | (data.charCodeAt(i+2) >>> 6)); | |
dst += b64x.charAt(data.charCodeAt(i+2) & 63); | |
} | |
if (len % 3 === 2) { | |
dst += b64x.charAt(data.charCodeAt(i) >>> 2); | |
dst += b64x.charAt(((data.charCodeAt(i) & 3) << 4) | (data.charCodeAt(i+1) >>> 4)); | |
dst += b64x.charAt(((data.charCodeAt(i+1) & 15) << 2)); | |
dst += b64pad; | |
} else if (len % 3 === 1) { | |
dst += b64x.charAt(data.charCodeAt(i) >>> 2); | |
dst += b64x.charAt(((data.charCodeAt(i) & 3) << 4)); | |
dst += b64pad; | |
dst += b64pad; | |
} | |
return dst | |
} | |
const utf8str = unescape(encodeURIComponent(string)); | |
return base64_encode_data(utf8str, utf8str.length, b64u); | |
}; | |
const hex = (buffer) => { | |
const hexCodes = []; | |
const view = new DataView(buffer); | |
for (let i = 0; i < view.byteLength; i += 4) { | |
const stringValue = view.getUint32(i).toString(16); | |
const padding = '00000000'; | |
hexCodes.push((padding + stringValue).slice(-padding.length)); | |
} | |
return hexCodes.join(''); | |
}; | |
const buffer = new TextEncoder('utf-8').encode(string); | |
return crypto.subtle.digest('SHA-256', buffer) | |
.then(hash => base64url_encode(hex(hash))); | |
} | |
var verifier = generateCodeVerifier(128); | |
console.log('verifier', verifier); | |
generateCodeChallenge(verifier) | |
.then((challenge) => { | |
console.log('challenge', challenge); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment