ComposeWithMe

Multi-Provider Sign-In

GitHub, Google, Facebook, and X sign-in via NextAuth v4

Overview

Users sign in with GitHub, Google, Facebook, or X (Twitter OAuth 2.0) via NextAuth v4, backed by a Postgres adapter. Any linked provider identity resolves to the same User row in Postgres — session.user.id is that row's id, and is what everything else in the app (song ownership, uploads) keys off.

A signed-in user who didn't start with GitHub can later "Connect GitHub" to unlock self-hosted (GITHUB-backed) songs — see Storage Backends.

How it works

  • src/lib/auth.ts builds the NextAuth providers list dynamically: a provider only registers if its _CLIENT_ID/_CLIENT_SECRET env vars are both set (enabledProviderIds exports the resulting list). This keeps unconfigured providers (Facebook/X, until credentials exist) out of the sign-in UI entirely rather than rendering a button that errors on click.
  • Session strategy is JWT, not database sessions — the jwt/session callbacks copy user.id and (for GitHub) the GitHub login onto the token/session.
  • allowDangerousEmailAccountLinking: false on every provider — two providers never auto-merge into one account just because the email matches. Linking only happens through the explicit "Connect GitHub" flow (ConnectGithubButton.tsx), which relies on NextAuth v4 already decoding the active session cookie during an OAuth callback and linking the new identity to that user (rejecting with OAuthAccountNotLinked if the GitHub identity belongs to someone else).
  • X/Twitter uses OAuth 2.0, which does not return an email — code must never assume session.user.email exists for X sign-ins.

Key files

FileRole
src/lib/auth.tsProvider list, JWT/session callbacks, enabledProviderIds
src/components/ConnectGithubButton.tsxsignIn('github') while already authenticated, to link GitHub
prisma/schema.prismaUser/Account/Session/VerificationToken adapter tables

Limitations / notes

  • Apple sign-in is intentionally not implemented (needs a paid Apple Developer account + a JWT client secret rotated every 6 months).
  • GitHub's OAuth App callback URL is a single field (classic OAuth Apps don't support multiple), so it's manually switched between local dev and production.