From 897c9d65f045262b35f2efe9de788d301a072475 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Mon, 5 Jan 2026 01:14:54 +0530 Subject: [PATCH 1/2] fix(auth): enhance SAML login handling by introducing userId field and updating JWT token structure (#26428) --- .../features/auth/lib/next-auth-options.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 0767004b9d5632..41c01f2aaccec0 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -52,6 +52,7 @@ import { dub } from "./dub"; import { validateSamlAccountConversion } from "./samlAccountLinking"; import CalComAdapter from "./next-auth-custom-adapter"; import { verifyPassword } from "./verifyPassword"; +import { UserProfile } from "@calcom/types/UserProfile"; type UserWithProfiles = NonNullable< Awaited> @@ -274,6 +275,16 @@ export const CalComCredentialsProvider = CredentialsProvider({ }); const providers: Provider[] = [CalComCredentialsProvider, ImpersonationProvider]; +type SamlIdpUser = { + id: number; + userId: number; + firstName: string; + lastName: string; + email: string; + name: string; + email_verified: boolean; + profile: UserProfile; +}; if (IS_GOOGLE_LOGIN_ENABLED) { providers.push( @@ -356,7 +367,7 @@ if (isSAMLLoginEnabled) { credentials: { code: {}, }, - async authorize(credentials) { + async authorize(credentials): Promise { log.debug("CredentialsProvider:saml-idp:authorize", safeStringify({ credentials })); if (!credentials) { return null; @@ -418,7 +429,11 @@ if (isSAMLLoginEnabled) { } const [userProfile] = user?.allProfiles ?? []; return { + // This `id` is actually email as sent by the saml configuration of NameId=email + // Instead of changing it, we introduce a new userId field to the object + // Also, another reason to not touch it is that setting to to user.id starts breaking the saml-idp flow with an uncaught error something related to that it is expected to be a string id: id as unknown as number, + userId: user.id, firstName, lastName, email, @@ -622,7 +637,14 @@ export const getOptions = ({ log.debug("callbacks:jwt:accountType:credentials", safeStringify({ account })); // return token if credentials,saml-idp if (account.provider === "saml-idp") { - return { ...token, upId: user.profile?.upId ?? token.upId ?? null } as JWT; + const samlIdpUser = user as SamlIdpUser; + const updatedToken = { + ...token, + // Server Session explicitly requires sub to be userId. So, override what is set by BoxyHQ + sub: samlIdpUser.userId.toString(), + upId: samlIdpUser.profile?.upId ?? token.upId ?? null, + } as JWT; + return updatedToken; } // any other credentials, add user info return { From aca3b1ecdbe58fc2ed9c5c775959f56972876f46 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Sun, 4 Jan 2026 18:26:01 -0300 Subject: [PATCH 2/2] fix(ci): delete old prod build caches on cache miss (#26437) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../production-build-without-database.yml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/production-build-without-database.yml b/.github/workflows/production-build-without-database.yml index c27f0298d2cddb..5517fc18686643 100644 --- a/.github/workflows/production-build-without-database.yml +++ b/.github/workflows/production-build-without-database.yml @@ -4,6 +4,7 @@ on: permissions: contents: read + actions: write env: ALLOWED_HOSTNAMES: ${{ vars.CI_ALLOWED_HOSTNAMES }} @@ -44,4 +45,26 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/dangerous-git-checkout - uses: ./.github/actions/yarn-install + - name: Generate cache key + id: cache-key + uses: ./.github/actions/cache-build-key + with: + branch_key: ${{ github.head_ref || github.ref_name }} + - name: Check if production build cache exists + uses: actions/cache@v4 + id: cache-check + with: + path: | + ${{ github.workspace }}/apps/web/.next + ${{ github.workspace }}/apps/web/public/embed + **/.turbo/** + **/dist/** + key: ${{ steps.cache-key.outputs.key }} + lookup-only: true + - name: Delete old production build caches for this branch + if: steps.cache-check.outputs.cache-hit != 'true' + uses: useblacksmith/cache-delete@v1 + with: + key: prod-build-${{ github.head_ref || github.ref_name }} + prefix: "true" - uses: ./.github/actions/cache-build