diff --git a/public/test-auth.html b/public/test-auth.html
new file mode 100644
index 0000000..a12361d
--- /dev/null
+++ b/public/test-auth.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+ Authentication Test Page
+
+
+
+ Authentication & API Test Page
+
+
+
Authentication Status
+
+
+
+
+
+
API Endpoint Tests
+
+
+
+
+
+
Manual Login Instructions
+
+
Test Credentials:
+
Email: admin@localhost.com
+
Password: admin123456
+
Open Sign-in Page
+
+
+
+
+
+
diff --git a/src/app/api/debug-auth/route.js b/src/app/api/debug-auth/route.js
new file mode 100644
index 0000000..e63f04e
--- /dev/null
+++ b/src/app/api/debug-auth/route.js
@@ -0,0 +1,37 @@
+import { auth } from "@/lib/auth"
+import { NextResponse } from "next/server"
+
+export const GET = auth(async (req) => {
+ try {
+ console.log("=== DEBUG AUTH ENDPOINT ===")
+ console.log("Request URL:", req.url)
+ console.log("Auth object:", req.auth)
+
+ if (!req.auth?.user) {
+ return NextResponse.json({
+ error: "No session found",
+ debug: {
+ hasAuth: !!req.auth,
+ authKeys: req.auth ? Object.keys(req.auth) : [],
+ }
+ }, { status: 401 })
+ }
+
+ return NextResponse.json({
+ message: "Authenticated",
+ user: req.auth.user,
+ debug: {
+ authKeys: Object.keys(req.auth),
+ userKeys: Object.keys(req.auth.user)
+ }
+ })
+
+ } catch (error) {
+ console.error("Auth debug error:", error)
+ return NextResponse.json({
+ error: "Auth error",
+ message: error.message,
+ stack: error.stack
+ }, { status: 500 })
+ }
+})
diff --git a/src/app/api/tasks/route.js b/src/app/api/tasks/route.js
index 7dc61a0..24d63bc 100644
--- a/src/app/api/tasks/route.js
+++ b/src/app/api/tasks/route.js
@@ -1,6 +1,7 @@
import db from "@/lib/db";
import { NextResponse } from "next/server";
-import { withUserAuth } from "@/lib/middleware/auth";
+import { withUserAuth, withReadAuth } from "@/lib/middleware/auth";
+import { getAllTaskTemplates } from "@/lib/queries/tasks";
// POST: create new template
async function createTaskHandler(req) {
@@ -20,5 +21,12 @@ async function createTaskHandler(req) {
return NextResponse.json({ success: true });
}
+// GET: Get all task templates
+async function getTasksHandler(req) {
+ const templates = getAllTaskTemplates();
+ return NextResponse.json(templates);
+}
+
// Protected routes - require authentication
+export const GET = withReadAuth(getTasksHandler);
export const POST = withUserAuth(createTaskHandler);
diff --git a/src/lib/middleware/auth.js b/src/lib/middleware/auth.js
index 1adcba7..70169c2 100644
--- a/src/lib/middleware/auth.js
+++ b/src/lib/middleware/auth.js
@@ -10,20 +10,21 @@ const ROLE_HIERARCHY = {
}
export function withAuth(handler, options = {}) {
- return async (req, context) => {
+ return auth(async (req) => {
try {
- const session = await auth(req)
-
// Check if user is authenticated
- if (!session?.user) {
+ if (!req.auth?.user) {
+ console.log("No session found for request to:", req.url)
return NextResponse.json(
{ error: "Authentication required" },
{ status: 401 }
)
}
+ console.log("Session found for user:", req.auth.user.email)
+
// Check role-based permissions (without database access)
- if (options.requiredRole && !hasPermission(session.user.role, options.requiredRole)) {
+ if (options.requiredRole && !hasPermission(req.auth.user.role, options.requiredRole)) {
return NextResponse.json(
{ error: "Insufficient permissions" },
{ status: 403 }
@@ -32,14 +33,14 @@ export function withAuth(handler, options = {}) {
// Add user info to request
req.user = {
- id: session.user.id,
- email: session.user.email,
- name: session.user.name,
- role: session.user.role
+ id: req.auth.user.id,
+ email: req.auth.user.email,
+ name: req.auth.user.name,
+ role: req.auth.user.role
}
// Call the original handler
- return await handler(req, context)
+ return await handler(req)
} catch (error) {
console.error("Auth middleware error:", error)
return NextResponse.json(
@@ -47,7 +48,7 @@ export function withAuth(handler, options = {}) {
{ status: 500 }
)
}
- }
+ })
}
export function hasPermission(userRole, requiredRole) {
@@ -64,12 +65,12 @@ export function withUserAuth(handler) {
return withAuth(handler, { requiredRole: 'user' })
}
+// Helper for admin-level operations
+export function withAdminAuth(handler) {
+ return withAuth(handler, { requiredRole: 'admin' })
+}
+
// Helper for project manager operations
export function withManagerAuth(handler) {
return withAuth(handler, { requiredRole: 'project_manager' })
}
-
-// Helper for admin operations
-export function withAdminAuth(handler) {
- return withAuth(handler, { requiredRole: 'admin' })
-}
diff --git a/test-auth-api.mjs b/test-auth-api.mjs
new file mode 100644
index 0000000..a88b3f4
--- /dev/null
+++ b/test-auth-api.mjs
@@ -0,0 +1,109 @@
+// Test authenticated API access using NextAuth.js client-side approach
+
+const BASE_URL = 'http://localhost:3000';
+
+async function testAuthenticatedAPI() {
+ console.log('š Testing Authenticated API Access\n');
+
+ try {
+ // Test 1: Check if server is running
+ console.log('1ļøā£ Checking server status...');
+ const healthResponse = await fetch(`${BASE_URL}/api/auth/session`);
+ console.log(`Server status: ${healthResponse.status}`);
+
+ if (!healthResponse.ok) {
+ console.log('ā Server not responding properly');
+ return;
+ }
+
+ // Test 2: Test unauthenticated access to protected endpoints
+ console.log('\n2ļøā£ Testing unauthenticated access...');
+ const protectedEndpoints = [
+ '/api/projects',
+ '/api/contracts',
+ '/api/tasks',
+ '/api/project-tasks'
+ ];
+
+ for (const endpoint of protectedEndpoints) {
+ const response = await fetch(`${BASE_URL}${endpoint}`);
+ console.log(`${endpoint}: ${response.status} ${response.status === 401 ? 'ā
(properly protected)' : 'ā (not protected)'}`);
+ }
+
+ // Test 3: Check protected pages
+ console.log('\n3ļøā£ Testing protected pages...');
+ const protectedPages = ['/projects', '/contracts', '/tasks'];
+
+ for (const page of protectedPages) {
+ const response = await fetch(`${BASE_URL}${page}`, {
+ redirect: 'manual'
+ });
+
+ if (response.status === 302) {
+ const location = response.headers.get('location');
+ if (location && location.includes('/auth/signin')) {
+ console.log(`${page}: ā
Properly redirects to sign-in`);
+ } else {
+ console.log(`${page}: ā ļø Redirects to: ${location}`);
+ }
+ } else if (response.status === 200) {
+ console.log(`${page}: ā Accessible without authentication`);
+ } else {
+ console.log(`${page}: ā Status ${response.status}`);
+ }
+ }
+
+ // Test 4: Test sign-in page accessibility
+ console.log('\n4ļøā£ Testing sign-in page...');
+ const signinResponse = await fetch(`${BASE_URL}/auth/signin`);
+ if (signinResponse.ok) {
+ console.log('ā
Sign-in page accessible');
+ const content = await signinResponse.text();
+ const hasEmailField = content.includes('name="email"') || content.includes('id="email"');
+ const hasPasswordField = content.includes('name="password"') || content.includes('id="password"');
+ console.log(` Email field: ${hasEmailField ? 'ā
' : 'ā'}`);
+ console.log(` Password field: ${hasPasswordField ? 'ā
' : 'ā'}`);
+ } else {
+ console.log('ā Sign-in page not accessible');
+ }
+
+ // Test 5: Check NextAuth.js providers endpoint
+ console.log('\n5ļøā£ Testing NextAuth.js configuration...');
+ const providersResponse = await fetch(`${BASE_URL}/api/auth/providers`);
+ if (providersResponse.ok) {
+ const providers = await providersResponse.json();
+ console.log('ā
NextAuth.js providers endpoint accessible');
+ console.log('Available providers:', Object.keys(providers));
+ } else {
+ console.log('ā NextAuth.js providers endpoint failed');
+ }
+
+ // Test 6: Check CSRF token endpoint
+ console.log('\n6ļøā£ Testing CSRF token...');
+ const csrfResponse = await fetch(`${BASE_URL}/api/auth/csrf`);
+ if (csrfResponse.ok) {
+ const csrf = await csrfResponse.json();
+ console.log('ā
CSRF token endpoint accessible');
+ console.log('CSRF token available:', !!csrf.csrfToken);
+ } else {
+ console.log('ā CSRF token endpoint failed');
+ }
+
+ console.log('\nšÆ Manual Testing Instructions:');
+ console.log('1. Open browser to: http://localhost:3000/auth/signin');
+ console.log('2. Use credentials:');
+ console.log(' Email: admin@localhost.com');
+ console.log(' Password: admin123456');
+ console.log('3. After login, test these pages:');
+ protectedPages.forEach(page => {
+ console.log(` - http://localhost:3000${page}`);
+ });
+ console.log('4. Test API endpoints with browser dev tools or Postman');
+
+ } catch (error) {
+ console.error('ā Test failed with error:', error.message);
+ }
+}
+
+// Run the test
+testAuthenticatedAPI();
diff --git a/test-logged-in-flow.mjs b/test-logged-in-flow.mjs
new file mode 100644
index 0000000..4e0b70d
--- /dev/null
+++ b/test-logged-in-flow.mjs
@@ -0,0 +1,206 @@
+// Test authenticated flow without external dependencies
+
+const BASE_URL = 'http://localhost:3000';
+
+// Test data
+const TEST_CREDENTIALS = {
+ email: 'admin@localhost.com',
+ password: 'admin123456'
+};
+
+// Helper function to extract cookies from response
+function extractCookies(response) {
+ const cookies = response.headers.raw()['set-cookie'];
+ if (!cookies) return '';
+
+ return cookies
+ .map(cookie => cookie.split(';')[0])
+ .join('; ');
+}
+
+// Helper function to make authenticated requests
+async function makeAuthenticatedRequest(url, options = {}, cookies = '') {
+ return fetch(url, {
+ ...options,
+ headers: {
+ 'Cookie': cookies,
+ 'Content-Type': 'application/json',
+ ...options.headers
+ }
+ });
+}
+
+async function testCompleteAuthenticatedFlow() {
+ console.log('š Testing Complete Authenticated Flow\n');
+
+ try {
+ // Step 1: Get CSRF token from sign-in page
+ console.log('1ļøā£ Getting CSRF token...');
+ const signinResponse = await fetch(`${BASE_URL}/auth/signin`);
+ const signinHtml = await signinResponse.text();
+
+ // Extract CSRF token (NextAuth.js typically includes it in the form)
+ const csrfMatch = signinHtml.match(/name="csrfToken" value="([^"]+)"/);
+ const csrfToken = csrfMatch ? csrfMatch[1] : null;
+
+ if (!csrfToken) {
+ console.log('ā Could not extract CSRF token');
+ return;
+ }
+
+ console.log('ā
CSRF token extracted');
+ const initialCookies = extractCookies(signinResponse);
+
+ // Step 2: Attempt login
+ console.log('\n2ļøā£ Attempting login...');
+ const loginResponse = await fetch(`${BASE_URL}/api/auth/callback/credentials`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Cookie': initialCookies
+ },
+ body: new URLSearchParams({
+ csrfToken,
+ email: TEST_CREDENTIALS.email,
+ password: TEST_CREDENTIALS.password,
+ callbackUrl: `${BASE_URL}/projects`,
+ json: 'true'
+ }),
+ redirect: 'manual'
+ });
+
+ console.log(`Login response status: ${loginResponse.status}`);
+
+ if (loginResponse.status === 200) {
+ const loginResult = await loginResponse.json();
+ console.log('Login result:', loginResult);
+
+ if (loginResult.url) {
+ console.log('ā
Login successful, redirecting to:', loginResult.url);
+ } else if (loginResult.error) {
+ console.log('ā Login failed:', loginResult.error);
+ return;
+ }
+ } else if (loginResponse.status === 302) {
+ console.log('ā
Login successful (redirect)');
+ } else {
+ console.log('ā Login failed with status:', loginResponse.status);
+ const errorText = await loginResponse.text();
+ console.log('Error response:', errorText.substring(0, 500));
+ return;
+ }
+
+ // Get session cookies
+ const sessionCookies = extractCookies(loginResponse) || initialCookies;
+ console.log('Session cookies:', sessionCookies ? 'Present' : 'Missing');
+
+ // Step 3: Test session endpoint
+ console.log('\n3ļøā£ Testing session endpoint...');
+ const sessionResponse = await makeAuthenticatedRequest(
+ `${BASE_URL}/api/auth/session`,
+ {},
+ sessionCookies
+ );
+
+ if (sessionResponse.ok) {
+ const session = await sessionResponse.json();
+ console.log('ā
Session data:', JSON.stringify(session, null, 2));
+ } else {
+ console.log('ā Session check failed:', sessionResponse.status);
+ }
+
+ // Step 4: Test protected pages
+ console.log('\n4ļøā£ Testing protected pages...');
+ const protectedPages = ['/projects', '/contracts', '/tasks'];
+
+ for (const page of protectedPages) {
+ const pageResponse = await makeAuthenticatedRequest(
+ `${BASE_URL}${page}`,
+ {},
+ sessionCookies
+ );
+
+ if (pageResponse.ok) {
+ console.log(`ā
${page} - accessible`);
+ } else if (pageResponse.status === 302) {
+ console.log(`ā ļø ${page} - redirected (status: 302)`);
+ } else {
+ console.log(`ā ${page} - failed (status: ${pageResponse.status})`);
+ }
+ }
+
+ // Step 5: Test API endpoints
+ console.log('\n5ļøā£ Testing API endpoints...');
+ const apiEndpoints = [
+ { url: '/api/projects', method: 'GET' },
+ { url: '/api/contracts', method: 'GET' },
+ { url: '/api/tasks', method: 'GET' },
+ { url: '/api/tasks/templates', method: 'GET' }
+ ];
+
+ for (const endpoint of apiEndpoints) {
+ const apiResponse = await makeAuthenticatedRequest(
+ `${BASE_URL}${endpoint.url}`,
+ { method: endpoint.method },
+ sessionCookies
+ );
+
+ if (apiResponse.ok) {
+ const data = await apiResponse.json();
+ console.log(`ā
${endpoint.method} ${endpoint.url} - success (${Array.isArray(data) ? data.length : 'object'} items)`);
+ } else if (apiResponse.status === 401) {
+ console.log(`ā ${endpoint.method} ${endpoint.url} - unauthorized (status: 401)`);
+ } else {
+ console.log(`ā ${endpoint.method} ${endpoint.url} - failed (status: ${apiResponse.status})`);
+ const errorText = await apiResponse.text();
+ console.log(` Error: ${errorText.substring(0, 200)}`);
+ }
+ }
+
+ // Step 6: Test creating data
+ console.log('\n6ļøā£ Testing data creation...');
+
+ // Test creating a project
+ const projectData = {
+ name: 'Test Project Auth',
+ description: 'Testing authentication flow',
+ deadline: '2025-12-31',
+ status: 'active'
+ };
+
+ const createProjectResponse = await makeAuthenticatedRequest(
+ `${BASE_URL}/api/projects`,
+ {
+ method: 'POST',
+ body: JSON.stringify(projectData)
+ },
+ sessionCookies
+ );
+
+ if (createProjectResponse.ok) {
+ const newProject = await createProjectResponse.json();
+ console.log('ā
Project creation successful:', newProject.name);
+
+ // Clean up - delete the test project
+ const deleteResponse = await makeAuthenticatedRequest(
+ `${BASE_URL}/api/projects/${newProject.id}`,
+ { method: 'DELETE' },
+ sessionCookies
+ );
+
+ if (deleteResponse.ok) {
+ console.log('ā
Test project cleaned up');
+ }
+ } else {
+ console.log('ā Project creation failed:', createProjectResponse.status);
+ const errorText = await createProjectResponse.text();
+ console.log(' Error:', errorText.substring(0, 200));
+ }
+
+ } catch (error) {
+ console.error('ā Test failed with error:', error.message);
+ }
+}
+
+// Run the test
+testCompleteAuthenticatedFlow();