From bcdda0754b4a209a8f074c1df47154e486a1c316 Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Thu, 16 Jul 2026 07:48:10 -0400 Subject: [PATCH] fix: set cookieSecure=False so session persists on HTTP (localhost) The UserSession cookie was defaulting to secure=True which prevents browsers from sending it on non-HTTPS connections. This caused the dashboard to redirect back to login on every click. Verified with Playwright: login, dashboard link click, and navbar navigation all preserve the session correctly. --- src/Sis/View/Layout.hs | 1 + test-login.js | 89 +++++++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/Sis/View/Layout.hs b/src/Sis/View/Layout.hs index acc2fcb..8458aeb 100644 --- a/src/Sis/View/Layout.hs +++ b/src/Sis/View/Layout.hs @@ -31,6 +31,7 @@ newtype UserSession = UserSession instance Session UserSession where cookiePath = Just "/" + cookieSecure = False instance Default UserSession where def = UserSession 0 diff --git a/test-login.js b/test-login.js index 7479f3e..ca712fc 100644 --- a/test-login.js +++ b/test-login.js @@ -2,46 +2,71 @@ const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ headless: true }); - const page = await browser.newPage(); + const context = await browser.newContext(); + const page = await context.newPage(); - // Collect ALL console messages - page.on('console', msg => console.log('CONSOLE:', msg.type(), msg.text())); + page.on('console', msg => console.log('CONSOLE:', msg.type(), msg.text().substring(0, 150))); page.on('pageerror', err => console.log('PAGE ERROR:', err.message)); - // Listen for network responses - page.on('response', resp => { - if (resp.url().includes('/rlogin') || resp.url().includes('websocket')) { - console.log('RESPONSE:', resp.status(), resp.url().substring(0, 50)); - } - }); - + // Step 1: Load login page + console.log('\n=== Step 1: Load login page ==='); await page.goto('http://localhost:8080/rlogin', { waitUntil: 'networkidle', timeout: 10000 }); - - // Check the HTML - const html = await page.content(); - console.log('Has "Welcome":', html.includes('Welcome')); - console.log('Has "lfEmail":', html.includes('lfEmail')); - console.log('Has form action:', html.includes('data-onsubmit')); - + console.log('URL:', page.url()); + + // Step 2: Submit login form + console.log('\n=== Step 2: Submit login ==='); await page.fill('input[name="lfEmail"]', 'alice@demo.com'); await page.fill('input[name="lfPassword"]', 'password123'); - - console.log('Clicking submit...'); await page.click('button[type="submit"]'); - - // Wait to see what happens - await page.waitForTimeout(4000); - - const newHtml = await page.content(); - console.log('After submit URL:', page.url()); - console.log('Has "Invalid":', newHtml.includes('Invalid')); - console.log('Has "Logged In":', newHtml.includes('Logged In')); - console.log('Has "Redirecting":', newHtml.includes('Redirecting')); - console.log('Has "Dashboard":', newHtml.includes('Dashboard')); - + await page.waitForTimeout(3000); + console.log('URL after submit:', page.url()); + // Check cookies - const cookies = await page.context().cookies(); - console.log('Cookies:', JSON.stringify(cookies.map(c => c.name + '=' + c.value))); + const cookies = await context.cookies(); + console.log('Cookies:', JSON.stringify(cookies.map(c => ({ name: c.name, value: c.value.substring(0, 20), domain: c.domain, path: c.path })))); + + // Check content + const content = await page.content(); + console.log('Has "Go to Dashboard":', content.includes('Go to Dashboard')); + console.log('Has "Logged In":', content.includes('Logged In')); + + // Step 3: Click "Go to Dashboard" + console.log('\n=== Step 3: Click Go to Dashboard ==='); + const dashLink = await page.$('a[href="/rdashboard"]'); + if (dashLink) { + console.log('Found dashboard link'); + await dashLink.click(); + await page.waitForTimeout(3000); + } else { + console.log('No dashboard link found, navigating directly'); + await page.goto('http://localhost:8080/rdashboard', { waitUntil: 'networkidle', timeout: 10000 }); + } + console.log('URL after dashboard:', page.url()); + + const dashContent = await page.content(); + console.log('Has "Welcome Back":', dashContent.includes('Welcome Back')); + console.log('Has "Dashboard":', dashContent.includes('Dashboard')); + console.log('Has "Overdue":', dashContent.includes('Overdue')); + console.log('Has "No households":', dashContent.includes('No households')); + + // Step 4: Check what happens when clicking navbar Dashboard + console.log('\n=== Step 4: Click navbar Dashboard ==='); + const navLinks = await page.$$('a'); + for (const link of navLinks) { + const href = await link.getAttribute('href'); + const text = await link.textContent(); + if (text && text.trim() === 'Dashboard') { + console.log('Found navbar Dashboard link:', href); + await link.click(); + await page.waitForTimeout(3000); + break; + } + } + console.log('URL after navbar click:', page.url()); + const navContent = await page.content(); + console.log('Has "Welcome Back":', navContent.includes('Welcome Back')); + console.log('Has "Overdue":', navContent.includes('Overdue')); await browser.close(); + console.log('\nDone.'); })().catch(e => { console.error('FAIL:', e.message); process.exit(1); });