Skip to content

Repository files navigation

Game Library

Production-style authentication is implemented and verified locally. See Production Auth & Email for Google/SMTP setup, email templates, secure callback URLs, onboarding, and focused verification. Gmail SMTP, the hosted templates, and Google sign-in have been tested; Google remains in Testing until the public launch.

The design/UX/performance phase is complete. See the phase report for the redesigned pages, shared components, public RAWG caching, security checks, measurements and verification limits. Discovery now includes Popular, New releases and Critically acclaimed sections; library/profile state remains uncached and authenticated mutations retain RLS ownership checks.

A minimal gaming-library foundation using Next.js App Router, TypeScript, Tailwind CSS, Supabase Auth/Postgres, and server-side RAWG discovery.

The first product loop is implemented: search → game details → add/manage library → public profile. See the library-phase implementation report for architecture, security, migration verification, changed files, and live test results.

Run locally

Use Node.js 22+ and the pnpm version pinned in package.json.

pnpm install --frozen-lockfile
pnpm dev

Open http://localhost:3000. For a new checkout, copy .env.example to .env.local; do not overwrite an existing environment file.

Variable Purpose
NEXT_PUBLIC_SUPABASE_URL Browser-safe Supabase project URL
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY Browser-safe publishable key; RLS enforces access
RAWG_API_KEY Private, server-only RAWG key
SUPABASE_SECRET_KEY Private server-only key used exclusively by the application to cache verified RAWG game references
APP_URL Server-only canonical Auth origin; local default http://localhost:3000, required HTTPS origin in production

Restart after environment changes. Environment files are ignored except .env.example. The secret key is required for adding games; browsing and existing-library updates use public credentials and user sessions. Never prefix private keys with NEXT_PUBLIC_.

Architecture

Boundary Responsibility
src/app/ Server Component pages, Server Actions, confirmation handlers
src/components/ Small reusable forms, navigation, library list, existing RAWG preview
src/lib/data/ Server-only profile/library queries using the current user's session
src/lib/auth/session.ts Verified identity and protected-route guard
src/lib/supabase/ Typed browser client, per-request server client, cookie refresh, safe server transport
src/lib/validation/ Zod server validation; unrecognized ownership fields are discarded
src/types/ Generated database types and shared form/game types
src/lib/rawg/server.ts Existing server-only API helper returning selected metadata
supabase/migrations/ Reproducible schema, constraints, triggers, grants, and RLS

Forms use Client Components and useActionState; data fetching stays on the server. Authentication uses @supabase/ssr cookie clients. src/proxy.ts refreshes sessions and propagates cookies/cache headers. Protected pages/actions independently verify identity with auth.getUser(); they do not trust a supplied user ID or a session object alone.

Server Supabase requests use a fixed-origin Node HTTPS adapter to avoid framework fetch tracing of passwords/auth headers. The existing RAWG helper similarly keeps credential-bearing URLs out of fetch tracing. Both enforce timeouts, reject redirects, and sanitize upstream errors. Framework request logging is disabled. The publishable key and a user's own auth cookies are intentionally browser-accessible; private provider keys are not.

Database and security

Applied additive migration: supabase/migrations/20260919230110_game_library_foundation.sql, on the existing game-library project (phupiulpcyxhxhxsrzpk). Inspection before application found no application tables, migrations, Auth users, or storage buckets.

Table Purpose and constraints Access
profiles Auth-user UUID primary/foreign key; unique lowercase username (3–40 letters/digits/underscores); display name ≤80, bio ≤500; optional HTTPS avatar; timestamps Public SELECT; owner-only INSERT/UPDATE
games Small reference cache; unique positive RAWG ID, slug/name, optional RAWG cover/release date, timestamps Public SELECT; browser roles cannot write
user_games Composite user/game primary key and foreign keys; status, favorite, optional integer rating 1–10, optional hours 0–100000 with one decimal, timestamps Public SELECT; owner-only INSERT/UPDATE/DELETE

All three tables have RLS enabled. Eight explicit policies cover public reads and owner writes. UPDATE policies check ownership both before and after updates. Column grants also prevent changing ownership, usernames, or timestamps. Foreign keys and supporting indexes protect/query relationships. Status is an enum: playing, played, want_to_play, dropped.

An Auth insert trigger atomically creates email-signup profiles from the new Auth ID and validated username. New social accounts and email signups without a username finish through owner-only onboarding. Existing usernames remain immutable. The trigger's SECURITY DEFINER function lives in an unexposed private schema with an empty search path; browser roles cannot execute it. User metadata supplies display input, never authorization.

Usernames are immutable in V1; display name and bio are editable. Profiles/libraries are public by design. Public profiles contain no email or password. The server verifies RAWG metadata before writing the small game cache with its private key. Library mutations use the current user's session and remain protected by RLS. There is no browser game-cache writer. Later additive migrations provide reviews, following, activity, avatars, and public game lists with the same explicit grants and RLS ownership model.

Authentication and routes

