#13644·appwrite

Enhancement: configurable Microsoft OAuth account-linking trust policy

Author: alexfreeman90Created Sep 14, 2026Updated Sep 16, 2026
Labelsproduct / databasesproduct / authproduct / functionsproduct / self-hostedproduct / messagingproduct / vcsproduct / avatars

Enhancement description

Add an explicit per-project, per-provider account-linking policy, starting with Microsoft, for deployments that deliberately trust the provider-reported address when connecting an OAuth identity to an existing account.

Keep the current verified-email requirement as the default. An administrator should be able to opt into a documented alternative without maintaining a modified OAuth adapter. The documentation should explain the broader trust involved, its effect on automatic linking, and any interaction with Appwrite's emailVerification flag.

I am raising this as an enhancement because the existing restriction is intentional: PR #10986 introduced the verified-email linking requirement and the generic bad-request response.

Pitch

We encountered this while integrating Microsoft OAuth into a self-hosted Appwrite 2.0.0 deployment at CyberITEX, using Next.js SSR and createOAuth2Token followed by createSession.

Observed behaviour / reproduction

  1. Have an existing active email/password account with a verified email address, such as [email protected], and no linked Microsoft identity.
  2. Start a new Microsoft OAuth sign-in using createOAuth2Token, without an existing authenticated Appwrite browser session at the native callback.
  3. Authenticate with a Microsoft account for which the adapter returns the same address.
  4. Appwrite redirects to the configured failure URL with type: general_bad_request and code: 400, before the application's success callback runs.

Other Microsoft accounts could sign in, and Google and GitHub sign-in also worked in our testing. Those observations do not establish that every provider linked to the same Appwrite user ID.

In the 2.0.0 Microsoft adapter, the address comes from Graph's userPrincipalName, while isEmailVerified() always returns false. The native callback rejects a same-email match when the identity has not already resolved to a user and the provider reports the address as unverified.

Desired outcome

  • A supported opt-in policy for deployments that accept Microsoft authentication and deliberately trust its reported address for initial account linking, while preserving the current default for other deployments.
  • Once linked, normal Microsoft sign-in continues to resolve the existing account through its provider identity.
  • Clear documentation and a recoverable linking path when the default policy rejects a collision, without exposing account information to unauthorised callers.

Our application does not require a Microsoft 365 licence or Microsoft-hosted mailbox for sign-in. This is an application requirement, not a claim that Appwrite currently checks licences or that a missing mailbox caused this failure.

Current workaround and trust boundary

We applied a version-specific, read-only mounted Microsoft adapter override that returns true from isEmailVerified(). This resolved the previously failing sign-in in our deployment, but requires maintenance across Appwrite upgrades. The confirmation is limited to that sign-in; this report does not include a broader account-type/tenant test matrix or automated assertions about user-ID and permission preservation.

Successful Microsoft authentication proves control of a Microsoft identity; it does not independently prove ownership of every reported address or principal name. Our override is a deliberate local trust-policy change, not a proposal to make the upstream adapter unconditionally return true. We would welcome maintainer guidance on an acceptable configurable policy or supported alternative that avoids carrying this patch.

Have you spent some time to check if this issue has been raised before?

  • I checked and didn't find a matching enhancement.

I reviewed the older Microsoft duplicate-user issue #3856, which concerns an upgrade to 1.0 and a 409 response, and the intentional linking change in #10986.

Have you read the Code of Conduct?