MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-20

1.1.4 Google OAuth provider — Better-Auth config, auto-link semantics

Done
Description

Estimate: 15m · Depends on: 1.1.2, 1.1.3

Add Google OAuth as a peer sign-in method to the Better-Auth instance configured in 1.1.2. Wires the provider's config block, the OAuth callback handler (Better-Auth mounts it automatically once the provider is registered), and the auto-link semantics so that a Google sign-in with an email matching an existing password account links into the same User row. No UI work in this subtask — the "Continue with Google" button on the sign-in/sign-up pages lands in 1.1.5, which depends on this.

Why auto-link, not require-verification: Decided in Story-1.1 planning conversation (recorded in MOTIR.md). Same email → same user; both methods work afterward. Lowest-friction UX; the security trade-off (Google-account compromise → full account takeover) is acceptable for v1 because Google already verified the email and most users use Google with 2FA on. If a future Story needs require-verification semantics for compliance reasons, swap the accountLinking Better-Auth option there.

Why env-var-driven with no shipped defaults: Per the planner-as-consumer principle (MOTIR.md "Current state" + notes.html mistake #22), each Motir-planned project owns its own Google Cloud OAuth app. The starters ship GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET as required env vars with no defaults; the planner adds a "Set up Google Cloud OAuth credentials" Story in pre-plan for each project, parallel to the email-provider Story. In dev, missing creds make the Google button render but error visibly when clicked — no silent fallback.

What you'll do: Extend /lib/auth/index.ts (from 1.1.2) to register the Google social provider with clientId / clientSecret read from env, and enable accountLinking with the trustedProviders: ["google"] option so matching emails auto-link. Wire the OAuth callback to use findOrCreateOAuthUser from /lib/users/repo.ts (from 1.1.3). Document GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET in .env.example with a comment explaining where to obtain them (Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client ID, web application, authorized redirect URI {BETTER_AUTH_URL}/api/auth/callback/google).

Acceptance criteria

  • Better-Auth's Google social provider registered in /lib/auth/index.ts; reads GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET from env.
  • accountLinking.trustedProviders includes "google"; auto-link by matching email is enabled.
  • OAuth callback handler at /app/api/auth/[...all]/route.ts (mounted by 1.1.2) handles Google's callback round-trip; on success the user is created or linked via findOrCreateOAuthUser from 1.1.3.
  • OAuth-created users have passwordHash: null and emailVerifiedAt set to the OAuth callback time (Google has already verified the email).
  • If a user already exists with the Google email (created via email/password) and signs in with Google for the first time, a new OAuthAccount row is created linking to the existing User; the existing passwordHash is unchanged; both sign-in methods work afterward.
  • .env.example documents GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET with a comment pointing to Google Cloud Console + the redirect URI shape.
  • Smoke test (manual; full E2E lands in 1.1.7): with valid Google creds in .env, hitting /api/auth/sign-in/social?provider=google directly (no UI) redirects to Google's consent screen, then after consent redirects back, creates a session cookie, and the protected route renders.
  • Tests: unit test for findOrCreateOAuthUser's auto-link branch (called above in 1.1.3's AC list — verify here that the OAuth callback actually invokes it correctly via a stubbed Google response).

Context refs

  • /lib/auth/index.ts — Better-Auth instance from 1.1.2 (to extend)
  • /lib/users/repo.ts — user repository with findOrCreateOAuthUser from 1.1.3
  • /prisma/schema.prismaOAuthAccount model from 1.1.3
  • .env.example — env-var pattern to extend
  • Better-Auth social-providers docs (fetched at prompt-gen time): Google provider config, account-linking options, callback handler shape
  • Google Cloud Console OAuth 2.0 setup docs (URL, fetched at prompt-gen): authorized redirect URIs, OAuth consent screen
Status
Done
Type
Sub-task