HarperFast / HarperFast/oauth

Support selecting which email becomes the login identity when a provider returns several (e.g. GitHub)

Open
#228 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
JavaScript
Stars
1
Forks
1
Avg merge
2d 16h
Merged PRs (30d)
12

Description

## Summary

When a provider can return more than one email for a user (GitHub is the concrete case — `/user/emails` returns the full set with per-address `verified` flags), the plugin picks one deterministically and drops the rest. There is no way for an application to choose which email becomes the login identity, and the alternatives never reach the `onLogin` hook.

## Current behavior

`src/lib/providers/github.ts` `getUserInfo`:
- If the public-profile email (`/user`) is set, it uses that address and surfaces its `verified` flag.
- Otherwise it selects the `primary` email.
- The remaining emails are discarded inside the adapter; only the single chosen `email` (and its `email_verified`) is attached to the returned user info.

OIDC/generic providers have no multi-email concept — the email is a single id-token claim or `userinfo.email`.

Because the email is the account identity (the default `usernameClaim`), the chosen address determines which Harper account the login resolves to. A user with several addresses can only reach the account keyed to their profile-or-primary one; if their intended address is a different (even verified) one, the only workaround today is to change their profile/primary at the provider.

## Proposed direction

Keep the policy in the application, but give it the inputs it needs:

1. **Plugin surfaces the candidates.** Stop discarding the fetched list — expose the verified email set to the `onLogin` hook (e.g. on `oauthUser.metadata.oauthClaims`), so an app can see the options.
2. **Application owns selection.** The choice — an interactive "which email?" step, or a rule such as "prefer the corporate domain" — is expressed through the existing `onLogin` outcome contract (#174). The plugin does not need its own picker UX.
3. **Guardrail (must-have).** Any application-chosen email must still be one of the provider's **verified** addresses; identity/adoption may only ever key on a verified email. The selection path must not become a way to key identity on an unverified or unfetched address.

A full plugin-served interactive picker is possible but heavier and not the recommended starting point.

## Scope

- Plugin: expose verified candidates to the hook; validate any hook-chosen email against the verified set before it keys identity.
- App: selection UX/policy via `onLogin`.

Depends on the `onLogin` outcome contract (#174, shipped).

Contributor guide

Open the contributing guide

Research direction

Start in src/lib/providers/github.ts, especially getUserInfo, and trace how the returned user info reaches the onLogin hook and its outcome contract from #174. Check how oauthUser.metadata.oauthClaims is assembled. Done means verified email candidates reach the hook and any selected identity is restricted to that fetched verified set.

Written by the indexing model from the issue text.

Assessment

Tech stack
github, typescript
Domain
authentication, backend, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.