diff --git a/apps/backend/src/app/api/latest/auth/otp/sign-in/verification-code-handler.tsx b/apps/backend/src/app/api/latest/auth/otp/sign-in/verification-code-handler.tsx index 7167d4fb1..2072093cf 100644 --- a/apps/backend/src/app/api/latest/auth/otp/sign-in/verification-code-handler.tsx +++ b/apps/backend/src/app/api/latest/auth/otp/sign-in/verification-code-handler.tsx @@ -1,4 +1,5 @@ import { sendEmailFromTemplate } from "@/lib/emails"; +import { getSoleTenancyFromProject } from "@/lib/tenancies"; import { createAuthTokens } from "@/lib/tokens"; import { createVerificationCodeHandler } from "@/route-handlers/verification-code-handler"; import { VerificationCodeType } from "@prisma/client"; @@ -36,8 +37,9 @@ export const signInVerificationCodeHandler = createVerificationCodeHandler({ body: signInResponseSchema.defined(), }), async send(codeObj, createOptions, sendOptions: { email: string }) { + const tenancy = await getSoleTenancyFromProject(createOptions.project.id); await sendEmailFromTemplate({ - tenancy: createOptions.tenancy, + tenancy, email: createOptions.method.email, user: null, templateType: "magic_link", @@ -80,14 +82,15 @@ export const signInVerificationCodeHandler = createVerificationCodeHandler({ if (user.requires_totp_mfa) { throw await createMfaRequiredError({ - tenancy, + project: tenancy.project, + branchId: tenancy.branchId, isNewUser: data.is_new_user, userId: user.id, }); } const { refreshToken, accessToken } = await createAuthTokens({ - tenancyId: tenancy.id, + tenancy, projectUserId: user.id, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, }); diff --git a/apps/backend/src/app/api/latest/auth/passkey/sign-in/verification-code-handler.tsx b/apps/backend/src/app/api/latest/auth/passkey/sign-in/verification-code-handler.tsx index 4ec7c2710..a735a26ec 100644 --- a/apps/backend/src/app/api/latest/auth/passkey/sign-in/verification-code-handler.tsx +++ b/apps/backend/src/app/api/latest/auth/passkey/sign-in/verification-code-handler.tsx @@ -126,14 +126,15 @@ export const passkeySignInVerificationCodeHandler = createVerificationCodeHandle if (user.requiresTotpMfa) { throw await createMfaRequiredError({ - tenancy, + project: tenancy.project, + branchId: tenancy.branchId, isNewUser: false, userId: user.projectUserId, }); } const { refreshToken, accessToken } = await createAuthTokens({ - tenancyId: tenancy.id, + tenancy, projectUserId: user.projectUserId, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, }); diff --git a/apps/backend/src/app/api/latest/auth/password/reset/verification-code-handler.tsx b/apps/backend/src/app/api/latest/auth/password/reset/verification-code-handler.tsx index e79f2cad4..3e88f1ff7 100644 --- a/apps/backend/src/app/api/latest/auth/password/reset/verification-code-handler.tsx +++ b/apps/backend/src/app/api/latest/auth/password/reset/verification-code-handler.tsx @@ -1,4 +1,5 @@ import { sendEmailFromTemplate } from "@/lib/emails"; +import { getSoleTenancyFromProject } from "@/lib/tenancies"; import { createVerificationCodeHandler } from "@/route-handlers/verification-code-handler"; import { VerificationCodeType } from "@prisma/client"; import { KnownErrors } from "@stackframe/stack-shared"; @@ -35,8 +36,9 @@ export const resetPasswordVerificationCodeHandler = createVerificationCodeHandle bodyType: yupString().oneOf(["success"]).defined(), }), async send(codeObj, createOptions, sendOptions: { user: UsersCrud["Admin"]["Read"] }) { + const tenancy = await getSoleTenancyFromProject(createOptions.project.id); await sendEmailFromTemplate({ - tenancy: createOptions.tenancy, + tenancy, user: sendOptions.user, email: createOptions.method.email, templateType: "password_reset", diff --git a/apps/backend/src/app/api/latest/auth/password/sign-in/route.tsx b/apps/backend/src/app/api/latest/auth/password/sign-in/route.tsx index 5ce11aadd..a2dd78e3e 100644 --- a/apps/backend/src/app/api/latest/auth/password/sign-in/route.tsx +++ b/apps/backend/src/app/api/latest/auth/password/sign-in/route.tsx @@ -60,14 +60,15 @@ export const POST = createSmartRouteHandler({ if (contactChannel.projectUser.requiresTotpMfa) { throw await createMfaRequiredError({ - tenancy, + project: tenancy.project, + branchId: tenancy.branchId, isNewUser: false, userId: contactChannel.projectUser.projectUserId, }); } const { refreshToken, accessToken } = await createAuthTokens({ - tenancyId: tenancy.id, + tenancy, projectUserId: contactChannel.projectUser.projectUserId, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, }); diff --git a/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx b/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx index 62e0eefb1..571fd5f8e 100644 --- a/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx +++ b/apps/backend/src/app/api/latest/auth/password/sign-up/route.tsx @@ -85,14 +85,15 @@ export const POST = createSmartRouteHandler({ if (createdUser.requires_totp_mfa) { throw await createMfaRequiredError({ - tenancy, + project: tenancy.project, + branchId: tenancy.branchId, isNewUser: true, userId: createdUser.id, }); } const { refreshToken, accessToken } = await createAuthTokens({ - tenancyId: tenancy.id, + tenancy, projectUserId: createdUser.id, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, }); diff --git a/apps/backend/src/app/api/latest/auth/sessions/current/refresh/route.tsx b/apps/backend/src/app/api/latest/auth/sessions/current/refresh/route.tsx index 30541a206..c7736116e 100644 --- a/apps/backend/src/app/api/latest/auth/sessions/current/refresh/route.tsx +++ b/apps/backend/src/app/api/latest/auth/sessions/current/refresh/route.tsx @@ -43,7 +43,7 @@ export const POST = createSmartRouteHandler({ } const accessToken = await generateAccessToken({ - tenancyId: sessionObj.tenancyId, + tenancy, userId: sessionObj.projectUserId, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, }); diff --git a/apps/backend/src/app/api/latest/auth/sessions/route.tsx b/apps/backend/src/app/api/latest/auth/sessions/route.tsx index 83ed9048f..807644045 100644 --- a/apps/backend/src/app/api/latest/auth/sessions/route.tsx +++ b/apps/backend/src/app/api/latest/auth/sessions/route.tsx @@ -44,7 +44,7 @@ export const POST = createSmartRouteHandler({ } const { refreshToken, accessToken } = await createAuthTokens({ - tenancyId: tenancy.id, + tenancy, projectUserId: user.id, useLegacyGlobalJWT: tenancy.config.legacy_global_jwt_signing, expiresAt: new Date(Date.now() + expiresInMillis), diff --git a/apps/backend/src/app/api/latest/contact-channels/verify/verification-code-handler.tsx b/apps/backend/src/app/api/latest/contact-channels/verify/verification-code-handler.tsx index 6ee521fc0..fd973e8cc 100644 --- a/apps/backend/src/app/api/latest/contact-channels/verify/verification-code-handler.tsx +++ b/apps/backend/src/app/api/latest/contact-channels/verify/verification-code-handler.tsx @@ -1,4 +1,5 @@ import { sendEmailFromTemplate } from "@/lib/emails"; +import { getSoleTenancyFromProject } from "@/lib/tenancies"; import { prismaClient } from "@/prisma-client"; import { createVerificationCodeHandler } from "@/route-handlers/verification-code-handler"; import { VerificationCodeType } from "@prisma/client"; @@ -30,8 +31,10 @@ export const contactChannelVerificationCodeHandler = createVerificationCodeHandl bodyType: yupString().oneOf(["success"]).defined(), }), async send(codeObj, createOptions, sendOptions: { user: UsersCrud["Admin"]["Read"] }) { + const tenancy = await getSoleTenancyFromProject(createOptions.project.id); + await sendEmailFromTemplate({ - tenancy: createOptions.tenancy, + tenancy, user: sendOptions.user, email: createOptions.method.email, templateType: "email_verification", diff --git a/apps/backend/src/app/api/latest/integrations/neon/api-keys/route.tsx b/apps/backend/src/app/api/latest/integrations/neon/api-keys/route.tsx index 955fa6744..8182a709e 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/api-keys/route.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/api-keys/route.tsx @@ -13,7 +13,7 @@ export const POST = createSmartRouteHandler({ request: yupObject({ auth: yupObject({ type: adminAuthTypeSchema, - tenancy: adaptSchema.defined(), + project: adaptSchema.defined(), }).defined(), body: yupObject({ description: yupString().defined(), @@ -40,7 +40,7 @@ export const POST = createSmartRouteHandler({ }), handler: async ({ auth, body }) => { const set = await createApiKeySet({ - tenancyId: auth.tenancy.id, + projectId: auth.project.id, ...body, }); diff --git a/apps/backend/src/app/api/latest/integrations/neon/projects/provision/route.tsx b/apps/backend/src/app/api/latest/integrations/neon/projects/provision/route.tsx index 66aae03fc..74c1873d6 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/projects/provision/route.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/projects/provision/route.tsx @@ -49,13 +49,13 @@ export const POST = createSmartRouteHandler({ await prismaClient.neonProvisionedProject.create({ data: { - tenancyId: createdProject.id, + projectId: createdProject.id, neonClientId: clientId, }, }); const set = await createApiKeySet({ - tenancyId: createdProject.id, + projectId: createdProject.id, description: `Auto-generated for Neon (${req.body.display_name})`, expires_at_millis: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 100).getTime(), has_publishable_client_key: false, diff --git a/apps/backend/src/app/api/latest/integrations/neon/projects/transfer/confirm/verification-code-handler.tsx b/apps/backend/src/app/api/latest/integrations/neon/projects/transfer/confirm/verification-code-handler.tsx index 6e0df7e18..d0cb7e87d 100644 --- a/apps/backend/src/app/api/latest/integrations/neon/projects/transfer/confirm/verification-code-handler.tsx +++ b/apps/backend/src/app/api/latest/integrations/neon/projects/transfer/confirm/verification-code-handler.tsx @@ -42,8 +42,8 @@ export const neonIntegrationProjectTransferCodeHandler = createVerificationCodeH const recentDbUser = await tx.projectUser.findUnique({ where: { - projectId_projectUserId: { - projectId: "internal", + tenancyId_projectUserId: { + tenancyId: tenancy.id, projectUserId: user.id, }, }, @@ -52,8 +52,8 @@ export const neonIntegrationProjectTransferCodeHandler = createVerificationCodeH await tx.projectUser.update({ where: { - projectId_projectUserId: { - projectId: "internal", + tenancyId_projectUserId: { + tenancyId: tenancy.id, projectUserId: user.id, }, }, diff --git a/apps/backend/src/app/api/latest/internal/api-keys/route.tsx b/apps/backend/src/app/api/latest/internal/api-keys/route.tsx index ab77731e7..4c10f5a9e 100644 --- a/apps/backend/src/app/api/latest/internal/api-keys/route.tsx +++ b/apps/backend/src/app/api/latest/internal/api-keys/route.tsx @@ -15,7 +15,7 @@ export const POST = createSmartRouteHandler({ request: yupObject({ auth: yupObject({ type: adminAuthTypeSchema, - tenancy: adaptSchema.defined(), + project: adaptSchema.defined(), }).defined(), body: apiKeysCreateInputSchema.defined(), method: yupString().oneOf(["POST"]).defined(), @@ -29,7 +29,7 @@ export const POST = createSmartRouteHandler({ const set = await prismaClient.apiKeySet.create({ data: { id: generateUuid(), - tenancyId: auth.tenancy.id, + projectId: auth.project.id, description: body.description, expiresAt: new Date(body.expires_at_millis), publishableClientKey: body.has_publishable_client_key ? `pck_${generateSecureRandomString()}` : undefined, diff --git a/apps/backend/src/route-handlers/crud-handler.tsx b/apps/backend/src/route-handlers/crud-handler.tsx index 9e6981fa7..18feb79bb 100644 --- a/apps/backend/src/route-handlers/crud-handler.tsx +++ b/apps/backend/src/route-handlers/crud-handler.tsx @@ -253,6 +253,7 @@ export function createCrudHandlers< throw new StackAssertionError("Must specify either project or tenancy, not both"); } else if (tenancy) { project = tenancy.project; + branchId = tenancy.branchId; } else if (project) { tenancy = await getSoleTenancyFromProject(project.id); } else {