Skip to content

feat: Add an opt-in to trust emails from the custom OAuth/OIDC provider #1421

Description

@thriz0

Problem Statement

Kaneo currently stores email_verified as false by default for newly created users, including users created through the custom OAuth/OIDC provider.

This behavior is defined in the user schema:

https://github.com/usekaneo/kaneo/blob/v2.9.8/apps/api/src/database/schema.ts#L22-L24

I reproduced this in a local environment with a custom OIDC provider: after a new user successfully signed in through OIDC, their database record contained:

email_verified = false

The custom OAuth profile mapper currently maps the user's name but does not provide an emailVerified value:

https://github.com/usekaneo/kaneo/blob/v2.9.8/apps/api/src/utils/custom-oauth-profile.ts#L5-L21

This affects features that rely on the local verification state. For example, /api/invitation/pending returns an empty list when user.emailVerified is false:

https://github.com/usekaneo/kaneo/blob/v2.9.8/apps/api/src/invitation/index.ts#L45-L53

As a result, a user can successfully authenticate through a centrally managed enterprise identity provider but still be unable to see their pending workspace invitations.

The current default is appropriate for untrusted or public OAuth providers and should remain unchanged.

Proposed Solution

Add an explicit opt-in environment variable for the custom OAuth/OIDC provider, for example:

CUSTOM_OAUTH_ASSUME_EMAIL_VERIFIED=false

When set to true, Kaneo would store the email returned by the custom provider as verified.

Expected behavior:

  • Keep false as the default.
  • Apply the override only to providerId: "custom".
  • Do not affect email/password authentication or built-in social providers.
  • Mark new users created through the custom provider as verified.
  • Update existing unverified users after their next successful custom OAuth sign-in.
  • If the provider explicitly returns email_verified: false, preferably preserve that explicit value instead of overriding it.
  • Document the account-linking and impersonation risks.

The option should only be enabled when the operator guarantees that:

  • The configured issuer and tenant are controlled and restricted.
  • The returned email cannot be freely chosen by the authenticated user.
  • The identity provider guarantees ownership or authoritative assignment of every returned email.

Alternative Solutions

  1. Configure the OIDC provider to return email_verified: true, when supported and correctly propagated by Better Auth.

  2. Require a second verification through Kaneo using an email OTP, magic link, or verification email. This remains the safest provider-independent solution, but it is redundant for controlled SSO-only environments.

  3. Manually update email_verified in the database. This is fragile, bypasses application configuration, and does not cover future users.

A separate development-only option could also be considered for email/password authentication:

DEV_AUTO_VERIFY_EMAIL=true

It should only be accepted in an explicitly detected development environment and should fail startup if enabled in production. Because this has a different threat model, it may be better handled in a separate issue.

Relevant Context

Kaneo already includes the custom provider in Better Auth's trustedProviders list for account linking:

https://github.com/usekaneo/kaneo/blob/v2.9.8/apps/api/src/auth.ts#L187-L199

However, there is currently no operator-facing option to apply the same explicit trust decision to the user's local emailVerified state.

This proposal does not request changing the secure default. It requests a narrowly scoped and explicitly enabled trust delegation for controlled enterprise deployments.

Does this feature align with Kaneo's focus on simplicity?

Yes.

It introduces one optional environment variable, keeps the current secure behavior by default, and removes the need for a redundant second email-verification flow in controlled SSO-only environments.

The security trade-off remains explicit and limited to the configured custom provider.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions