Learn by Directing AI
Unit 4

The Members Area

Step 1: Set up OAuth authentication

The museum's 400 members need to log in. Rather than managing passwords yourself -- storing hashes, handling resets, dealing with compromised credentials -- you delegate authentication to an external provider through OAuth/OIDC.

OAuth (Open Authorization) lets a user log in through a provider they already have an account with -- Google, GitHub, or a dedicated auth service. The museum never sees or stores the user's password. The provider handles identity verification and sends your application a token confirming who the user is.

This is a trade-off. You give up control over the login experience and take on a dependency on the provider's availability. In return, you never handle passwords, never worry about password storage security, and your members use credentials they already trust.

Direct Claude to integrate Clerk or NextAuth.js with OAuth. Configure at least one social provider (Google is simplest for testing). Set up the callback URLs for your development environment.

AI commonly generates OAuth implementations that skip state parameter validation -- creating CSRF vulnerabilities in the login flow itself. Review the generated code. The state parameter prevents an attacker from tricking a user into logging in to the attacker's account.

Step 2: Implement RBAC

Two roles for now: public visitor (no login needed for public pages) and member (access to exclusive content behind login).

The server enforces this. Add middleware that checks authentication status before rendering any /members/* route. If the user isn't authenticated, the server redirects them to the login page. The redirect happens at the server level -- not in the React component, not in the browser. The user never sees a flash of protected content.

Why server-side? Because client-side checks are visual guidance, not security. Hiding a navigation link in React doesn't prevent someone from typing the URL directly. Only the server can deny access before content is delivered.

Step 3: Build the members dashboard

The members area uses client-side rendering -- it's interactive, personalized to the logged-in member, and accessed only after authentication.

Direct Claude to build:

  • A dashboard showing the member's name and membership status
  • A lectures page listing available lecture recordings
  • An archives page with links to research PDFs and high-resolution collection images
  • Navigation between members-only pages

The content is behind auth. The rendering strategy is CSR because the data is user-specific and the pages are interactive. This matches the architecture decision from Unit 2.

Step 4: Implement secret management

Your application now has secrets: OAuth client IDs, client secrets, database connection strings, signing keys. These must never appear in source code, never in client-side bundles, never in git history.

Create a .env.local file for development. Add it to .gitignore. Document what goes in the production environment configuration.

AI commonly generates code with hardcoded API keys and database URLs. Review every file Claude produces. If you find a secret in source code, extract it to an environment variable before committing. A secret committed to git even once is compromised permanently -- git history doesn't disappear when you delete the file.

Step 5: Test the auth flow

Test the complete authentication flow:

  1. Log in through the OAuth provider
  2. Verify you can access member-only pages
  3. Log out
  4. Navigate directly to a members-only URL while logged out

The last step is the critical one. Check the network tab in DevTools.

You should see a 302 redirect from the members URL to the login page. The redirect happens before any page content is delivered. If you see the page content load briefly before redirecting, the auth check is happening in the React component, not in the middleware -- that's a security gap, not a cosmetic issue.

✓ Check

Check: Log out and navigate directly to a members-only URL. The server redirects to login -- not a client-side redirect, a server-side one (check the network tab for a 302 or middleware redirect, not a JS-driven redirect after the page loads).