diff --git a/apps/api/v1/lib/helpers/verifyApiKey.test.ts b/apps/api/v1/lib/helpers/verifyApiKey.test.ts index 1ec41081b8..bd8c404a04 100644 --- a/apps/api/v1/lib/helpers/verifyApiKey.test.ts +++ b/apps/api/v1/lib/helpers/verifyApiKey.test.ts @@ -139,6 +139,7 @@ describe("Verify API key - Unit Tests", () => { valid: true, userId: 1, user: { + uuid: "test-uuid-1", role: UserPermissionRole.ADMIN, locked: false, email: "admin@example.com", @@ -183,6 +184,7 @@ describe("Verify API key - Unit Tests", () => { valid: true, userId: 2, user: { + uuid: "test-uuid-2", role: UserPermissionRole.USER, locked: false, email: "org-admin@acme.com", @@ -227,6 +229,7 @@ describe("Verify API key - Unit Tests", () => { valid: true, userId: 3, user: { + uuid: "test-uuid-3", role: UserPermissionRole.USER, locked: true, email: "locked@example.com", diff --git a/apps/api/v1/lib/helpers/verifyApiKey.ts b/apps/api/v1/lib/helpers/verifyApiKey.ts index 804b21bce3..b71cdcab31 100644 --- a/apps/api/v1/lib/helpers/verifyApiKey.ts +++ b/apps/api/v1/lib/helpers/verifyApiKey.ts @@ -36,9 +36,10 @@ export const verifyApiKey: NextMiddleware = async (req, res, next) => { return res.status(401).json({ error: result.error }); } - // save the user id in the request for later use - req.userId = result.userId!; - req.user = result.user!; + // save the user id and uuid in the request for later use + req.userId = result.userId; + req.userUuid = result.user.uuid; + req.user = result.user; const { isAdmin, scope } = await isAdminGuard(req); const userIsLockedOrBlocked = await isLockedOrBlocked(req); diff --git a/apps/api/v1/next.d.ts b/apps/api/v1/next.d.ts index 6816e97e4f..3c4f4ac536 100644 --- a/apps/api/v1/next.d.ts +++ b/apps/api/v1/next.d.ts @@ -8,6 +8,7 @@ export declare module "next" { session?: Session | null; userId: number; + userUuid: string; user?: { role: string; locked: boolean; email: string } | null; method: string; // session: { user: { id: number } }; diff --git a/apps/api/v1/pages/api/invites/_post.ts b/apps/api/v1/pages/api/invites/_post.ts index e23595939b..67340c8e68 100644 --- a/apps/api/v1/pages/api/invites/_post.ts +++ b/apps/api/v1/pages/api/invites/_post.ts @@ -22,6 +22,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { return { user: { id: req.userId, + uuid: req.userUuid, username: "", profile: { id: null, diff --git a/apps/api/v1/pages/api/teams/[teamId]/publish.ts b/apps/api/v1/pages/api/teams/[teamId]/publish.ts index bf3ea71df6..665d5bd391 100644 --- a/apps/api/v1/pages/api/teams/[teamId]/publish.ts +++ b/apps/api/v1/pages/api/teams/[teamId]/publish.ts @@ -22,6 +22,7 @@ const patchHandler = async (req: NextApiRequest, res: NextApiResponse) => { return { user: { id: req.userId, + uuid: req.userUuid, username: "" /* Not used in this context */, role: req.isSystemWideAdmin ? UserPermissionRole.ADMIN : UserPermissionRole.USER, profile: { diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrg.test.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrg.test.ts index ecc2f9eece..2fc5361f77 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrg.test.ts +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrg.test.ts @@ -38,6 +38,7 @@ describe("validateUserHasOrg", () => { hasValidLicense: true, user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { @@ -87,6 +88,7 @@ describe("validateUserHasOrg", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, @@ -132,6 +134,7 @@ describe("validateUserHasOrg", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, @@ -159,6 +162,7 @@ describe("validateUserHasOrg", () => { const mockSession = createMockSession({ user: { id: undefined as unknown as number, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrgAdmin.test.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrgAdmin.test.ts index 664fc02fac..65e523ff03 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrgAdmin.test.ts +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/actions/validateUserHasOrgAdmin.test.ts @@ -44,6 +44,7 @@ describe("validateUserHasOrgAdmin", () => { hasValidLicense: true, user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { @@ -95,6 +96,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { @@ -136,6 +138,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, @@ -187,6 +190,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, @@ -216,6 +220,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { @@ -260,6 +265,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, @@ -324,6 +330,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: { @@ -366,6 +373,7 @@ describe("validateUserHasOrgAdmin", () => { const mockSession = createMockSession({ user: { id: 123, + uuid: "test-uuid-123", email: "test@example.com", name: "Test User", org: undefined, diff --git a/apps/web/app/api/link/route.ts b/apps/web/app/api/link/route.ts index d105ee3b28..36113b72bc 100644 --- a/apps/web/app/api/link/route.ts +++ b/apps/web/app/api/link/route.ts @@ -37,10 +37,11 @@ const decryptedSchema = z.object({ }); // Move the sessionGetter function outside the GET function -const createSessionGetter = (userId: number) => async () => { +const createSessionGetter = (userId: number, userUuid: string) => async () => { return { user: { id: userId, + uuid: userUuid, username: "" /* Not used in this context */, role: UserPermissionRole.USER, /* Not used in this context */ @@ -85,13 +86,14 @@ async function handler(request: NextRequest) { }); // Use the factory function instead of declaring inside the block - const sessionGetter = createSessionGetter(userId); + const sessionGetter = createSessionGetter(userId, user.uuid); try { /** @see https://trpc.io/docs/server-side-calls */ // Create a legacy request object for compatibility const legacyReq = buildLegacyRequest(await headers(), await cookies()); - const res = {} as any; // Response is still mocked as it's not used in this context + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Response is mocked as it's not used in this context + const res = {} as any; const ctx = await createContext({ req: legacyReq, res }, sessionGetter); const createCaller = createCallerFactory(bookingsRouter); diff --git a/apps/web/app/api/verify-booking-token/route.ts b/apps/web/app/api/verify-booking-token/route.ts index 642b4fc00e..c0401168b2 100644 --- a/apps/web/app/api/verify-booking-token/route.ts +++ b/apps/web/app/api/verify-booking-token/route.ts @@ -96,6 +96,7 @@ async function handleBookingAction( return { user: { id: Number(userId), + uuid: user.uuid, username: "" /* Not used in this context */, role: UserPermissionRole.USER, /* Not used in this context */ @@ -118,7 +119,9 @@ async function handleBookingAction( const createCaller = createCallerFactory(bookingsRouter); // Use buildLegacyRequest to create a request object compatible with Pages Router + const legacyReq = request ? buildLegacyRequest(await headers(), await cookies()) : ({} as any); + const res = {} as any; const ctx = await createContext({ req: legacyReq, res }, sessionGetter); diff --git a/packages/features/auth/lib/getServerSession.ts b/packages/features/auth/lib/getServerSession.ts index 1fab94ef10..69fd6b063b 100644 --- a/packages/features/auth/lib/getServerSession.ts +++ b/packages/features/auth/lib/getServerSession.ts @@ -96,6 +96,7 @@ export async function getServerSession(options: { expires: new Date(typeof token.exp === "number" ? token.exp * 1000 : Date.now()).toISOString(), user: { id: user.id, + uuid: user.uuid, name: user.name, username: user.username, email: user.email, diff --git a/packages/features/auth/lib/next-auth-custom-adapter.ts b/packages/features/auth/lib/next-auth-custom-adapter.ts index 757349eaf9..7b75622e29 100644 --- a/packages/features/auth/lib/next-auth-custom-adapter.ts +++ b/packages/features/auth/lib/next-auth-custom-adapter.ts @@ -14,6 +14,7 @@ const parseIntSafe = (id: string | number): number => { // Simple utility functions for transformations const toAdapterUser = (user: User): AdapterUser => ({ id: user.id.toString(), + uuid: user.uuid, name: user.name, email: user.email, emailVerified: user.emailVerified, diff --git a/packages/features/ee/api-keys/repositories/PrismaApiKeyRepository.ts b/packages/features/ee/api-keys/repositories/PrismaApiKeyRepository.ts index 9482ab59d3..35fa5c024a 100644 --- a/packages/features/ee/api-keys/repositories/PrismaApiKeyRepository.ts +++ b/packages/features/ee/api-keys/repositories/PrismaApiKeyRepository.ts @@ -21,6 +21,7 @@ export class PrismaApiKeyRepository { expiresAt: true, user: { select: { + uuid: true, role: true, locked: true, email: true, diff --git a/packages/features/ee/api-keys/services/ApiKeyService.ts b/packages/features/ee/api-keys/services/ApiKeyService.ts index c1d175443c..2853359ce0 100644 --- a/packages/features/ee/api-keys/services/ApiKeyService.ts +++ b/packages/features/ee/api-keys/services/ApiKeyService.ts @@ -4,19 +4,22 @@ type Deps = { apiKeyRepo: PrismaApiKeyRepository; }; -interface VerifyKeyResult { - valid: boolean; - error?: string; - userId?: number; - user?: { +type VerifyKeyResult = { + valid: true; + userId: number; + user: { + uuid: string; role: string; locked: boolean; email: string; }; +} | { + valid: false; + error: string; } export class ApiKeyService { - constructor(private readonly deps: Deps) {} + constructor(private readonly deps: Deps) { } async verifyKeyByHashedKey(hashedKey: string): Promise { const apiKey = await this.deps.apiKeyRepo.findByHashedKey(hashedKey); diff --git a/packages/features/users/repositories/UserRepository.ts b/packages/features/users/repositories/UserRepository.ts index 073b85b53d..6af638fa99 100644 --- a/packages/features/users/repositories/UserRepository.ts +++ b/packages/features/users/repositories/UserRepository.ts @@ -339,6 +339,7 @@ export class UserRepository { locked: true, role: true, id: true, + uuid: true, username: true, name: true, email: true, diff --git a/packages/types/next-auth.d.ts b/packages/types/next-auth.d.ts index 4dd1cc4d76..769858fea7 100644 --- a/packages/types/next-auth.d.ts +++ b/packages/types/next-auth.d.ts @@ -13,11 +13,12 @@ declare module "next-auth" { hasValidLicense: boolean; profileId?: number | null; upId: string; - user: User; + user: User & { uuid: PrismaUser["uuid"] }; } interface User extends Omit { id: PrismaUser["id"]; + uuid?: PrismaUser["uuid"]; emailVerified?: PrismaUser["emailVerified"]; email_verified?: boolean; completedOnboarding?: boolean; diff --git a/tests/libs/mockTRPCContext.ts b/tests/libs/mockTRPCContext.ts index 840bd64fbe..e9c2359510 100644 --- a/tests/libs/mockTRPCContext.ts +++ b/tests/libs/mockTRPCContext.ts @@ -12,6 +12,7 @@ const sessionGetter: GetSessionFn = () => email: "", image: "", id: 1, + uuid: "test-uuid-mock-context", username: "" /* Not used in this context */, role: UserPermissionRole.USER, profile: {