I don't want to post this as an issue cos it's just Claude slop, I'll lodge a proper one in the morning if needed. One arm is occupied rocking a 6 day old to sleep
Signing in through a configured Entra ID OIDC provider fails with account not linked whenever the email belongs to an existing user. New users can sign up through the same provider fine.
Better-auth allows implicit linking if the provider is trusted or the IdP says email_verified: true. Entra fails both here.
The trust check can't pass for anything except Google. For SSO providers, trusted means domainVerified === true and the sign-in email's domain matches the provider's domain column. The trustedProviders list from getTrustedAccountLinkingProviderIds isn't consulted at all, since the SSO plugin calls the linking handler with trustProviderByName: false. The domainVerified half is fine (the workaround in IdentityProviderModel.create sets it), but normalizePersistedAllowedEmailDomain writes an empty domain for every providerId except "Google", and an empty domain can never match. So trust is false for every non-Google provider.
That leaves email_verified, and Microsoft doesn't send it. Not in v2.0 ID tokens, not from Graph's oidc/userinfo. Okta, Google and GitLab all send it, which is why linking looks fine everywhere else and this went unnoticed.
My workaround: add the xmsedov optional claim to the ID token in the Entra app registration, set oidcConfig.mapping.emailVerified to xmsedov, and drop userInfoEndpoint so claims come from the ID token (userinfo can't see xms_edov). The mapping field is in the schema but the form doesn't expose it, so it has to go in via the API.
Suggested fix: persist the allowed email domain for all providers instead of only Google, and show the field for all of them. The trust branch then works everywhere and linking stops depending on which IdPs happen to emit email_verified. It would also extend the assertSsoEmailDomainAllowed sign-in filter, currently Google-only, to every provider.
While I was in there: the TODO on the domainVerified workaround cites better-auth#6481 as pending, but that's closed and #6551 made the strict linking permanent, so the comment is stale. The dynamic trustedProviders from #7904 is wired up but does nothing for SSO linking on @better-auth/sso 1.6.22, per the trustProviderByName: false behaviour above