Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/core/auth-js/src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,12 @@ export function retryable<T>(
}

function dec2hex(dec: number) {
return ('0' + dec.toString(16)).substr(-2)
return ('0' + dec.toString(16)).slice(-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
Expand All @@ -275,6 +274,11 @@ export function generatePKCEVerifier() {
}
return verifier
}
// Each byte maps to two hex characters, so a 56-byte buffer yields a
// 112-character verifier (within RFC 7636's 43-128 range). A Uint8Array
// requests exactly the entropy consumed; a Uint32Array would draw 4x the
// bytes and discard three of every four.
const array = new Uint8Array(verifierLength)
crypto.getRandomValues(array)
return Array.from(array, dec2hex).join('')
}
Expand Down
19 changes: 19 additions & 0 deletions packages/core/auth-js/test/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
decodeJWT,
generateCallbackId,
generatePKCEFlowId,
generatePKCEVerifier,
getAlgorithm,
getItemAsync,
parseParametersFromURL,
Expand Down Expand Up @@ -275,6 +276,24 @@ describe('getAlgorithm', () => {
})
})

describe('generatePKCEVerifier', () => {
it('should generate a verifier within the RFC 7636 length range', () => {
const verifier = generatePKCEVerifier()
expect(verifier.length).toBeGreaterThanOrEqual(43)
expect(verifier.length).toBeLessThanOrEqual(128)
})

it('should only use RFC 7636 unreserved characters', () => {
const verifier = generatePKCEVerifier()
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
expect(verifier).toMatch(/^[A-Za-z0-9\-._~]+$/)
})

it('should generate a unique verifier on each call', () => {
expect(generatePKCEVerifier()).not.toEqual(generatePKCEVerifier())
})
})

describe('getCodeChallengeAndMethod', () => {
const testCases = [
{
Expand Down