GeezerVison is showing github.com — fix(auth): let Microsoft sign-in link via Entra's domain-verified email claim by waleedlatif1 · Pull Request #6546 · simstudioai/sim
Conversation
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_edov and email 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
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)
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
greptile-apps[bot] reacted with thumbs up emoji
…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
Bot
commented
Aug 11, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
| Project |
Deployment |
Actions |
Updated (UTC) |
docs |
Skipped |
|
Aug 11, 2026 6:32pm |

|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor
Bot
commented
Aug 11, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
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.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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
| Filename |
Overview |
| apps/sim/lib/oauth/microsoft.ts |
Adds a narrowly scoped mapper that promotes Microsoft email verification only for accepted xms_edov values. |
| apps/sim/lib/auth/auth.ts |
Wires the mapper and explicit account-selection prompt into the Microsoft social provider configuration. |
| apps/sim/lib/oauth/microsoft.test.ts |
Tests verified, unverified, absent, string, and numeric encodings of the Entra claim. |
| apps/sim/app/oauth-error/page.tsx |
Adds 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
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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.
greptile-apps[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
greptile-apps[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 11, 2026
This branch was previously deployed
1 inactive
deployment
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|