60 lines
2.1 KiB
JavaScript
60 lines
2.1 KiB
JavaScript
import { bufferToBase64, base64ToBase64Url } from '~/authentication/webauthn/util';
|
|
import { PKCE_CODE_CHALLENGE_DIGEST_ALGORITHM } from './constants';
|
|
|
|
// PKCE codeverifier should have a maximum length of 128 characters.
|
|
// Using 96 bytes generates a string of 128 characters.
|
|
// RFC: https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
|
|
export const CODE_VERIFIER_BYTES = 96;
|
|
|
|
/**
|
|
* Generate a cryptographically random string.
|
|
* @param {Number} lengthBytes
|
|
* @returns {String} a random string
|
|
*/
|
|
function getRandomString(lengthBytes) {
|
|
// generate random values and load them into byteArray.
|
|
const byteArray = new Uint8Array(lengthBytes);
|
|
window.crypto.getRandomValues(byteArray);
|
|
|
|
// Convert array to string
|
|
const randomString = bufferToBase64(byteArray);
|
|
return randomString;
|
|
}
|
|
|
|
/**
|
|
* Creates a code verifier to be used for OAuth PKCE authentication.
|
|
* The code verifier has 128 characters.
|
|
*
|
|
* RFC: https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
|
|
* @returns {String} code verifier
|
|
*/
|
|
export function createCodeVerifier() {
|
|
const verifier = getRandomString(CODE_VERIFIER_BYTES);
|
|
return base64ToBase64Url(verifier);
|
|
}
|
|
|
|
/**
|
|
* Creates a code challenge for OAuth PKCE authentication.
|
|
* The code challenge is derived from the given [codeVerifier].
|
|
* [codeVerifier] is tranformed in the following way (as per the RFC):
|
|
* code_challenge = BASE64URL-ENCODE(SHA256(ASCII(codeVerifier)))
|
|
*
|
|
* RFC: https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
|
* @param {String} codeVerifier
|
|
* @returns {String} code challenge
|
|
*/
|
|
export async function createCodeChallenge(codeVerifier) {
|
|
// Generate SHA-256 digest of the [codeVerifier]
|
|
const buffer = new TextEncoder().encode(codeVerifier);
|
|
const digestArrayBuffer = await window.crypto.subtle.digest(
|
|
PKCE_CODE_CHALLENGE_DIGEST_ALGORITHM.long,
|
|
buffer,
|
|
);
|
|
|
|
// Convert digest to a Base64URL-encoded string
|
|
const digestHash = bufferToBase64(digestArrayBuffer);
|
|
// Escape string to remove reserved charaters
|
|
const codeChallenge = base64ToBase64Url(digestHash);
|
|
|
|
return codeChallenge;
|
|
}
|