fix: remove [Authorize] from Dashboard, add internal auth check
Root cause: [Authorize] attribute was blocking /dashboard access before Blazor auth state could be established, causing redirect to /not-found. Solution: - Remove [Authorize] from Dashboard.razor - Add authentication check in OnInitializedAsync - If not authenticated, redirect to login internally - Reduced wait time from 6s to 3s in login.html This allows: 1. /dashboard to load immediately 2. Blazor auth state to initialize 3. Dashboard to verify user is authenticated 4. Redirect to login if not authenticated Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { chromium } from "@playwright/test";
|
||||
|
||||
(async () => {
|
||||
console.log("=== FULL LOGIN FLOW TEST WITH DETAILED LOGGING ===\n");
|
||||
|
||||
const b = await chromium.launch({
|
||||
headless: false, // 브라우저 화면 표시
|
||||
args: ["--disable-blink-features=AutomationControlled"]
|
||||
});
|
||||
|
||||
const p = await b.newPage();
|
||||
|
||||
// 모든 콘솔 메시지 캡처
|
||||
p.on("console", msg => {
|
||||
const type = msg.type();
|
||||
const text = msg.text();
|
||||
console.log(` [BROWSER-${type.toUpperCase()}] ${text}`);
|
||||
});
|
||||
|
||||
// 모든 요청/응답 로그
|
||||
p.on("request", req => {
|
||||
if (req.url().includes("auth")) {
|
||||
console.log(` [REQUEST] ${req.method()} ${req.url()}`);
|
||||
}
|
||||
});
|
||||
|
||||
p.on("response", res => {
|
||||
if (res.url().includes("auth")) {
|
||||
console.log(` [RESPONSE] ${res.status()} ${res.url()}`);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
console.log("1️⃣ STEP 1: Loading login page...");
|
||||
await p.goto("http://localhost:5265/login.html", { waitUntil: "networkidle" });
|
||||
console.log(" ✓ Page loaded\n");
|
||||
|
||||
console.log("2️⃣ STEP 2: Filling form (admin/admin)...");
|
||||
const userInput = await p.$("input[name='username']");
|
||||
if (!userInput) {
|
||||
console.log(" ✗ Username input NOT FOUND");
|
||||
console.log(" Page content snippet:");
|
||||
const html = await p.content();
|
||||
const snippet = html.substring(0, 500);
|
||||
console.log(snippet);
|
||||
} else {
|
||||
await p.fill("input[name='username']", "admin");
|
||||
await p.fill("input[name='password']", "admin");
|
||||
console.log(" ✓ Form filled\n");
|
||||
|
||||
console.log("3️⃣ STEP 3: Clicking login button...");
|
||||
await p.click("button[type='submit']");
|
||||
console.log(" ✓ Button clicked\n");
|
||||
|
||||
console.log("4️⃣ STEP 4: Waiting 7 seconds for auth flow...");
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
const url = p.url();
|
||||
console.log(` [${i}s] Current URL: ${url}`);
|
||||
}
|
||||
|
||||
console.log("\n5️⃣ FINAL RESULT:");
|
||||
const finalUrl = p.url();
|
||||
const finalContent = await p.content();
|
||||
|
||||
console.log(` URL: ${finalUrl}`);
|
||||
|
||||
if (finalUrl.includes("/dashboard")) {
|
||||
if (finalContent.includes("관리자 대시보드")) {
|
||||
console.log(" ✓✓✓ SUCCESS! Dashboard loaded with content!");
|
||||
} else if (finalContent.includes("Not Found")) {
|
||||
console.log(" ✗ Dashboard URL but 'Not Found' error");
|
||||
} else {
|
||||
console.log(" ✓ Dashboard page (content varies)");
|
||||
}
|
||||
} else if (finalUrl.includes("/not-found")) {
|
||||
console.log(" ✗ FAILED: Redirected to /not-found");
|
||||
console.log(" This means authentication failed");
|
||||
} else if (finalUrl.includes("/login")) {
|
||||
console.log(" ✗ Back at login page");
|
||||
} else {
|
||||
console.log(" ? Other page");
|
||||
}
|
||||
|
||||
// 스크린샷 저장
|
||||
await p.screenshot({ path: "./playwright-test-result.png", fullPage: true });
|
||||
console.log("\n📷 Screenshot saved: playwright-test-result.png");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("❌ Error:", e.message);
|
||||
} finally {
|
||||
await b.close();
|
||||
}
|
||||
})();
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
@@ -0,0 +1,45 @@
|
||||
import { chromium } from "@playwright/test";
|
||||
|
||||
(async () => {
|
||||
console.log("=== SIMPLE DIRECT TEST ===\n");
|
||||
|
||||
const b = await chromium.launch({ headless: false });
|
||||
const p = await b.newPage();
|
||||
|
||||
// 모든 콘솔 로그 출력
|
||||
p.on("console", msg => console.log(` [${msg.type()}] ${msg.text()}`));
|
||||
|
||||
try {
|
||||
console.log("1. Navigate to login...");
|
||||
// URL에 타임스탐프 추가 (캐시 무시)
|
||||
await p.goto("http://localhost:5265/login.html?v=" + Date.now());
|
||||
|
||||
console.log("2. Submit form...");
|
||||
await p.fill("input[name='username']", "admin");
|
||||
await p.fill("input[name='password']", "admin");
|
||||
|
||||
// Before submit - 현재 URL
|
||||
console.log(" URL before submit: " + p.url());
|
||||
|
||||
await p.click("button[type='submit']");
|
||||
|
||||
// 8초 동안 URL 변화 감시
|
||||
console.log("3. Monitoring for 8 seconds...");
|
||||
let lastUrl = "";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
const currentUrl = p.url();
|
||||
if (currentUrl !== lastUrl) {
|
||||
console.log(` [${i+1}s] ➜ ${currentUrl}`);
|
||||
lastUrl = currentUrl;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\n4. RESULT: " + p.url());
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error:", e.message);
|
||||
}
|
||||
|
||||
await b.close();
|
||||
})();
|
||||
@@ -1,7 +1,9 @@
|
||||
@page "/dashboard"
|
||||
@attribute [Authorize]
|
||||
@using QuantEngine.Core.Infrastructure
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject HttpClient Http
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager NavManager
|
||||
|
||||
<PageTitle>QuantEngine - Admin Dashboard</PageTitle>
|
||||
|
||||
@@ -240,6 +242,15 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Check authentication
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
if (!authState.User.Identity?.IsAuthenticated ?? true)
|
||||
{
|
||||
// Not authenticated - redirect to login
|
||||
NavManager.NavigateTo("/login.html", forceLoad: true);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Load operational report
|
||||
|
||||
@@ -324,14 +324,13 @@
|
||||
|
||||
// 로그인 성공 메시지 표시
|
||||
alertDiv.className = 'alert alert-success show';
|
||||
alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다. (Blazor 초기화 중...)';
|
||||
alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다.';
|
||||
|
||||
// Blazor 앱이 로드되고 localStorage에서 토큰을 읽을 시간을 충분히 제공
|
||||
// 3초 대기 후 대시보드로 이동
|
||||
console.log('[Login] Waiting 3 seconds for Blazor to initialize...');
|
||||
// Blazor 앱이 로드될 시간 제공
|
||||
// (대시보드는 이제 [Authorize] 없이 로드되고, 내부에서 인증 체크)
|
||||
console.log('[Login] Token saved. Redirecting in 3 seconds...');
|
||||
setTimeout(() => {
|
||||
console.log('[Login] Redirecting to dashboard');
|
||||
console.log('[Login] Token in localStorage:', localStorage.getItem('quant_admin_access_token') ? 'YES' : 'NO');
|
||||
console.log('[Login] Navigating to dashboard');
|
||||
window.location.href = '/dashboard';
|
||||
}, 3000);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { chromium } from "@playwright/test";
|
||||
|
||||
(async () => {
|
||||
console.log("=== TEST WITH CACHE BYPASS ===\n");
|
||||
|
||||
const b = await chromium.launch({ headless: false });
|
||||
const ctx = await b.createIncognitoBrowserContext(); // Private mode = no cache
|
||||
const p = await ctx.newPage();
|
||||
|
||||
p.on("console", msg => {
|
||||
if (msg.text().includes("[Login]")) {
|
||||
console.log(` ✓ ${msg.text()}`);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// 캐시 무시하고 로드
|
||||
console.log("1. Loading login page (no cache)...");
|
||||
await p.goto("http://localhost:5265/login.html?nocache=" + Date.now(), {
|
||||
waitUntil: "networkidle"
|
||||
});
|
||||
console.log(" ✓ Loaded\n");
|
||||
|
||||
console.log("2. Submitting login...");
|
||||
await p.fill("input[name='username']", "admin");
|
||||
await p.fill("input[name='password']", "admin");
|
||||
await p.click("button[type='submit']");
|
||||
console.log(" ✓ Submitted\n");
|
||||
|
||||
console.log("3. Waiting 8 seconds...");
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
const url = p.url();
|
||||
process.stdout.write(` [${i}s] ${url}\r`);
|
||||
}
|
||||
|
||||
console.log("\n\n4. FINAL CHECK:");
|
||||
const finalUrl = p.url();
|
||||
if (finalUrl.includes("/dashboard")) {
|
||||
console.log(" ✓✓✓ SUCCESS: Dashboard loaded!");
|
||||
const content = await p.content();
|
||||
if (content.includes("관리자 대시보드")) {
|
||||
console.log(" ✓ Dashboard content confirmed!");
|
||||
}
|
||||
} else if (finalUrl.includes("/not-found")) {
|
||||
console.log(" ✗ /not-found (auth failed)");
|
||||
} else {
|
||||
console.log(" URL: " + finalUrl);
|
||||
}
|
||||
|
||||
await p.screenshot({ path: "./final-test-no-cache.png", fullPage: true });
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error:", e.message);
|
||||
} finally {
|
||||
await b.close();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user