diff --git a/.env.example b/.env.example index bd2e2bc..e36f706 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,9 @@ RAZORPAY_KEY_SECRET=your_razorpay_secret # Supabase NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key +# Note: In Supabase Dashboard > Authentication > URL Configuration, add: +# - Site URL: https://your-production-domain.com (or http://localhost:3000 for dev) +# - Redirect URLs: https://your-production-domain.com/auth/callback (or http://localhost:3000/auth/callback for dev) # SMTP / Email SMTP_HOST=smtp.gmail.com diff --git a/.gitignore b/.gitignore index 2515836..27d133f 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,5 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -.eslintcache.env.local +# cache files +.eslintcache diff --git a/README.md b/README.md index 51715b9..33f7ed6 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,14 @@ 4. **Supabase Setup** - Create a new project at [Supabase Dashboard](https://supabase.com/dashboard) - - Enable Authentication providers (Email, etc.) + - Enable Authentication providers: + * Go to Authentication > Providers + * Enable Email provider + * Enable Google OAuth provider and configure with your Google OAuth credentials + - Configure Authentication URLs: + * Go to Authentication > URL Configuration + * Add your Site URL (e.g., `https://your-domain.com` or `http://localhost:3000`) + * Add Redirect URL: `https://your-domain.com/auth/callback` (or `http://localhost:3000/auth/callback` for development) - Create storage bucket: `assignments` 5. **Run the development server** diff --git a/package-lock.json b/package-lock.json index 97afd30..58fb027 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3071,7 +3071,6 @@ "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3082,7 +3081,6 @@ "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -3149,7 +3147,6 @@ "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", @@ -3666,7 +3663,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4086,7 +4082,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", @@ -4764,7 +4759,6 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4938,7 +4932,6 @@ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -7503,7 +7496,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7696,7 +7688,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7706,7 +7697,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -8544,7 +8534,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8710,7 +8699,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts new file mode 100644 index 0000000..12da7ec --- /dev/null +++ b/src/app/auth/callback/route.ts @@ -0,0 +1,35 @@ +import { createClient } from '@supabase/supabase-js' +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export async function GET(request: NextRequest) { + const requestUrl = new URL(request.url) + const code = requestUrl.searchParams.get('code') + const origin = requestUrl.origin + + if (code) { + const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL + const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY + + if (!supabaseUrl || !supabaseAnonKey) { + return NextResponse.redirect(`${origin}/sign?error=missing_env_vars`) + } + + const supabase = createClient(supabaseUrl, supabaseAnonKey, { + auth: { + flowType: 'pkce' + } + }) + + try { + const { error } = await supabase.auth.exchangeCodeForSession(code) + if (error) throw error + } catch (error) { + console.error('Error exchanging code for session:', error) + return NextResponse.redirect(`${origin}/sign?error=code_exchange_failed`) + } + } + + // URL to redirect to after sign in process completes + return NextResponse.redirect(`${origin}/dashboard`) +} diff --git a/src/components/signup.tsx b/src/components/signup.tsx index 3a9c94d..a36b595 100644 --- a/src/components/signup.tsx +++ b/src/components/signup.tsx @@ -65,7 +65,7 @@ export default function SignupFormDemo() { const { error } = await supabase.auth.signInWithOAuth({ provider: "google", options: { - redirectTo: `${window.location.origin}/dashboard`, + redirectTo: `${window.location.origin}/auth/callback`, }, }); if (error) throw error; diff --git a/src/lib/supabaseclient.ts b/src/lib/supabaseclient.ts index c41d707..0d50d18 100644 --- a/src/lib/supabaseclient.ts +++ b/src/lib/supabaseclient.ts @@ -6,5 +6,12 @@ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'placeholde // Only create client if we have valid environment variables export const supabase = supabaseUrl !== 'https://placeholder.supabase.co' && supabaseAnonKey !== 'placeholder-key' - ? createClient(supabaseUrl, supabaseAnonKey) + ? createClient(supabaseUrl, supabaseAnonKey, { + auth: { + flowType: 'pkce', + autoRefreshToken: true, + persistSession: true, + detectSessionInUrl: true + } + }) : null