From 7659827e13fb5b4b75adb32d124c4f3073a1fb87 Mon Sep 17 00:00:00 2001 From: DanSnow Date: Mon, 22 Apr 2024 16:15:24 +0800 Subject: [PATCH] feat: add jwt utils --- .changeset/silent-moons-boil.md | 5 ++ .../karbon-utils/src/__tests__/jwt.spec.ts | 46 +++++++++++++++++++ packages/karbon-utils/src/jwt.ts | 24 ++++++++++ 3 files changed, 75 insertions(+) create mode 100644 .changeset/silent-moons-boil.md create mode 100644 packages/karbon-utils/src/__tests__/jwt.spec.ts create mode 100644 packages/karbon-utils/src/jwt.ts diff --git a/.changeset/silent-moons-boil.md b/.changeset/silent-moons-boil.md new file mode 100644 index 00000000..8e9b3606 --- /dev/null +++ b/.changeset/silent-moons-boil.md @@ -0,0 +1,5 @@ +--- +'@storipress/karbon-utils': minor +--- + +feat: add jwt utils diff --git a/packages/karbon-utils/src/__tests__/jwt.spec.ts b/packages/karbon-utils/src/__tests__/jwt.spec.ts new file mode 100644 index 00000000..b8ecfc10 --- /dev/null +++ b/packages/karbon-utils/src/__tests__/jwt.spec.ts @@ -0,0 +1,46 @@ +import '../crypto-polyfill' +import { describe, expect } from 'vitest' +import { fc, it } from '@fast-check/vitest' +import { createEncryptJWT, decryptJWT, textToUint8Array, uint8ArrayToText } from '../jwt' + +describe('createEncryptJWT', () => { + // Returns an encrypted JWT string when given a valid key and plaintext + it('should return an encrypted JWT string when given a valid key and plaintext', async () => { + const key = new Uint8Array(32) + const plaintext = 'Hello, world!' + const result = await createEncryptJWT(key, plaintext) + expect(typeof result).toBe('string') + }) + + // Returns an error when given a key of incorrect length + it('should return an error when given a key of incorrect length', async () => { + const key = new Uint8Array(16) + const plaintext = 'Hello, world!' + await expect(createEncryptJWT(key, plaintext)).rejects.toThrowError() + }) +}) + +describe('createEncryptJWT & decryptJWT', () => { + it.prop({ plaintext: fc.string(), key: fc.uint8Array({ minLength: 32, maxLength: 32 }) })( + 'can encrypt and decrypt', + async ({ plaintext, key }) => { + const result = await createEncryptJWT(key, plaintext) + + expect(typeof result).toBe('string') + + expect(result).not.toBe(plaintext) + + const decrypted = await decryptJWT(key, result) + + expect(decrypted).toBe(plaintext) + }, + ) +}) + +describe('textToUint8Array & uint8ArrayToText', () => { + it.prop({ text: fc.string() })('can convert text to and from uint8array', ({ text }) => { + const array = textToUint8Array(text) + const result = uint8ArrayToText(array) + expect(result).toBe(text) + }) +}) diff --git a/packages/karbon-utils/src/jwt.ts b/packages/karbon-utils/src/jwt.ts new file mode 100644 index 00000000..7688248b --- /dev/null +++ b/packages/karbon-utils/src/jwt.ts @@ -0,0 +1,24 @@ +import { CompactEncrypt, compactDecrypt } from '@storipress/jose-browser' + +export async function createEncryptJWT(key: Uint8Array, plaintext: string) { + const compactEncrypt = new CompactEncrypt(textToUint8Array(plaintext)).setProtectedHeader({ + enc: 'A256GCM', + alg: 'dir', + }) + return await compactEncrypt.encrypt(key) +} + +export async function decryptJWT(key: Uint8Array, jwt: string) { + const { plaintext } = await compactDecrypt(jwt, key) + return uint8ArrayToText(plaintext) +} + +export function textToUint8Array(text: string) { + const encoder = new TextEncoder() + return encoder.encode(text) +} + +export function uint8ArrayToText(array: Uint8Array) { + const decoder = new TextDecoder() + return decoder.decode(array) +}