-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
113 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { describe, expect, test } from 'vitest' | ||
import { | ||
toSha256, | ||
toBase64Url, | ||
createRandomString, | ||
createPKCECodeVerifier, | ||
} from '../pkce' | ||
|
||
describe('createRandomString', () => { | ||
test('throws error on 0 length', () => { | ||
expect(() => createRandomString(0)) | ||
.toThrow('length must be greater than 0') | ||
}) | ||
|
||
test('creates string with length', () => { | ||
const randomString43 = createRandomString(43) | ||
const randomString128 = createRandomString(128) | ||
|
||
expect(randomString43).toHaveLength(43) | ||
expect(randomString128).toHaveLength(128) | ||
}) | ||
|
||
test('creates unique strings', () => { | ||
const randomString1 = createRandomString(43) | ||
const randomString2 = createRandomString(43) | ||
|
||
expect(randomString1).not.toEqual(randomString2) | ||
console.log(randomString1) | ||
}) | ||
}) | ||
|
||
describe('toSha256', () => { | ||
test('throws error on empty string', async () => { | ||
await expect(toSha256('')).rejects.toThrow('data is required') | ||
}) | ||
|
||
test('hashes correctly', async () => { | ||
const hash = await toSha256('hello') | ||
const base64url = toBase64Url(hash) | ||
|
||
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= | ||
// but without + and = | ||
expect(base64url).toEqual('LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ') | ||
}) | ||
}) | ||
|
||
describe('toBase64Url', () => { | ||
test('converts to base64url', () => { | ||
const base64url = toBase64Url([1, 2, 3, 4, 5]) | ||
|
||
expect(base64url).toEqual('AQIDBAU') | ||
}) | ||
}) | ||
|
||
describe('createPKCECodeVerifier', () => { | ||
test('creates code challenge', async () => { | ||
const codeVerifier = createRandomString(43) | ||
const codeChallenge = await createPKCECodeVerifier(codeVerifier) | ||
|
||
expect(codeChallenge).toHaveLength(43) | ||
}) | ||
|
||
test('creates unique code challenge', async () => { | ||
const codeVerifier = createRandomString(43) | ||
const codeChallenge1 = await createPKCECodeVerifier(codeVerifier) | ||
const codeChallenge2 = await createPKCECodeVerifier(codeVerifier) | ||
|
||
expect(codeChallenge1).toEqual(codeChallenge2) | ||
}) | ||
}) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Sha256 } from '@aws-crypto/sha256-js'; | ||
|
||
export const toSha256 = async (data: string): Promise<Uint8Array> => { | ||
if (!data) throw new Error('data is required'); | ||
|
||
const hash = new Sha256(); | ||
hash.update(data); | ||
|
||
const hashed = await hash.digest(); | ||
return hashed; | ||
} | ||
|
||
// refer to base64url-encoding in RFC 7636 | ||
// https://datatracker.ietf.org/doc/html/rfc7636#appendix-A | ||
export const toBase64Url = (bytes: Uint8Array) => { | ||
const charCodes = Array.from(bytes); | ||
let str = btoa(String.fromCharCode.apply(null, charCodes)); | ||
str = str.split('=')[0]; | ||
str = str.replace(/\+/g, '-'); | ||
str = str.replace(/\//g, '_'); | ||
return str; | ||
} | ||
|
||
// refer to random string generation in RFC 7636 | ||
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 | ||
export const createRandomString = (length: number = 34): string => { | ||
if (length === 0) throw new Error('length must be greater than 0'); | ||
|
||
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; | ||
let randomString = ''; | ||
for (let i = 0; i < length; i++) { | ||
const randomIndex = Math.floor(Math.random() * charset.length); | ||
randomString += charset[randomIndex]; | ||
} | ||
return randomString; | ||
} | ||
|
||
export const createPKCECodeVerifier = async (str: string): string => { | ||
const hashed: Uint8Array = await toSha256(str) | ||
const codeVerifier = toBase64Url(hashed) | ||
return codeVerifier; | ||
} | ||
|