Skip to content

fix(oauth): auto-bind verified external login to existing active user by email - #1613

Open
renich wants to merge 2 commits into
apache:devfrom
renich:fix/oauth-auto-bind-existing-user
Open

fix(oauth): auto-bind verified external login to existing active user by email#1613
renich wants to merge 2 commits into
apache:devfrom
renich:fix/oauth-auto-bind-existing-user

Conversation

@renich

@renich renich commented Sep 13, 2026

Copy link
Copy Markdown

Proposed Changes

  • When an external OAuth connector authenticates a user and returns a verified email address matching an existing active local user account, automatically bind the external identity to that account instead of returning UserAccessDenied (/50x?title=Access%20denied&msg=Access%20denied).
  • Update last_login_date for the existing user.
  • Activate the user's email status if it was in EmailStatusToBeVerified.
  • Issue and return the login access token directly so the user is seamlessly logged into their existing account.

Motivation & Context

Previously, if a user had registered using email/password (or was created during initial site setup), and subsequently attempted to log in using an OAuth connector (e.g., "Continue with Google"), Apache Answer aborted in ExternalLogin:

	if _, exist, err := us.userRepo.GetByEmail(ctx, externalUserInfo.Email); err != nil {
		return nil, err
	} else if exist {
		return &schema.UserExternalLoginResp{
			ErrTitle: translator.Tr(handler.GetLangByCtx(ctx), reason.UserAccessDenied),
			ErrMsg:   translator.Tr(handler.GetLangByCtx(ctx), reason.UserAccessDenied),
		}, nil
	}

This redirected users to /50x?title=Access%20denied&msg=Access%20denied, requiring them to realize they had to log in with their password first, navigate to Settings -> Account, and manually link the third-party provider.

For non-technical communities and modern SSO expectations, this creates friction and confusion. Connectors already verify email ownership (for example, connector-google checks respGoogleAuthUserInfo.EmailVerified before setting userInfo.Email). Automatically binding verified OAuth emails to existing active accounts provides a seamless single sign-on experience.


Co-authored with Antigravity AI (Google DeepMind).

… by email

When an external OAuth connector returns a verified email address that
matches an existing active user account in Answer, the previous
implementation immediately aborted with UserAccessDenied (50x Access denied).
This required users who had previously registered with password to log in
first and manually bind the provider under user settings.

For open communities and seamless SSO onboarding, auto-bind the external
identity to the existing active user when the email matches, update their
last login timestamp, and issue an access token directly.

Co-developed with Antigravity AI (Google DeepMind) for root cause analysis
and patch generation.

Signed-off-by: Rénich Bon Ćirić <renich@evalinux.com>
Co-authored-by: Antigravity <antigravity@google.com>
@renich
renich force-pushed the fix/oauth-auto-bind-existing-user branch from a26cbd5 to 2ee3902 Compare September 13, 2026 16:01
When an external OAuth provider does not return a verified email address,
the user is prompted to enter their email. If the email belongs to an
existing user, Answer requests user confirmation (must: true).

Previously, when must: true was received, ExternalLoginBindingUserSendEmail
hit a duplicate `else if exist` branch that immediately returned
EmailExistAndMustBeConfirmed without sending the binding confirmation email.

Allow the flow to proceed when must: true: cache the external login info
for the existing user and dispatch the verification email with the binding
token. Upon email confirmation, ExternalLoginBindingUser binds the provider
to the existing account.

Co-developed with Antigravity AI (Google DeepMind) for root cause analysis
and patch generation.

Signed-off-by: Rénich Bon Ćirić <renich@evalinux.com>
Co-authored-by: Antigravity <antigravity@google.com>
Signed-off-by: Rénich Bon Ćirić <renich@evalinux.com>
Sign up for free to 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