From c0d0ecf19833633ca54e945659fd6be94333852c Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Mon, 27 Jul 2026 11:28:32 -0700 Subject: [PATCH] feat(schema): declare user relation on verification tokens Keys have always referenced their owner by a bare user_id string with no declared relation, so the database could not enforce integrity and no query could join keys to users. This adds the litellm_user_table relation on LiteLLM_VerificationToken (FK on user_id, ON DELETE SET NULL, matching the table's existing budget, organization and project FKs) and a keys back-relation on LiteLLM_UserTable. The migration first nulls user_ids pointing at users that no longer exist; the constraint cannot be added over them, and SET NULL would have produced the same rows had it existed when those users were deleted. The user-delete endpoint already deletes the user's keys itself, so the FK changes no runtime behavior; it formalizes the link and unlocks relation queries and joins for future work --- .../migration.sql | 15 +++++++++++++++ litellm/proxy/schema.prisma | 2 ++ schema.prisma | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20260727000000_add_verification_token_user_fkey/migration.sql diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20260727000000_add_verification_token_user_fkey/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260727000000_add_verification_token_user_fkey/migration.sql new file mode 100644 index 00000000000..fed756cd334 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20260727000000_add_verification_token_user_fkey/migration.sql @@ -0,0 +1,15 @@ +-- Null out user_ids that reference users that no longer exist; the foreign key +-- below cannot be added over them, and ON DELETE SET NULL would have produced +-- the same rows had the constraint existed when those users were deleted +UPDATE "LiteLLM_VerificationToken" vt +SET "user_id" = NULL +WHERE vt."user_id" IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM "LiteLLM_UserTable" u WHERE u."user_id" = vt."user_id"); + +-- AddForeignKey +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'LiteLLM_VerificationToken_user_id_fkey') THEN + ALTER TABLE "LiteLLM_VerificationToken" ADD CONSTRAINT "LiteLLM_VerificationToken_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "LiteLLM_UserTable"("user_id") ON DELETE SET NULL ON UPDATE CASCADE; + END IF; +END $$; diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 6713b212314..c336e482bfc 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -265,6 +265,7 @@ model LiteLLM_UserTable { invitations_updated LiteLLM_InvitationLink[] @relation("UpdatedBy") invitations_user LiteLLM_InvitationLink[] @relation("UserId") object_permission LiteLLM_ObjectPermissionTable? @relation(fields: [object_permission_id], references: [object_permission_id]) + keys LiteLLM_VerificationToken[] } model LiteLLM_ObjectPermissionTable { @@ -462,6 +463,7 @@ model LiteLLM_VerificationToken { litellm_budget_table LiteLLM_BudgetTable? @relation(fields: [budget_id], references: [budget_id]) litellm_organization_table LiteLLM_OrganizationTable? @relation(fields: [organization_id], references: [organization_id]) litellm_project_table LiteLLM_ProjectTable? @relation(fields: [project_id], references: [project_id]) + litellm_user_table LiteLLM_UserTable? @relation(fields: [user_id], references: [user_id]) object_permission LiteLLM_ObjectPermissionTable? @relation(fields: [object_permission_id], references: [object_permission_id]) jwt_key_mappings LiteLLM_JWTKeyMapping[] diff --git a/schema.prisma b/schema.prisma index 6713b212314..c336e482bfc 100644 --- a/schema.prisma +++ b/schema.prisma @@ -265,6 +265,7 @@ model LiteLLM_UserTable { invitations_updated LiteLLM_InvitationLink[] @relation("UpdatedBy") invitations_user LiteLLM_InvitationLink[] @relation("UserId") object_permission LiteLLM_ObjectPermissionTable? @relation(fields: [object_permission_id], references: [object_permission_id]) + keys LiteLLM_VerificationToken[] } model LiteLLM_ObjectPermissionTable { @@ -462,6 +463,7 @@ model LiteLLM_VerificationToken { litellm_budget_table LiteLLM_BudgetTable? @relation(fields: [budget_id], references: [budget_id]) litellm_organization_table LiteLLM_OrganizationTable? @relation(fields: [organization_id], references: [organization_id]) litellm_project_table LiteLLM_ProjectTable? @relation(fields: [project_id], references: [project_id]) + litellm_user_table LiteLLM_UserTable? @relation(fields: [user_id], references: [user_id]) object_permission LiteLLM_ObjectPermissionTable? @relation(fields: [object_permission_id], references: [object_permission_id]) jwt_key_mappings LiteLLM_JWTKeyMapping[]