From b56764ce575c20a22509a5141007dfea97e7568d Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 1 May 2025 15:22:20 -0400 Subject: [PATCH] Update JWT schema (#632) ## Description: - Handle the new `sub` encoding - Decode the new `rol` claim - Related to https://github.com/openfrontio/infra/pull/25 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> --- src/client/ApiSchemas.ts | 22 +++++++++++++++++++++- src/client/Base64.ts | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/client/Base64.ts diff --git a/src/client/ApiSchemas.ts b/src/client/ApiSchemas.ts index b3aa8e8e6..e296d6c70 100644 --- a/src/client/ApiSchemas.ts +++ b/src/client/ApiSchemas.ts @@ -1,12 +1,32 @@ import { z } from "zod"; +import { base64urlToUuid } from "./Base64"; export const TokenPayloadSchema = z.object({ jti: z.string(), - sub: z.string().uuid(), + sub: z + .string() + .refine( + (val) => { + const uuid = base64urlToUuid(val); + return uuid != null; + }, + { + message: "Invalid base64-encoded UUID", + }, + ) + .transform((val) => { + const uuid = base64urlToUuid(val); + if (!uuid) throw new Error("Invalid base64 UUID"); + return uuid; + }), iat: z.number(), iss: z.string(), aud: z.string(), exp: z.number(), + rol: z + .string() + .optional() + .transform((val) => val.split(",")), }); export type TokenPayload = z.infer; diff --git a/src/client/Base64.ts b/src/client/Base64.ts new file mode 100644 index 000000000..2f5a776ff --- /dev/null +++ b/src/client/Base64.ts @@ -0,0 +1,37 @@ +import { base64url } from "jose"; + +/** + * Converts a UUID string to a base64url-encoded binary representation. + * @param uuid - The UUID string (e.g., '123e4567-e89b-12d3-a456-426614174000') + * @returns base64url string (e.g., 'Ej5FZ+i7EtOkVkJmFBdAAA') + */ +export function uuidToBase64url(uuid: string): string { + const hex = uuid.replace(/-/g, ""); + const bytes = new Uint8Array(16); + + for (let i = 0; i < 16; i++) { + bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); + } + + return base64url.encode(bytes); +} + +/** + * Converts a base64url-encoded binary UUID back to its canonical UUID string. + * @param encoded - base64url string (e.g., 'Ej5FZ+i7EtOkVkJmFBdAAA') + * @returns UUID string (e.g., '123e4567-e89b-12d3-a456-426614174000') + */ +export function base64urlToUuid(encoded: string): string { + const bytes = base64url.decode(encoded); + const hex = Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + return [ + hex.slice(0, 8), + hex.slice(8, 12), + hex.slice(12, 16), + hex.slice(16, 20), + hex.slice(20), + ].join("-"); +}