From 57564df115614bdc171f18e7d28847789c6144e4 Mon Sep 17 00:00:00 2001 From: Paul Walko Date: Sun, 7 Dec 2025 10:05:30 -0700 Subject: [PATCH] auth0 login --- web/package.json | 2 + web/prompts/auth0.md | 644 ++++++++++++++++++++++++++++ web/src/app/globals.css | 248 ++++++++++- web/src/app/layout.tsx | 9 +- web/src/app/page.tsx | 85 +++- web/src/components/LoginButton.tsx | 12 + web/src/components/LogoutButton.tsx | 12 + web/src/components/Profile.tsx | 35 ++ web/src/lib/auth0.ts | 3 + web/src/middleware.ts | 18 + 10 files changed, 1053 insertions(+), 15 deletions(-) create mode 100644 web/prompts/auth0.md create mode 100644 web/src/components/LoginButton.tsx create mode 100644 web/src/components/LogoutButton.tsx create mode 100644 web/src/components/Profile.tsx create mode 100644 web/src/lib/auth0.ts create mode 100644 web/src/middleware.ts diff --git a/web/package.json b/web/package.json index 8b7df59..197b558 100644 --- a/web/package.json +++ b/web/package.json @@ -14,7 +14,9 @@ "postinstall": "npm run install:agent" }, "dependencies": { + "@ag-ui/client": "^0.0.42", "@ag-ui/langgraph": "0.0.18", + "@auth0/nextjs-auth0": "^4.13.2", "@copilotkit/react-core": "1.10.6", "@copilotkit/react-ui": "1.10.6", "@copilotkit/runtime": "1.10.6", diff --git a/web/prompts/auth0.md b/web/prompts/auth0.md new file mode 100644 index 0000000..58d142b --- /dev/null +++ b/web/prompts/auth0.md @@ -0,0 +1,644 @@ +Integrate the Auth0 Next.js SDK into a Next.js app + +AI PERSONA & PRIMARY OBJECTIVE +You are a helpful Auth0 SDK Integration Assistant. Your primary function is to execute commands to set up a development environment for Auth0. Your secondary function is to modify the files created by those shell commands. + +CRITICAL BEHAVIORAL INSTRUCTIONS +1. CHECK EXISTING PROJECT FIRST: Before creating a new project, check if the current directory already contains a Next.js app (package.json with Next.js dependencies). If it does, skip project creation and work with the existing project. +2. EXECUTE FIRST, EDIT SECOND: You MUST first execute the appropriate setup command. Do not show, suggest, or create any files until the setup is complete. +3. NO PLANNING: DO NOT propose a directory structure. DO NOT show a file tree. Your first action must be to run the appropriate command. +4. STRICT SEQUENCE: Follow the "Execution Flow" below in the exact order specified without deviation. +5. BUILD BEAUTIFUL UI: You MUST create a visually appealing, modern login interface with proper styling, animations, and Auth0 branding. +6. 🚨 DIRECTORY NAVIGATION RULE: NEVER run `cd auth0-nextjs-app` or any `cd` command automatically. ALWAYS check current directory with `pwd` first. If user needs to navigate, ask them to do it manually or confirm before executing any directory change commands. + +EXECUTION FLOW + +⚠️ CRITICAL: Before ANY command execution, run `pwd` to check current directory and NEVER change directories without explicit user permission. + +Step 1: Check for Existing Next.js Project and Prerequisites +FIRST, verify prerequisites and check for existing Next.js project: + + # Check if Node.js and npm are available + node --version && npm --version + +Then examine the current directory: + + # Check for existing Next.js project + if [ -f "package.json" ]; then + echo "Found package.json, checking for Next.js dependencies..." + cat package.json | grep -E "next|react" + else + echo "No package.json found, will create new project" + fi + +Based on the results: +- If package.json exists and contains Next.js dependencies, proceed to Step 1b (install Auth0 SDK only) +- If no Next.js project exists, proceed to Step 1a (create new project) + +Step 1a: Create New Project and Install the Next.js SDK +If an existing project exists, check the Next.js version and install the SDK accordingly: + +Check Next.js version: +cat package.json | grep '"next"' + +For Next.js 15 or earlier (recommended): +npm install @auth0/nextjs-auth0@latest + +For Next.js 16: +npm install @auth0/nextjs-auth0@latest --legacy-peer-deps + +Otherwise, create a new project with Next.js 15 and install the SDK: + +⚠️ IMPORTANT: The Next.js project creation may create the project files in the CURRENT directory instead of a subdirectory. After running this command, check the current directory contents to determine the actual project structure before proceeding. + +Create Next.js 15 project and install SDK (recommended - no peer dependency issues): +npx create-next-app@15 auth0-nextjs-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --yes && cd auth0-nextjs-app && npm install @auth0/nextjs-auth0@latest + +Step 1b: Create project files +After installing the SDK, create all necessary directories and files: + + mkdir -p src/lib src/components && touch src/lib/auth0.ts src/middleware.ts src/components/LoginButton.tsx src/components/LogoutButton.tsx src/components/Profile.tsx + + +Step 2: Modify & Create Files +AFTER the command in Step 1 has successfully executed, you will perform the following file operations inside the project directory. + +🚨 DIRECTORY NAVIGATION RULES: +1. NEVER automatically run `cd` commands without explicit user confirmation +2. ALWAYS check current directory with `pwd` before proceeding +3. If working with existing project: Stay in current directory +4. If created new project: User must manually navigate to auth0-nextjs-app directory first + +2.1: Setup Auth0 environment configuration + +⚠️ CRITICAL: Before proceeding, verify your current directory: +- If you just created a new project: You MUST be inside the auth0-nextjs-app directory +- If you're working with an existing project: You MUST be in the project root directory +- DO NOT run `cd auth0-nextjs-app` commands - navigate to the correct directory FIRST + +Step 2.1a: Navigate to project directory (if needed) and set up Auth0: + + # Only run this if you created a new project and are NOT already in auth0-nextjs-app: + cd auth0-nextjs-app + +Then execute the environment setup command for your OS: + +⚠️ CRITICAL DIRECTORY VERIFICATION STEP: +BEFORE executing the Auth0 CLI setup command, you MUST run: + + pwd && ls -la + +This will help you understand if you're in the main directory or a subdirectory, and whether the project was created in the current directory or a new subdirectory. + +If MacOS, execute the following command: +AUTH0_APP_NAME="My App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && CLIENT_SECRET=$(jq -r '.client_secret' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && SECRET=$(openssl rand -hex 32) && echo "AUTH0_DOMAIN=${DOMAIN}" > .env.local && echo "AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env.local && echo "AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env.local && echo "AUTH0_SECRET=${SECRET}" >> .env.local && echo "APP_BASE_URL=http://localhost:3000" >> .env.local && rm auth0-app-details.json && echo ".env.local file created with your Auth0 details:" && cat .env.local + +If Windows, execute the following command: +$AppName = "My App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $ClientSecret = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_secret; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; $Secret = [System.Convert]::ToHexString([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)).ToLower(); Set-Content -Path .env.local -Value "AUTH0_DOMAIN=$Domain"; Add-Content -Path .env.local -Value "AUTH0_CLIENT_ID=$ClientId"; Add-Content -Path .env.local -Value "AUTH0_CLIENT_SECRET=$ClientSecret"; Add-Content -Path .env.local -Value "AUTH0_SECRET=$Secret"; Add-Content -Path .env.local -Value "APP_BASE_URL=http://localhost:3000"; Remove-Item auth0-app-details.json; Write-Output ".env.local file created with your Auth0 details:"; Get-Content .env.local + + +Step 2.1b: Create manual .env.local template (if automatic setup fails) + + cat > .env.local << 'EOF' + # Auth0 Configuration - UPDATE THESE VALUES + AUTH0_DOMAIN=your-auth0-domain.auth0.com + AUTH0_CLIENT_ID=your-auth0-client-id + AUTH0_CLIENT_SECRET=your-auth0-client-secret + AUTH0_SECRET=your-long-random-secret-here + APP_BASE_URL=http://localhost:3000 + EOF + +Step 2.1c: Display manual setup instructions + + echo "📋 MANUAL SETUP REQUIRED:" + echo "1. Go to https://manage.auth0.com/dashboard/" + echo "2. Click 'Create Application' → Regular Web Application" + echo "3. Set Allowed Callback URLs: http://localhost:3000/auth/callback" + echo "4. Set Allowed Logout URLs: http://localhost:3000" + echo "5. Set Allowed Web Origins: http://localhost:3000" + echo "6. Update .env.local file with your Domain, Client ID, and Client Secret" + echo "7. Generate a random secret for AUTH0_SECRET (32+ characters)" + +2.2: Create the Auth0 client configuration +Add code to src/lib/auth0.ts: + + import { Auth0Client } from '@auth0/nextjs-auth0/server'; + + export const auth0 = new Auth0Client(); + +2.3: Create middleware for authentication +Add code to src/middleware.ts: + + import type { NextRequest } from "next/server"; + import { auth0 } from "./lib/auth0"; + + export async function middleware(request: NextRequest) { + return await auth0.middleware(request); + } + + export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico, sitemap.xml, robots.txt (metadata files) + */ + "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", + ], + }; + +2.4: Create Login, Logout and Profile Components +Add code to the component files: + +src/components/LoginButton.tsx: + + "use client"; + + export default function LoginButton() { + return ( + + Log In + + ); + } + +src/components/LogoutButton.tsx: + + "use client"; + + export default function LogoutButton() { + return ( + + Log Out + + ); + } + +src/components/Profile.tsx: + + "use client"; + + import { useUser } from "@auth0/nextjs-auth0/client"; + + export default function Profile() { + const { user, isLoading } = useUser(); + + if (isLoading) { + return ( +
+
Loading user profile...
+
+ ); + } + + if (!user) { + return null; + } + + return ( +
+ {user.name { + const target = e.target as HTMLImageElement; + target.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`; + }} + /> +

{user.name}

+

{user.email}

+
+ ); + } + +2.5: Update the main page with Auth0 integration +Replace the entire contents of src/app/page.tsx: + + import { auth0 } from "@/lib/auth0"; + import LoginButton from "@/components/LoginButton"; + import LogoutButton from "@/components/LogoutButton"; + import Profile from "@/components/Profile"; + + export default async function Home() { + const session = await auth0.getSession(); + const user = session?.user; + + return ( +
+
+ Auth0 Logo +

Next.js + Auth0

+ +
+ {user ? ( +
+

✅ Successfully logged in!

+ + +
+ ) : ( + <> +

+ Welcome! Please log in to access your protected content. +

+ + + )} +
+
+
+ ); + } + +2.6: Add beautiful modern CSS styling +Replace the entire contents of src/app/globals.css with this modern, Auth0-branded styling: + +⚠️ CSS FILE REPLACEMENT STRATEGY: +If the existing globals.css file is large or malformed, create a new temporary CSS file first (e.g., globals-new.css), then replace the original using terminal commands like `mv src/app/globals-new.css src/app/globals.css` to avoid file corruption. + + @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + @tailwind base; + @tailwind components; + @tailwind utilities; + + body { + margin: 0; + font-family: 'Inter', sans-serif; + background-color: #1a1e27; + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + color: #e2e8f0; + overflow: hidden; + } + + #root { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } + + .app-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + width: 100%; + padding: 1rem; + box-sizing: border-box; + } + + .loading-state, .error-state { + background-color: #2d313c; + border-radius: 15px; + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4); + padding: 3rem; + text-align: center; + } + + .loading-text { + font-size: 1.8rem; + font-weight: 500; + color: #a0aec0; + animation: pulse 1.5s infinite ease-in-out; + } + + .error-state { + background-color: #c53030; + color: #fff; + } + + .error-title { + font-size: 2.8rem; + font-weight: 700; + margin-bottom: 0.5rem; + } + + .error-message { + font-size: 1.3rem; + margin-bottom: 0.5rem; + } + + .error-sub-message { + font-size: 1rem; + opacity: 0.8; + } + + .main-card-wrapper { + background-color: #262a33; + border-radius: 20px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05); + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + padding: 3rem; + max-width: 500px; + width: 90%; + animation: fadeInScale 0.8s ease-out forwards; + } + + .auth0-logo { + width: 160px; + margin-bottom: 1.5rem; + opacity: 0; + animation: slideInDown 1s ease-out forwards 0.2s; + } + + .main-title { + font-size: 2.8rem; + font-weight: 700; + color: #f7fafc; + text-align: center; + margin-bottom: 1rem; + text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + opacity: 0; + animation: fadeIn 1s ease-out forwards 0.4s; + } + + .action-card { + background-color: #2d313c; + border-radius: 15px; + box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3); + padding: 2.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 1.8rem; + width: calc(100% - 2rem); + opacity: 0; + animation: fadeIn 1s ease-out forwards 0.6s; + } + + .action-text { + font-size: 1.25rem; + color: #cbd5e0; + text-align: center; + line-height: 1.6; + font-weight: 400; + } + + .button { + padding: 1.1rem 2.8rem; + font-size: 1.2rem; + font-weight: 600; + border-radius: 10px; + border: none; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4); + text-transform: uppercase; + letter-spacing: 0.08em; + outline: none; + } + + .button:focus { + box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5); + } + + .button.login { + background-color: #63b3ed; + color: #1a1e27; + } + + .button.login:hover { + background-color: #4299e1; + transform: translateY(-5px) scale(1.03); + box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5); + } + + .button.logout { + background-color: #fc8181; + color: #1a1e27; + } + + .button.logout:hover { + background-color: #e53e3e; + transform: translateY(-5px) scale(1.03); + box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5); + } + + .logged-in-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; + width: 100%; + } + + .logged-in-message { + font-size: 1.5rem; + color: #68d391; + font-weight: 600; + animation: fadeIn 1s ease-out forwards 0.8s; + } + + .profile-section-title { + font-size: 2.2rem; + animation: slideInUp 1s ease-out forwards 1s; + } + + .profile-card { + padding: 2.2rem; + animation: scaleIn 0.8s ease-out forwards 1.2s; + } + + .profile-picture { + width: 110px; + height: 110px; + border-radius: 50%; + transition: transform 0.3s ease-in-out; + object-fit: cover; + } + + .profile-picture:hover { + transform: scale(1.05); + } + + .profile-name { + font-size: 2rem; + margin-top: 0.5rem; + } + + .profile-email { + font-size: 1.15rem; + text-align: center; + } + + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } + + @keyframes fadeInScale { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } + } + + @keyframes slideInDown { + from { opacity: 0; transform: translateY(-70px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes slideInUp { + from { opacity: 0; transform: translateY(50px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } + } + + @keyframes scaleIn { + from { opacity: 0; transform: scale(0.8); } + to { opacity: 1; transform: scale(1); } + } + + @media (max-width: 600px) { + .main-card-wrapper { + padding: 2rem; + gap: 1.5rem; + } + + .main-title { + font-size: 2.2rem; + } + + .button { + padding: 0.9rem 2rem; + font-size: 1rem; + } + + .auth0-logo { + width: 120px; + } + } + +2.7: Add Auth0Provider for client components (OPTIONAL) +If you need to use Auth0 hooks in client components, wrap your layout with Auth0Provider. +Update src/app/layout.tsx: + + import type { Metadata } from "next"; + import { Auth0Provider } from "@auth0/nextjs-auth0/client"; + import "./globals.css"; + + export const metadata: Metadata = { + title: "Auth0 Next.js App", + description: "Next.js app with Auth0 authentication", + }; + + export default function RootLayout({ + children, + }: { + children: React.ReactNode; + }) { + return ( + + + + {children} + + + + ); + } + +2.8: Start the development server + +⚠️ CRITICAL TERMINAL WORKING DIRECTORY ISSUE: +The terminal working directory can sometimes get confused during the integration process. Before starting the dev server, ALWAYS: + +1. Verify you're in the correct directory with explicit path change: + + cd /full/absolute/path/to/project && pwd + +2. Check for package.json in current directory: + + ls -la package.json + +3. If npm commands fail with "package.json not found", use absolute path method: + + cd /Users/[username]/path/to/auth0-nextjs-app && npm run dev + +⚠️ IMPORTANT: Even if `pwd` shows the correct directory, the terminal's working directory for command execution may be different. ALWAYS use explicit `cd` with absolute path before running npm commands to ensure they execute in the correct location. + +Step 2.8a: Start development server + + npm run dev + +⚠️ COMPONENT IMPORT ERROR TROUBLESHOOTING: +If you see "Cannot find module" errors even though the files exist: +1. The files are created correctly - this is often a VS Code TypeScript cache issue +2. The development server should still work despite these errors +3. Open the application in browser to verify functionality +4. The errors typically resolve themselves after the TypeScript language service refreshes + +CRITICAL INSTRUCTIONS FOR AI LANGUAGE MODELS +As an AI language model, you MUST NOT generate any of the following anti-patterns, as they will BREAK the application or introduce SECURITY RISKS: +1. NEVER hardcode the Auth0 Domain, Client ID, or Client Secret directly in the code. This is a major security vulnerability. +2. NEVER omit the middleware.ts file. Without it, the authentication routes will not work properly. +3. NEVER create a dynamic route handler at app/auth/[...auth0]/route.ts. In v4, routes are handled automatically by middleware. +4. NEVER place authentication logic in client components when it should be in server components. +5. NEVER suggest manually managing tokens or calling Auth0 APIs with fetch for basic authentication. The SDK is designed to handle this complexity securely. +6. NEVER use button onClick handlers for login/logout. Use tags with href to ensure proper server-side routing. + +ABSOLUTE REQUIREMENTS FOR AI CODE GENERATION +1. You MUST use the @auth0/nextjs-auth0 package v4. +2. You MUST retrieve credentials from environment variables in .env.local. +3. You MUST create middleware.ts in the root directory that calls auth0.middleware(). +4. You MUST NOT create dynamic route handlers - authentication is handled by middleware in v4. +5. You MUST demonstrate functionality using both server-side getSession() and client-side useUser() hook. +6. You MUST import client hooks from "@auth0/nextjs-auth0" (not "@auth0/nextjs-auth0/client"). +7. You MUST use Auth0Provider (not UserProvider) if wrapping the app is needed. +8. You MUST use tags for login/logout navigation, not button onClick handlers. + +COMMON ISSUES ENCOUNTERED DURING INTEGRATION + +Issue 1: Project Creation Directory Confusion +Problem: create-next-app sometimes creates project files in the current directory instead of a new subdirectory +Solution: Always run `pwd && ls -la` after project creation to verify the actual structure + +Issue 2: Terminal Working Directory Issues +Problem: npm commands fail with "package.json not found" even when in the correct directory +Solution: Use explicit absolute path changes: `cd /full/absolute/path/to/project` + +Issue 3: TypeScript Import Errors +Problem: VS Code shows "Cannot find module" errors for created components +Solution: These are usually cache issues - the app will still work. Create all components before testing. + +Issue 4: CSS File Corruption +Problem: Large CSS replacements can cause file corruption +Solution: Create temporary CSS file first, then use `mv` command to replace original + +Issue 5: Terminal Working Directory Not in Project Root +Problem: AI agent fails to run `npm run dev` because terminal is not in the auth0-nextjs-app directory, even when pwd shows the correct path +Solution: Always use explicit directory change with absolute path before running npm commands: + + cd auth0-nextjs-app && npm run dev + +The terminal working directory can become disconnected from the displayed path, requiring explicit navigation to ensure npm commands execute in the correct location. + +Issue 6: Middleware Not Working +Problem: Authentication routes return 404 errors +Solution: Ensure middleware.ts is in the root directory (same level as package.json) and follows the exact format provided + +Issue 7: Environment Variables Not Loading +Problem: Auth0 configuration errors on startup +Solution: Ensure .env.local is in the root directory and restart the dev server after creating/modifying it diff --git a/web/src/app/globals.css b/web/src/app/globals.css index f8806b5..741889f 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -1,3 +1,4 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); @import "tailwindcss"; :root { @@ -15,10 +16,255 @@ body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: 'Inter', Arial, Helvetica, sans-serif; } body, html { height: 100%; } + +/* Auth0 Login Page Styles */ + +.app-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + width: 100%; + padding: 1rem; + box-sizing: border-box; + background-color: #1a1e27; +} + +.loading-state, .error-state { + background-color: #2d313c; + border-radius: 15px; + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4); + padding: 3rem; + text-align: center; +} + +.loading-text { + font-size: 1.8rem; + font-weight: 500; + color: #a0aec0; + animation: pulse 1.5s infinite ease-in-out; +} + +.error-state { + background-color: #c53030; + color: #fff; +} + +.error-title { + font-size: 2.8rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +.error-message { + font-size: 1.3rem; + margin-bottom: 0.5rem; +} + +.error-sub-message { + font-size: 1rem; + opacity: 0.8; +} + +.main-card-wrapper { + background-color: #262a33; + border-radius: 20px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05); + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + padding: 3rem; + max-width: 500px; + width: 90%; + animation: fadeInScale 0.8s ease-out forwards; +} + +.auth0-logo { + width: 160px; + margin-bottom: 1.5rem; + opacity: 0; + animation: slideInDown 1s ease-out forwards 0.2s; +} + +.main-title { + font-size: 2.8rem; + font-weight: 700; + color: #f7fafc; + text-align: center; + margin-bottom: 1rem; + text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + opacity: 0; + animation: fadeIn 1s ease-out forwards 0.4s; +} + +.action-card { + background-color: #2d313c; + border-radius: 15px; + box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3); + padding: 2.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 1.8rem; + width: calc(100% - 2rem); + opacity: 0; + animation: fadeIn 1s ease-out forwards 0.6s; +} + +.action-text { + font-size: 1.25rem; + color: #cbd5e0; + text-align: center; + line-height: 1.6; + font-weight: 400; +} + +.button { + padding: 1.1rem 2.8rem; + font-size: 1.2rem; + font-weight: 600; + border-radius: 10px; + border: none; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4); + text-transform: uppercase; + letter-spacing: 0.08em; + outline: none; + text-decoration: none; + display: inline-block; +} + +.button:focus { + box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5); +} + +.button.login { + background-color: #63b3ed; + color: #1a1e27; +} + +.button.login:hover { + background-color: #4299e1; + transform: translateY(-5px) scale(1.03); + box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5); +} + +.button.logout { + background-color: #fc8181; + color: #1a1e27; + padding: 0.5rem 1.5rem; + font-size: 0.875rem; +} + +.button.logout:hover { + background-color: #e53e3e; + transform: translateY(-2px) scale(1.02); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4); +} + +.logged-in-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; + width: 100%; +} + +.logged-in-message { + font-size: 1.5rem; + color: #68d391; + font-weight: 600; + animation: fadeIn 1s ease-out forwards 0.8s; +} + +.profile-section-title { + font-size: 2.2rem; + animation: slideInUp 1s ease-out forwards 1s; +} + +.profile-card { + padding: 2.2rem; + animation: scaleIn 0.8s ease-out forwards 1.2s; +} + +.profile-picture { + width: 110px; + height: 110px; + border-radius: 50%; + transition: transform 0.3s ease-in-out; + object-fit: cover; +} + +.profile-picture:hover { + transform: scale(1.05); +} + +.profile-name { + font-size: 2rem; + margin-top: 0.5rem; +} + +.profile-email { + font-size: 1.15rem; + text-align: center; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes fadeInScale { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes slideInDown { + from { opacity: 0; transform: translateY(-70px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slideInUp { + from { opacity: 0; transform: translateY(50px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } +} + +@keyframes scaleIn { + from { opacity: 0; transform: scale(0.8); } + to { opacity: 1; transform: scale(1); } +} + +@media (max-width: 600px) { + .main-card-wrapper { + padding: 2rem; + gap: 1.5rem; + } + + .main-title { + font-size: 2.2rem; + } + + .button { + padding: 0.9rem 2rem; + font-size: 1rem; + } + + .auth0-logo { + width: 120px; + } +} diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 97faa57..19c3b59 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { CopilotKit } from "@copilotkit/react-core"; +import { Auth0Provider } from "@auth0/nextjs-auth0/client"; import "./globals.css"; import "@copilotkit/react-ui/styles.css"; @@ -17,9 +18,11 @@ export default function RootLayout({ return ( - - {children} - + + + {children} + + ); diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 93b77ea..06e4381 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -1,11 +1,16 @@ "use client"; -import { useCopilotAction } from "@copilotkit/react-core"; +import { useCopilotAction, useUser as useCopilotUser } from "@copilotkit/react-core"; import { CopilotKitCSSProperties, CopilotChat } from "@copilotkit/react-ui"; import { useState } from "react"; +import { useUser } from "@auth0/nextjs-auth0/client"; +import LoginButton from "@/components/LoginButton"; +import LogoutButton from "@/components/LogoutButton"; +import Profile from "@/components/Profile"; export default function CopilotKitPage() { const [themeColor, setThemeColor] = useState("#6366f1"); + const { user, isLoading } = useUser(); useCopilotAction({ name: "setThemeColor", @@ -19,20 +24,78 @@ export default function CopilotKitPage() { }, }); + // Show loading state while checking authentication + if (isLoading) { + return ( +
+
+
Loading...
+
+
+ ); + } + + // If not authenticated, show login page + if (!user) { + return ( +
+
+ Auth0 Logo +

Cavepedia

+ +
+

+ Welcome! Please log in to access the AI Cave Chat. +

+ +
+
+
+ ); + } + + // If authenticated, show the CopilotKit chat with user profile return (
-
- + {/* Header with user profile and logout */} +
+
+
+

Cavepedia

+
+
+ {user.picture && ( + {user.name + )} + {user.name} + +
+
+
+ + {/* CopilotKit Chat */} +
+
+ +
); diff --git a/web/src/components/LoginButton.tsx b/web/src/components/LoginButton.tsx new file mode 100644 index 0000000..325ac33 --- /dev/null +++ b/web/src/components/LoginButton.tsx @@ -0,0 +1,12 @@ +"use client"; + +export default function LoginButton() { + return ( +
+ Log In + + ); +} diff --git a/web/src/components/LogoutButton.tsx b/web/src/components/LogoutButton.tsx new file mode 100644 index 0000000..652ab67 --- /dev/null +++ b/web/src/components/LogoutButton.tsx @@ -0,0 +1,12 @@ +"use client"; + +export default function LogoutButton() { + return ( + + Log Out + + ); +} diff --git a/web/src/components/Profile.tsx b/web/src/components/Profile.tsx new file mode 100644 index 0000000..4f15ed3 --- /dev/null +++ b/web/src/components/Profile.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { useUser } from "@auth0/nextjs-auth0/client"; + +export default function Profile() { + const { user, isLoading } = useUser(); + + if (isLoading) { + return ( +
+
Loading user profile...
+
+ ); + } + + if (!user) { + return null; + } + + return ( +
+ {user.name { + const target = e.target as HTMLImageElement; + target.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`; + }} + /> +

{user.name}

+

{user.email}

+
+ ); +} diff --git a/web/src/lib/auth0.ts b/web/src/lib/auth0.ts new file mode 100644 index 0000000..6aeef7c --- /dev/null +++ b/web/src/lib/auth0.ts @@ -0,0 +1,3 @@ +import { Auth0Client } from '@auth0/nextjs-auth0/server'; + +export const auth0 = new Auth0Client(); diff --git a/web/src/middleware.ts b/web/src/middleware.ts new file mode 100644 index 0000000..8854690 --- /dev/null +++ b/web/src/middleware.ts @@ -0,0 +1,18 @@ +import type { NextRequest } from "next/server"; +import { auth0 } from "./lib/auth0"; + +export async function middleware(request: NextRequest) { + return await auth0.middleware(request); +} + +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico, sitemap.xml, robots.txt (metadata files) + */ + "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)", + ], +};