Route Behavior
/ Existing live RAWG preview: five popular games, covers, release dates, IDs
/search Server-side name search, bounded pagination, normalized cards
/games/[game] Stable numeric RAWG ID plus readable slug; details and personal library editor
/signup Email/password signup and unique username
/login Email/password or Google sign-in; trusted return destination or username onboarding
/verify-email Neutral verification resend with cooldown feedback
/forgot-password Neutral recovery-email request
/reset-password Verified-session password update and confirmation
/reset-password/success Successful reset confirmation followed by sign-in
/onboarding Authenticated first-time username selection
/account Protected display-name/bio editor
/library Protected collection with status/favorites filters, sorting, and management
/players Public player discovery and search
/feed Protected activity from followed players
/lists Protected list management and public list viewing
/profile/[username] Public profile, library statistics, and game collection; missing users return 404
/auth/callback PKCE exchange; canonical origin and restricted internal destinations
/auth/confirm Supabase token-hash verification/recovery; clean redirects

Signup creates a profile through the database trigger. With email confirmation enabled, confirm then sign in. Header sign-out clears the local session and revalidates navigation. Errors are generic and never print credentials.

Configure Supabase Site URL, callback allowlist, Google provider and SMTP using the exact setup checklist. Prepared token-hash email templates support verification/recovery across browsers. The existing PKCE path and root code fallback remain supported, with same-browser initiation required for PKCE. Hosted Auth provider/SMTP/template settings were not changed by this phase.

Verification

pnpm test
pnpm typecheck
pnpm lint
pnpm build

tests/validation.test.ts checks canonical usernames, password handling, input limits, and unwanted ownership fields.

tests/library.test.ts adds focused library validation, canonical route, upstream normalization, filter/sort, and statistics tests. pnpm test discovers and runs all regular and server-only TypeScript tests on Windows and Unix-like hosts.

scripts/verify-library.mjs is a separate explicit live test of existing authorized accounts. It requires the server-only secret key and TEST_EMAIL_A/TEST_EMAIL_B, uses temporary in-memory sign-in tokens without sending emails or changing passwords, exercises actual Server Actions/RLS, and cleans only its own game/library fixtures. See the phase report before running it.

supabase/tests/foundation_rls.sql creates transaction-local fixtures and rolls everything back. It tests profile creation, positive owner operations, cross-user profile/library write denial, anonymous write denial, cache write denial, uniqueness, immutable ownership/username, and rating/hours/bio constraints. Run only against the intended database with permission for rollback-only fixtures.

scripts/verify-auth.mjs is an explicit live test, not part of the automatic test command. It creates two real accounts and sends confirmation emails. Set TEST_EMAIL_A and TEST_EMAIL_B to fresh addresses you control in the process environment; optionally set the loopback TEST_APP_URL. Start the app, run node scripts/verify-auth.mjs, confirm BOTH emails, then enter verify. It keeps generated app passwords/tokens only in memory, exercises real forms/cookies and authenticated RLS, scans responses/assets/logs/source for private secrets, signs out, and exits. Enter stop to discard credentials. It never asks for inbox passwords. Accounts remain in Supabase; rerunning with the same addresses is not a cleanup/reset mechanism.

Implementation verification used two explicitly authorized accounts and manual confirmations. Signup, login/logout, independent-request session persistence, automatic profile creation, editing, protected/public access, actual authenticated cross-user profile restrictions, SQL library ownership checks, RAWG regression, and secret scans passed. Generated app passwords were discarded afterward. The two confirmed accounts/profiles remain; game/library SQL fixtures were rolled back. Automatic PKCE sign-in from an email link and a timed token-expiry refresh were not separately exercised end-to-end.

The security advisor reported two pre-existing execute-grant warnings on public.rls_auto_enable(), an event-trigger function. It was left unchanged; no new application-schema security warnings were reported. Details: anonymous execute warning, authenticated execute warning.

Foundation file inventory

Added:

  • src/app/actions/auth.ts, src/app/actions/profile.ts
  • src/app/signup/page.tsx, src/app/login/page.tsx, src/app/account/page.tsx, src/app/library/page.tsx, src/app/profile/[username]/page.tsx
  • src/app/auth/callback/route.ts, src/app/auth/confirm/route.ts, src/app/error.tsx, src/app/not-found.tsx
  • src/components/forms/auth-form.tsx, src/components/forms/profile-form.tsx, src/components/layout/site-header.tsx, src/components/library/library-list.tsx
  • src/lib/auth/session.ts, src/lib/data/profiles.ts, src/lib/data/library.ts, src/lib/validation/auth.ts
  • src/lib/supabase/config.ts, src/lib/supabase/server.ts, src/lib/supabase/proxy.ts, src/lib/supabase/transport.ts, src/proxy.ts
  • src/types/database.ts, src/types/forms.ts
  • supabase/migrations/20260919230110_game_library_foundation.sql, supabase/tests/foundation_rls.sql, tests/validation.test.ts, scripts/verify-auth.mjs

Changed: src/lib/supabase/client.ts, src/app/layout.tsx, src/app/page.tsx, src/app/globals.css, .env.example, .gitignore, package.json, pnpm-lock.yaml, pnpm-workspace.yaml, and this README. .env.local and the existing RAWG helper/preview were not changed. Build-generated files are ignored. The repository currently has no Git remote; nothing has been pushed or deployed.

About

A social game library for tracking, rating, reviewing, and discovering games with public player profiles and activity.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages