Skip to content

fix(auth): let Microsoft sign-in link via Entra's domain-verified email claim - #6546

Merged
waleedlatif1 merged 3 commits into
stagingfrom
fix/microsoft-oauth-linking
Aug 11, 2026
Merged

fix(auth): let Microsoft sign-in link via Entra's domain-verified email claim#6546
waleedlatif1 merged 3 commits into
stagingfrom
fix/microsoft-oauth-linking

Conversation

@waleedlatif1

@waleedlatif1waleedlatif1 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • microsoft is excluded from accountLinking.trustedProviders because the email claim is attacker-controllable on /common/ (nOAuth). Entra never emits email_verified for work/school accounts, so Better Auth refused to link a Microsoft identity onto any existing user row — those users hit account_not_linked permanently, with no recovery path
  • Derive emailVerified from Entra's xms_edov optional claim, emitted only when the email's domain belongs to the user's tenant and an admin verified that domain. It's the one email signal a hostile tenant cannot forge, and it's Microsoft's documented nOAuth mitigation. microsoft stays untrusted — the linking guard now passes on its own merits
  • The mapper returns {} when unverified, so it only ever promotes unverified → verified, never downgrades. With the claim absent, Better Auth's computation is byte-identical to today
  • Add prompt=select_account, so /common/ stops silently reusing whichever Microsoft session the browser already holds
  • Add /oauth-error copy for account_not_linked and email_not_found, which currently dead-end on "Please try again" — advice that can never succeed

Requires an Entra change to take effect

Inert until xms_edovandemail are added as optional claims on the ID token for the app registration (xms_edov requires email to be present). Until then the mapper returns {} and behavior is unchanged.

Known limitation: xms_edov is false for SAML/WS-Fed federated domains and for B2B guests, so those tenants remain unlinkable and should use Entra SSO, which is trusted for linking.

Verified against the Better Auth source

  • callback.mjs:140-152 passes getUserInfo's result into handleOAuthUserInfo; microsoft-entra-id.mjs:91-107 builds it from decodeJwt(idToken), so xms_edov is visible to mapProfileToUser, whose result is spread last (:116)
  • overrideUserInfoOnSignIn is unset, so the branch that rewrites email/emailVerified on every sign-in (link-account.mjs:67-77) never runs
  • Existing Microsoft users get emailVerified upgraded on next sign-in (link-account.mjs:49,65), guarded on exact email match — an upgrade, never a downgrade
  • Welcome email fires exactly once: today Microsoft users get none (auth.ts:348 gates on emailVerified); after this they get one at creation. afterEmailVerification only runs in the verification flow, which OAuth signups don't enter — no duplicate
  • The OAuth verification-email branch (link-account.mjs:105) needs both sendOnSignUp and sendVerificationEmail; we set neither, so it stays dead
  • Integration connectors are untouched — they're genericOAuth entries with their own providerId and getUserInfo

Type of Change

  • Bug fix

Testing

bun run type-check clean; 297 tests across 18 files in lib/auth + lib/oauth pass; verified the new tests fail when the mapper is stubbed out.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…il claim
Microsoft is excluded from accountLinking.trustedProviders because the email
claim is attacker-controllable on /common/ (nOAuth). Entra never emits
email_verified for work/school accounts, so Better Auth refused to link a
Microsoft identity onto any existing user row, permanently stranding those
users on account_not_linked.
Derive emailVerified from the xms_edov optional claim, which Entra emits only
when the email's domain belongs to the user's tenant and an admin verified it
— the one email signal a hostile tenant cannot forge. Microsoft stays
untrusted; the guard now passes on its own merits.
The mapper returns an empty object when unverified, so it can only ever
promote unverified to verified, never downgrade.
@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
docsSkippedSkippedAug 11, 2026 6:32pm

Request Review

@cursor

cursorBot commented Aug 11, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Touches authentication, account linking, and email-verification promotion for Microsoft OAuth; incorrect trust in xms_edov could affect who can link to an existing account (mitigated by only promoting verified, not downgrading).

Overview
Fixes Microsoft sign-in for users who already have a Sim account by deriving emailVerified from Entra’s xms_edov optional claim (domain-owned, admin-verified email) via mapMicrosoftProfileToUser, so account linking can succeed without adding microsoft to trustedProviders. The mapper only promotes unverified → verified (returns {} otherwise); behavior is unchanged until xms_edov and email are configured on the app registration.

Microsoft social login now uses prompt: 'select_account' so /common/ does not silently reuse an existing browser session.

The /oauth-error page adds actionable copy for account_not_linked and email_not_found instead of a generic “try again.”

Reviewed by Cursor Bugbot for commit 5675934. Configure here.

@greptile-apps

greptile-appsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR updates Microsoft authentication to recognize Entra’s domain-verified email claim while keeping Microsoft excluded from trusted account-linking providers.

  • Maps a verified xms_edov claim to emailVerified: true without downgrading absent or unverified claims.
  • Requests explicit Microsoft account selection during sign-in.
  • Adds actionable messages for account-linking and missing-email OAuth failures.
  • Adds focused tests for accepted and rejected xms_edov encodings.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

FilenameOverview
apps/sim/lib/oauth/microsoft.tsAdds a narrowly scoped mapper that promotes Microsoft email verification only for accepted xms_edov values.
apps/sim/lib/auth/auth.tsWires the mapper and explicit account-selection prompt into the Microsoft social provider configuration.
apps/sim/lib/oauth/microsoft.test.tsTests verified, unverified, absent, string, and numeric encodings of the Entra claim.
apps/sim/app/oauth-error/page.tsxAdds recovery-oriented messages for account-linking and missing-email failures.

Sequence Diagram

sequenceDiagram
participant User
participant Sim as Sim Auth
participant Entra as Microsoft Entra
participant BA as Better Auth
User->>Sim: Sign in with Microsoft
Sim->>Entra: "Authorize with prompt=select_account"
Entra-->>BA: ID token with email and optional xms_edov
BA->>Sim: Map Microsoft profile
alt xms_edov is verified
Sim-->>BA: "emailVerified=true"
BA->>BA: Evaluate existing-account linking
else claim absent or unverified
Sim-->>BA: No profile override
BA->>BA: Preserve existing verification result
end
Loading

Reviews (3): Last reviewed commit: "chore(auth): tighten the Microsoft linki..." | Re-trigger Greptile

Hosted Sim serves many Entra tenants, so it must stay on the multi-tenant
endpoint — pinning is only meaningful for a self-hoster restricting sign-in to
their own directory, and nobody is asking for that yet. The xms_edov fix is
independent of the tenant setting, so this removes surface without touching
behavior.
@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5675934. Configure here.

@waleedlatif1
waleedlatif1 merged commit bf82512 into stagingAug 11, 2026
29 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/microsoft-oauth-linking branch August 11, 2026 18:41
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@waleedlatif1