Skip to content

Instantly share code, notes, and snippets.

@romainl
Last active May 24, 2018 09:34
Show Gist options
  • Save romainl/8577c726a6ccddc7a892033dbd4ee9ff to your computer and use it in GitHub Desktop.
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)
// 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