From 8349f2b861347b344c474f828808fcbe5d3e680f Mon Sep 17 00:00:00 2001 From: Sebastian Pietras Date: Tue, 21 Jul 2026 19:46:32 +0200 Subject: [PATCH] Improved parsing user data from headers --- src/package-lock.json.jinja | 13 ++++++++ src/package.json.jinja | 1 + .../lib/get-user/main.ts | 6 ++-- .../lib/get-user/schemas.ts | 5 --- .../lib/get-user/utils.ts | 32 +++++++++++++++++++ 5 files changed, 49 insertions(+), 8 deletions(-) delete mode 100644 src/src/server/{% if identity %}identity{% endif %}/lib/get-user/schemas.ts create mode 100644 src/src/server/{% if identity %}identity{% endif %}/lib/get-user/utils.ts diff --git a/src/package-lock.json.jinja b/src/package-lock.json.jinja index 15a2a6d..7478b5a 100644 --- a/src/package-lock.json.jinja +++ b/src/package-lock.json.jinja @@ -23,6 +23,7 @@ "client-only": "~0.0", "dayjs": "~1.11", "es-toolkit": "~1.43", + "flat": "~6.0", "next": "16.1.5", "react": "~19.2", "react-dom": "~19.2", @@ -6633,6 +6634,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-6.0.1.tgz", + "integrity": "sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", diff --git a/src/package.json.jinja b/src/package.json.jinja index a19e93c..8691f5b 100644 --- a/src/package.json.jinja +++ b/src/package.json.jinja @@ -29,6 +29,7 @@ "client-only": "~0.0", "dayjs": "~1.11", "es-toolkit": "~1.43", + "flat": "~6.0", "next": "16.1.5", "react": "~19.2", "react-dom": "~19.2", diff --git a/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/main.ts b/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/main.ts index b94a08f..aed7bd1 100644 --- a/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/main.ts +++ b/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/main.ts @@ -1,13 +1,13 @@ import type { GetUserInput, GetUserOutput } from "./types"; -import { Schemas } from "./schemas"; +import { parseUserFromHeaders } from "./utils"; export async function getUser({ headers, }: GetUserInput): Promise { - const id = await Schemas.Id.safeParseAsync(headers.get("X-User-ID")); + const result = await parseUserFromHeaders(headers); - const user = id.success ? { id: id.data } : null; + const user = result.success ? result.data : null; return { user: user }; } diff --git a/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/schemas.ts b/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/schemas.ts deleted file mode 100644 index f3a0d48..0000000 --- a/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/schemas.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IdentitySchemas } from "../../../../common/identity/schemas"; - -export const Schemas = { - Id: IdentitySchemas.User.shape.id, -}; diff --git a/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/utils.ts b/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/utils.ts new file mode 100644 index 0000000..8f61077 --- /dev/null +++ b/src/src/server/{% if identity %}identity{% endif %}/lib/get-user/utils.ts @@ -0,0 +1,32 @@ +import { isJSONArray, isJSONObject } from "es-toolkit/predicate"; +import { unflatten } from "flat"; + +import { IdentitySchemas } from "../../../../common/identity/schemas"; + +export function parseValue(value: string) { + try { + const parsed = JSON.parse(value) as unknown; + return isJSONObject(parsed) || isJSONArray(parsed) ? parsed : value; + } catch { + return value; + } +} + +export async function parseUserFromHeaders(headers: Headers) { + const delimiter = "."; + + const flat = Object.fromEntries( + headers + .entries() + .map(([key, value]) => [key.toLowerCase(), value] as [string, string]) + .filter(([key]) => key === "x-user" || key.startsWith("x-user-")) + .map(([key, value]) => [ + key.replace(/^x-/, "").replaceAll("-", "."), + parseValue(value), + ]), + ); + + const raw = unflatten(flat, { delimiter: delimiter }); + const user = (raw as { user?: unknown }).user; + return await IdentitySchemas.User.safeParseAsync(user); +}