Skip to content

Potential identity connections: Discord, Twitch, Battle.net #24

Description

@amrtgaber

Scope

Add Discord, Twitch, and Battle.net as identity connections — not just sign-in OAuth options. Each should support the full lifecycle the platform already has for Google + Steam:

  • Sign in with the provider (creates a new criticalbit account if no link exists).
  • Link the provider to an existing account from settings (bidirectional — works regardless of how the account was originally created).
  • List the linked providers via GET /auth/me/connections.
  • Disconnect from settings (subject to the existing safety rule — won't strand a user with no login method).

This is closer to the Discord "connections" model than a sign-in feature: each linked provider is a piece of the user's platform identity that consumer tools can read off of /users/lookup, /users/search, or eventually a /auth/me/connections-derived projection.

Providers

Discord

  • OAuth2 with httpx-oauth (Discord client already exists upstream).
  • Useful as both a sign-in path AND a platform-identity signal — tournament hub / community features may want to ping the linked Discord ID later.
  • Provider scopes: identify minimum, email if we want to support sign-in-via-Discord.

Twitch

  • OAuth2 with httpx-oauth (Twitch client exists upstream).
  • Most valuable as an identity-only connection: streamer-facing tools (HSI, future bracket overlays) can use the linked Twitch ID to show stream status, embed players, surface "watch live" badges, etc.
  • Email may be present depending on scopes; needs the same email_verified trust check Google has.

Battle.net

  • OAuth2 with hand-rolled client (no httpx-oauth ready-made; small wrapper around their /oauth/authorize + /oauth/token endpoints).
  • Region-specific (BattleTag is the user-visible identifier; the OAuth sub is a numeric account ID).
  • Identity-only most likely (Battle.net typically doesn't return email via OAuth).

Why this is cheap now (post PR auth-api#40, #41)

The backend cost per provider is genuinely one file in app/providers/<name>.py + one entry in app/providers/registry.py. Everything else — the four routes (login/callback/associate-authorize/associate-callback), CSRF cookie binding, conflict detection, /auth/me/connections, /auth/me/connections/{provider} unlink, the unverified-merge guard — already works for any registered provider.

The frontend cost per provider is also bounded: a Connect button on the settings page (already iterating over providers from /auth/me/connections), provider branding/icons, and the matching forwarding handler on the dev-mode /callback/<provider> page.

Backend side (no companion issue on auth-api yet — file one when picking this up)

Per provider:

  1. app/providers/<name>.py implementing the AuthProvider protocol. ~70 lines for OAuth2 providers (use httpx-oauth's ready-made client); ~150 for Battle.net (hand-roll the OAuth2 dance).
  2. One line in app/providers/registry.py gating on the relevant env vars (<NAME>_CLIENT_ID, <NAME>_CLIENT_SECRET).
  3. Settings: add the env vars to app/config.py + .env.example.
  4. asserts_verified_email decision per provider (does the provider return a verified-email claim we can trust?). Google does (True). Steam doesn't have email at all. Twitch — needs spec check. Discord — needs spec check. Battle.net — probably no email at all.

Frontend side (this repo)

Per provider:

  1. Connect button on the settings/connections page. The page already reads /auth/me/connections to know what's linked.
  2. Provider icon + branding (Discord blurple, Twitch purple, Battle.net blue/black). SVG assets per provider.
  3. /callback/<provider> page that forwards to the API based on state.purpose (the same dispatch the existing Google + Steam callback pages do).
  4. /callback/<provider>-complete page for the post-login redirect (likely a small per-provider wrapper around the shared completion logic).

Order of operations

These can ship independently per provider. Suggested order based on perceived value:

  1. Discord first — biggest community signal, most useful identity link, simplest implementation (off-the-shelf httpx-oauth client).
  2. Twitch second — streamer-tooling story is the next obvious payoff.
  3. Battle.net last — most implementation work (hand-rolled OAuth client) and narrowest immediate consumer.

Or batch all three in one feature flag drop if it's easier to coordinate the frontend work.

Out of scope

  • This issue does NOT cover sign-in-only providers (where the user can't subsequently log in via password or another OAuth). All three providers should be additive identity connections, never the sole sign-in path for a long-lived account.
  • This issue does NOT cover deeper integrations (Discord bot, Twitch EventSub, Battle.net game API). Those are separate features that can read off the linked identities once they exist.

Acceptance per provider

  • Backend app/providers/<name>.py + registry entry + settings.
  • Sign-in flow works end-to-end (new account creation).
  • Link flow works for existing accounts.
  • Unlink obeys the safety rule.
  • Connect/Disconnect UI shipped in this repo.
  • Provider appears correctly in /auth/me/connections and downstream surfaces.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions