From ac8b583c9329f97ea46664cdc8a4b7262d9efeab Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 20 Jun 2026 15:04:28 -0700 Subject: [PATCH 1/2] improvement(auth): make Microsoft emailVerified derivation total deriveMicrosoftEmailVerified cast the verified-email claims to string[] and called .includes through optional chaining, which only guards null/undefined. A claim arriving as a non-array, non-string value (e.g. a number) would throw inside getUserInfo and fail the OAuth flow. Array-check the claims with a proper type guard so any claim shape resolves to unverified instead of throwing. --- apps/sim/lib/oauth/microsoft.test.ts | 7 ++++++- apps/sim/lib/oauth/microsoft.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/sim/lib/oauth/microsoft.test.ts b/apps/sim/lib/oauth/microsoft.test.ts index a3eaf4d9c6..c7302d3c8d 100644 --- a/apps/sim/lib/oauth/microsoft.test.ts +++ b/apps/sim/lib/oauth/microsoft.test.ts @@ -54,10 +54,15 @@ describe('deriveMicrosoftEmailVerified', () => { expect(deriveMicrosoftEmailVerified({ email_verified: 'true' }, EMAIL)).toBe(true) }) - it('treats a malformed verified-email claim as unverified', () => { + it('treats malformed (non-array) verified-email claims as unverified without throwing', () => { expect(deriveMicrosoftEmailVerified({ verified_primary_email: 'not-an-array' }, EMAIL)).toBe( false ) + expect(deriveMicrosoftEmailVerified({ verified_primary_email: 123 }, EMAIL)).toBe(false) + expect(deriveMicrosoftEmailVerified({ verified_secondary_email: { foo: 'bar' } }, EMAIL)).toBe( + false + ) + expect(deriveMicrosoftEmailVerified({ verified_primary_email: null }, EMAIL)).toBe(false) }) }) diff --git a/apps/sim/lib/oauth/microsoft.ts b/apps/sim/lib/oauth/microsoft.ts index 2ecf050e62..1e9be406f2 100644 --- a/apps/sim/lib/oauth/microsoft.ts +++ b/apps/sim/lib/oauth/microsoft.ts @@ -36,7 +36,10 @@ export function deriveMicrosoftEmailVerified( if (claims.email_verified !== undefined) { return Boolean(claims.email_verified) } - const verifiedPrimaryEmail = claims.verified_primary_email as string[] | undefined - const verifiedSecondaryEmail = claims.verified_secondary_email as string[] | undefined - return Boolean(verifiedPrimaryEmail?.includes(email) || verifiedSecondaryEmail?.includes(email)) + const { verified_primary_email: verifiedPrimary, verified_secondary_email: verifiedSecondary } = + claims + return ( + (Array.isArray(verifiedPrimary) && verifiedPrimary.includes(email)) || + (Array.isArray(verifiedSecondary) && verifiedSecondary.includes(email)) + ) } From 035b81fa1a08a9d16587c61a9b3992911f938118 Mon Sep 17 00:00:00 2001 From: waleed Date: Sat, 20 Jun 2026 15:11:44 -0700 Subject: [PATCH 2/2] test(auth): lock in unverified for a string verified-email claim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a boundary case asserting a string verified_primary_email/ verified_secondary_email equal to the email resolves to unverified — the old string[] cast would have returned true via String.includes. --- apps/sim/lib/oauth/microsoft.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/sim/lib/oauth/microsoft.test.ts b/apps/sim/lib/oauth/microsoft.test.ts index c7302d3c8d..e4bbf1f138 100644 --- a/apps/sim/lib/oauth/microsoft.test.ts +++ b/apps/sim/lib/oauth/microsoft.test.ts @@ -64,6 +64,11 @@ describe('deriveMicrosoftEmailVerified', () => { ) expect(deriveMicrosoftEmailVerified({ verified_primary_email: null }, EMAIL)).toBe(false) }) + + it('does not treat a string claim equal to the email as verified (guards the old unsafe cast)', () => { + expect(deriveMicrosoftEmailVerified({ verified_primary_email: EMAIL }, EMAIL)).toBe(false) + expect(deriveMicrosoftEmailVerified({ verified_secondary_email: EMAIL }, EMAIL)).toBe(false) + }) }) describe('isMicrosoftProvider', () => {