diff --git a/src/app/api/auth/google/callback/route.js b/src/app/api/auth/google/callback/route.js index b90a74b..8a5bdf4 100644 --- a/src/app/api/auth/google/callback/route.js +++ b/src/app/api/auth/google/callback/route.js @@ -175,7 +175,7 @@ export async function GET(req) { console.log('Authorization code:', code); - const redirectUri = 'https://your-production-domain.com/api/auth/google/callback'; + const redirectUri = 'https://inboxiq-seven.vercel.app/api/auth/google/callback'; const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, diff --git a/src/app/api/auth/google/route.js b/src/app/api/auth/google/route.js index 10fe345..b474245 100644 --- a/src/app/api/auth/google/route.js +++ b/src/app/api/auth/google/route.js @@ -1,162 +1,10 @@ -// import { google } from 'googleapis'; - -// export async function GET(req) { -// try { -// console.log('Redirecting to Google OAuth consent screen'); - -// const oauth2Client = new google.auth.OAuth2( -// process.env.GOOGLE_CLIENT_ID, -// process.env.GOOGLE_CLIENT_SECRET, -// 'https://localhost:3000/api/auth/google/callback' // The same URL should be set in your Google Cloud console -// ); - -// const authUrl = oauth2Client.generateAuthUrl({ -// access_type: 'offline', -// scope: [ -// 'https://www.googleapis.com/auth/gmail.readonly', -// 'https://www.googleapis.com/auth/userinfo.profile', // User profile scope -// 'https://www.googleapis.com/auth/userinfo.email', // User email scope -// 'https://www.googleapis.com/auth/gmail.modify', -// 'https://www.googleapis.com/auth/gmail.compose', -// 'https://www.googleapis.com/auth/gmail.send', -// 'https://www.googleapis.com/auth/gmail.labels', -// 'https://www.googleapis.com/auth/gmail.compose', -// 'https://www.googleapis.com/auth/gmail.modify', -// 'https://mail.google.com/' -// ], -// prompt: 'consent', -// }); - -// // Redirect the user to the Google OAuth URL -// return new Response(null, { -// status: 302, -// headers: { -// Location: authUrl, -// }, -// }); -// } catch (error) { -// console.error('Error during Google OAuth redirection:', error); -// return new Response(JSON.stringify({ error: 'Internal server error' }), { -// status: 500, -// headers: { 'Content-Type': 'application/json' }, -// }); -// } -// } - - - - - - - - - - - - - - -// import { google } from 'googleapis'; - -// export async function GET(req) { -// try { -// console.log('Redirecting to Google OAuth consent screen'); - -// const redirectUri = -// process.env.NODE_ENV === 'development' -// ? 'http://localhost:3000/api/auth/google/callback' // Development redirect URI -// : 'https://https://inboxiq-seven.vercel.app/api/auth/google/callback'; // Production redirect URI - -// const oauth2Client = new google.auth.OAuth2( -// process.env.GOOGLE_CLIENT_ID, -// process.env.GOOGLE_CLIENT_SECRET, -// redirectUri -// ); - -// const authUrl = oauth2Client.generateAuthUrl({ -// access_type: 'offline', -// scope: [ -// 'https://www.googleapis.com/auth/gmail.readonly', -// 'https://www.googleapis.com/auth/userinfo.profile', -// 'https://www.googleapis.com/auth/userinfo.email', -// 'https://www.googleapis.com/auth/gmail.modify', -// 'https://www.googleapis.com/auth/gmail.compose', -// 'https://www.googleapis.com/auth/gmail.send', -// 'https://www.googleapis.com/auth/gmail.labels', -// 'https://mail.google.com/' -// ], -// prompt: 'consent', -// }); -// return new Response(null, { -// status: 302, -// headers: { -// Location: authUrl, -// }, -// }); -// } catch (error) { -// console.error('Error during Google OAuth redirection:', error); -// return new Response(JSON.stringify({ error: 'Internal server error' }), { -// status: 500, -// headers: { 'Content-Type': 'application/json' }, -// }); -// } -// } - - - - - - - -// import { google } from 'googleapis'; - -// export async function GET(req) { -// try { -// const redirectUri = -// process.env.NODE_ENV === 'development' -// ? 'http://localhost:3000/api/auth/google/callback' -// : 'https://inboxiq-seven.vercel.app/api/auth/google/callback'; - -// const oauth2Client = new google.auth.OAuth2( -// process.env.GOOGLE_CLIENT_ID, -// process.env.GOOGLE_CLIENT_SECRET, -// redirectUri -// ); - -// const authUrl = oauth2Client.generateAuthUrl({ -// access_type: 'offline', -// scope: [ -// 'https://www.googleapis.com/auth/gmail.readonly', -// 'https://www.googleapis.com/auth/userinfo.profile', -// 'https://www.googleapis.com/auth/userinfo.email', -// ], -// prompt: 'consent', -// }); - -// return new Response(null, { -// status: 302, -// headers: { -// Location: authUrl, -// }, -// }); -// } catch (error) { -// console.error('Error during Google OAuth redirection:', error); -// return new Response(JSON.stringify({ error: 'Internal server error' }), { -// status: 500, -// headers: { 'Content-Type': 'application/json' }, -// }); -// } -// } - - - - import { google } from 'googleapis'; export async function GET(req) { try { - const redirectUri = 'http://inboxiq-seven.vercel.app/api/auth/google/callback'; + const redirectUri = 'https://inboxiq-seven.vercel.app/api/auth/google/callback'; + const oauth2Client = new google.auth.OAuth2( diff --git a/src/app/components/header.js b/src/app/components/header.js index 3a9a8c5..1a7caf7 100644 --- a/src/app/components/header.js +++ b/src/app/components/header.js @@ -149,11 +149,12 @@ const Navbar = () => { ) : ( Login + )} diff --git a/src/middleware.js b/src/middleware.js index 21063be..d2a99c8 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -147,9 +147,171 @@ +// import { NextResponse } from 'next/server'; +// import { Redis } from '@upstash/redis'; +// import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge'; + +// /* ------------------------------------ +// * 1. Initialize Redis client +// * ---------------------------------- */ +// let redis; +// try { +// redis = new Redis({ +// url: process.env.UPSTASH_REDIS_URL, +// token: process.env.UPSTASH_REDIS_TOKEN, +// }); +// console.log('Redis initialized successfully'); +// } catch (error) { +// console.error('Error initializing Redis:', error); +// // You could set `redis = null` here to handle fallback if needed +// } + +// /* ------------------------------------ +// * 2. Helper to safely get client IP +// * ---------------------------------- */ +// function getClientIp(req) { +// const forwardedFor = req.headers.get('x-forwarded-for'); +// if (forwardedFor) { +// // Typically, x-forwarded-for can contain multiple IPs, take the first +// return forwardedFor.split(',')[0].trim(); +// } +// // Fallback to req.ip or localhost +// return req.ip || '127.0.0.1'; +// } + +// /* -------------------------------------------------- +// * 3. Rate-Limiting Middleware (Fixed Window Example) +// * ------------------------------------------------- */ +// async function rateLimitMiddleware(req) { +// // If no Redis client is available, decide your fallback: +// // - Throw a 500 +// // - Or skip rate limiting and proceed +// if (!redis) { +// console.error('Redis client is not initialized. Skipping rate limiting.'); +// return null; // null = no response, continue chain +// } + +// // Identify user route and IP +// const ip = getClientIp(req); +// const path = req.nextUrl.pathname; +// const rateLimitKey = `rate_limit:${ip}:${path}`; + +// // Configure your rate limit window and max requests +// const limit = 10; // max requests +// const window = 60; // time window in seconds + +// // Increment the request count in Redis +// try { +// const current = await redis.incr(rateLimitKey); + +// // If first request, set expiration +// if (current === 1) { +// await redis.expire(rateLimitKey, window); +// } + +// // If user exceeded the limit, return 429 +// if (current > limit) { +// // Optionally log the event +// try { +// await redis.lpush('rate_limit_exceeded_logs', JSON.stringify({ +// ip, +// path, +// timestamp: Date.now(), +// })); +// } catch (logError) { +// console.error('Error logging rate-limit exceed:', logError); +// } + +// return NextResponse.json( +// { +// error: { +// message: 'Rate limit exceeded. Try again later.', +// code: 'RATE_LIMIT_EXCEEDED', +// }, +// }, +// { +// status: 429, +// headers: { +// 'Retry-After': String(window), // Time (in s) until limit resets +// 'X-RateLimit-Limit': String(limit), // Total allowed requests +// 'X-RateLimit-Remaining': '0', // None remaining +// 'X-RateLimit-Reset': String(Date.now() + (window * 1000)), // or just window +// }, +// } +// ); +// } + +// // Within limit, attach rate-limit headers for user’s reference +// return NextResponse.next({ +// headers: { +// 'X-RateLimit-Limit': String(limit), +// 'X-RateLimit-Remaining': String(limit - current), +// 'X-RateLimit-Reset': String(Date.now() + (window * 1000)), +// }, +// }); + +// } catch (error) { +// // If something went wrong in Redis calls, return 500 or skip +// console.error('Rate limiting error:', error); +// return NextResponse.json( +// { error: 'Internal server error while rate limiting.' }, +// { status: 500 } +// ); +// } +// } + +// /* ------------------------------------ +// * 4. Auth0 Middleware +// * ------------------------------------ +// * This wraps next-edge-auth0’s `withMiddlewareAuthRequired` +// * to ensure users are authenticated before accessing the matched routes. +// */ +// function authMiddleware(req) { +// return withMiddlewareAuthRequired()(req); +// } + +// /* ------------------------------------ +// * 5. Combined Middleware +// * ------------------------------------ +// * We first run the rateLimitMiddleware. If it returns a response +// * (429 or 500), we bail out. Otherwise, we proceed to Auth0 checks. +// */ +// export async function middleware(req) { +// // 1) Rate-limiting +// const rateLimitResponse = await rateLimitMiddleware(req); +// if (rateLimitResponse) { +// // If rateLimitMiddleware returned a response, block further handling +// return rateLimitResponse; +// } + +// // 2) Auth0 Protected Routes +// return authMiddleware(req); +// } + +// /* ------------------------------------ +// * 6. Configure Matching Routes +// * ------------------------------------ +// * Adjust the matcher as needed. +// * For example, apply to all API routes, your dashboard, etc. +// */ +// export const config = { +// matcher: ['/dashboard/:path*', '/api/:path*', '/rules/:path*', '/settings'], +// }; + + + + + + + + + + + import { NextResponse } from 'next/server'; import { Redis } from '@upstash/redis'; -import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge'; +import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'; +import { connectToDatabase } from './lib/mongodb'; /* ------------------------------------ * 1. Initialize Redis client @@ -261,17 +423,55 @@ async function rateLimitMiddleware(req) { } /* ------------------------------------ - * 4. Auth0 Middleware + * 4. Database Helper: Check User Email + * ---------------------------------- */ +async function checkUserEmailInDatabase(email) { + try { + const db = await connectToDatabase(); + const user = await db.collection('users').findOne({ email }); + return user !== null; // Return true if email exists, false otherwise + } catch (error) { + console.error('Database query error:', error); + return false; // Treat as not found if there's an error + } +} + +/* ------------------------------------ + * 5. Auth0 Middleware * ------------------------------------ * This wraps next-edge-auth0’s `withMiddlewareAuthRequired` * to ensure users are authenticated before accessing the matched routes. */ -function authMiddleware(req) { - return withMiddlewareAuthRequired()(req); +async function authMiddleware(req) { + // Ensure the user is authenticated using Auth0 + const res = await withMiddlewareAuthRequired()(req); + + if (!res) { + return NextResponse.redirect('/api/auth/login'); // Redirect unauthenticated users + } + + // Extract user session + const session = await getSession(req, res); + const email = session?.user?.email; + + if (!email) { + console.error('No email found in session.'); + return NextResponse.redirect('/api/auth/login'); // Redirect if no email + } + + // Check if the email exists in the database + const emailExists = await checkUserEmailInDatabase(email); + + if (!emailExists) { + // Redirect to Google OAuth flow if email is not in database + return NextResponse.redirect('/api/auth/google'); + } + + return null; // Continue the middleware chain } /* ------------------------------------ - * 5. Combined Middleware + * 6. Combined Middleware * ------------------------------------ * We first run the rateLimitMiddleware. If it returns a response * (429 or 500), we bail out. Otherwise, we proceed to Auth0 checks. @@ -284,12 +484,19 @@ export async function middleware(req) { return rateLimitResponse; } - // 2) Auth0 Protected Routes - return authMiddleware(req); + // 2) Auth0 Authentication and Database Check + const authResponse = await authMiddleware(req); + if (authResponse) { + // If authMiddleware returned a response, block further handling + return authResponse; + } + + // 3) Proceed to the next middleware or route + return NextResponse.next(); } /* ------------------------------------ - * 6. Configure Matching Routes + * 7. Configure Matching Routes * ------------------------------------ * Adjust the matcher as needed. * For example, apply to all API routes, your dashboard, etc.