644 lines
22 KiB
HTML
644 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Auth Flow Testing - Admin Panel</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
padding: 30px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
font-size: 28px;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #666;
|
|
margin-bottom: 30px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.test-section {
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
background: #f9f9f9;
|
|
border-radius: 6px;
|
|
border-left: 4px solid #3b82f6;
|
|
}
|
|
|
|
.test-section h2 {
|
|
color: #333;
|
|
margin-bottom: 15px;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.test-description {
|
|
color: #666;
|
|
margin-bottom: 15px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.test-steps {
|
|
background: white;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.test-steps ol {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
.test-steps li {
|
|
margin-bottom: 8px;
|
|
color: #444;
|
|
}
|
|
|
|
.expected-result {
|
|
background: #e8f5e9;
|
|
padding: 12px;
|
|
border-radius: 4px;
|
|
border-left: 3px solid #4caf50;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.expected-result strong {
|
|
color: #2e7d32;
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.expected-result ul {
|
|
margin-left: 20px;
|
|
color: #555;
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
button {
|
|
padding: 12px 24px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
button:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
button:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #3b82f6;
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: #2563eb;
|
|
}
|
|
|
|
.btn-danger {
|
|
background: #ef4444;
|
|
color: white;
|
|
}
|
|
|
|
.btn-danger:hover {
|
|
background: #dc2626;
|
|
}
|
|
|
|
.btn-warning {
|
|
background: #f59e0b;
|
|
color: white;
|
|
}
|
|
|
|
.btn-warning:hover {
|
|
background: #d97706;
|
|
}
|
|
|
|
.btn-success {
|
|
background: #10b981;
|
|
color: white;
|
|
}
|
|
|
|
.btn-success:hover {
|
|
background: #059669;
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #6b7280;
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #4b5563;
|
|
}
|
|
|
|
.status-panel {
|
|
background: #1e293b;
|
|
color: #e2e8f0;
|
|
padding: 20px;
|
|
border-radius: 6px;
|
|
margin-top: 30px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.status-panel h3 {
|
|
color: #38bdf8;
|
|
margin-bottom: 15px;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
}
|
|
|
|
.status-item {
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.status-label {
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.status-value {
|
|
color: #34d399;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.status-value.false {
|
|
color: #f87171;
|
|
}
|
|
|
|
.log-level-control {
|
|
background: #fef3c7;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
margin-bottom: 30px;
|
|
border-left: 4px solid #f59e0b;
|
|
}
|
|
|
|
.log-level-control h3 {
|
|
color: #92400e;
|
|
margin-bottom: 10px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.log-level-buttons {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.log-level-buttons button {
|
|
padding: 8px 16px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.warning-box {
|
|
background: #fef2f2;
|
|
border: 1px solid #fecaca;
|
|
border-radius: 6px;
|
|
padding: 15px;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.warning-box h3 {
|
|
color: #991b1b;
|
|
margin-bottom: 10px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.warning-box ul {
|
|
margin-left: 20px;
|
|
color: #7f1d1d;
|
|
}
|
|
|
|
.warning-box li {
|
|
margin-bottom: 5px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🧪 Auth Flow Testing</h1>
|
|
<p class="subtitle">Comprehensive testing for the Jinja2 migration auth loop fix</p>
|
|
|
|
<!-- Log Level Control -->
|
|
<div class="log-level-control">
|
|
<h3>📊 Log Level Control</h3>
|
|
<p style="color: #78350f; font-size: 13px; margin-bottom: 10px;">
|
|
Change logging verbosity for login.js and api-client.js
|
|
</p>
|
|
<div class="log-level-buttons">
|
|
<button onclick="setLogLevel(0)" class="btn-secondary">0 - None</button>
|
|
<button onclick="setLogLevel(1)" class="btn-danger">1 - Errors Only</button>
|
|
<button onclick="setLogLevel(2)" class="btn-warning">2 - Warnings</button>
|
|
<button onclick="setLogLevel(3)" class="btn-success">3 - Info (Production)</button>
|
|
<button onclick="setLogLevel(4)" class="btn-primary">4 - Debug (Full)</button>
|
|
</div>
|
|
<p style="color: #78350f; font-size: 12px; margin-top: 10px; font-style: italic;">
|
|
Current levels: LOGIN = <span id="currentLoginLevel">4</span>, API = <span id="currentApiLevel">3</span>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Test 1: Clean Slate -->
|
|
<div class="test-section">
|
|
<h2>Test 1: Clean Slate - Fresh Login Flow</h2>
|
|
<p class="test-description">
|
|
Tests the complete login flow from scratch with no existing tokens.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Click "Clear All Data" below</li>
|
|
<li>Click "Navigate to /admin"</li>
|
|
<li>Observe browser behavior and console logs</li>
|
|
<li>You should land on login page</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Single redirect: /admin → /admin/login</li>
|
|
<li>Login page loads with NO API calls to /admin/auth/me</li>
|
|
<li>No loops, no errors in console</li>
|
|
<li>Form is ready for input</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="clearAllData()" class="btn-danger">Clear All Data</button>
|
|
<button onclick="navigateToAdmin()" class="btn-primary">Navigate to /admin</button>
|
|
<button onclick="navigateToLogin()" class="btn-secondary">Go to Login</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test 2: Login Success -->
|
|
<div class="test-section">
|
|
<h2>Test 2: Successful Login</h2>
|
|
<p class="test-description">
|
|
Tests that login works correctly and redirects to dashboard.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Ensure you're on /admin/login</li>
|
|
<li>Enter valid admin credentials</li>
|
|
<li>Click "Login"</li>
|
|
<li>Observe redirect and dashboard load</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Login API call succeeds (check Network tab)</li>
|
|
<li>Token stored in localStorage</li>
|
|
<li>Success message shows briefly</li>
|
|
<li>Redirect to /admin/dashboard after 500ms</li>
|
|
<li>Dashboard loads with stats and recent vendors</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="navigateToLogin()" class="btn-primary">Go to Login Page</button>
|
|
<button onclick="checkAuthStatus()" class="btn-secondary">Check Auth Status</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test 3: Dashboard Refresh -->
|
|
<div class="test-section">
|
|
<h2>Test 3: Dashboard Refresh (Authenticated)</h2>
|
|
<p class="test-description">
|
|
Tests that refreshing the dashboard works without redirect loops.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Complete Test 2 (login successfully)</li>
|
|
<li>On dashboard, press F5 or click "Refresh Page"</li>
|
|
<li>Observe page reload behavior</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Dashboard reloads normally</li>
|
|
<li>No redirects to login</li>
|
|
<li>Stats and vendors load correctly</li>
|
|
<li>No console errors</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="navigateToDashboard()" class="btn-primary">Go to Dashboard</button>
|
|
<button onclick="window.location.reload()" class="btn-secondary">Refresh Page</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test 4: Expired Token -->
|
|
<div class="test-section">
|
|
<h2>Test 4: Expired Token Handling</h2>
|
|
<p class="test-description">
|
|
Tests that expired tokens are handled gracefully with redirect to login.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Click "Set Expired Token"</li>
|
|
<li>Click "Navigate to Dashboard"</li>
|
|
<li>Observe authentication failure and redirect</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Server detects expired token</li>
|
|
<li>Returns 401 Unauthorized</li>
|
|
<li>Browser redirects to /admin/login</li>
|
|
<li>Token is cleared from localStorage</li>
|
|
<li>No infinite loops</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="setExpiredToken()" class="btn-warning">Set Expired Token</button>
|
|
<button onclick="navigateToDashboard()" class="btn-primary">Navigate to Dashboard</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test 5: Direct Dashboard Access (No Token) -->
|
|
<div class="test-section">
|
|
<h2>Test 5: Direct Dashboard Access (Unauthenticated)</h2>
|
|
<p class="test-description">
|
|
Tests that accessing dashboard without token redirects to login.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Click "Clear All Data"</li>
|
|
<li>Click "Navigate to Dashboard"</li>
|
|
<li>Observe immediate redirect to login</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Redirect from /admin/dashboard to /admin/login</li>
|
|
<li>No API calls attempted</li>
|
|
<li>Login page loads correctly</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="clearAllData()" class="btn-danger">Clear All Data</button>
|
|
<button onclick="navigateToDashboard()" class="btn-primary">Navigate to Dashboard</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test 6: Login Page with Valid Token -->
|
|
<div class="test-section">
|
|
<h2>Test 6: Login Page with Valid Token</h2>
|
|
<p class="test-description">
|
|
Tests what happens when user visits login page while already authenticated.
|
|
</p>
|
|
|
|
<div class="test-steps">
|
|
<strong>Steps:</strong>
|
|
<ol>
|
|
<li>Login successfully (Test 2)</li>
|
|
<li>Click "Go to Login Page" below</li>
|
|
<li>Observe behavior</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="expected-result">
|
|
<strong>✅ Expected Result:</strong>
|
|
<ul>
|
|
<li>Login page loads</li>
|
|
<li>Existing token is cleared (init() clears it)</li>
|
|
<li>Form is displayed normally</li>
|
|
<li>NO redirect loops</li>
|
|
<li>NO API calls to validate token</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button onclick="setValidToken()" class="btn-success">Set Valid Token (Mock)</button>
|
|
<button onclick="navigateToLogin()" class="btn-primary">Go to Login Page</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Panel -->
|
|
<div class="status-panel">
|
|
<h3>🔍 Current Auth Status</h3>
|
|
<div id="statusDisplay">
|
|
<div class="status-item">
|
|
<span class="status-label">Current URL:</span>
|
|
<span class="status-value" id="currentUrl">-</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Has admin_token:</span>
|
|
<span class="status-value" id="hasToken">-</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Has admin_user:</span>
|
|
<span class="status-value" id="hasUser">-</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Token Preview:</span>
|
|
<span class="status-value" id="tokenPreview">-</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Username:</span>
|
|
<span class="status-value" id="username">-</span>
|
|
</div>
|
|
</div>
|
|
<button onclick="updateStatus()" style="margin-top: 15px; background: #38bdf8; color: #0f172a; padding: 8px 16px; border-radius: 4px; font-size: 12px; cursor: pointer; border: none;">
|
|
🔄 Refresh Status
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Warning Box -->
|
|
<div class="warning-box">
|
|
<h3>⚠️ Important Notes</h3>
|
|
<ul>
|
|
<li>Always check browser console for detailed logs</li>
|
|
<li>Use Network tab to see actual HTTP requests and redirects</li>
|
|
<li>Clear browser cache if you see unexpected behavior</li>
|
|
<li>Make sure FastAPI server is running on localhost:8000</li>
|
|
<li>Valid admin credentials required for login tests</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Update status display
|
|
function updateStatus() {
|
|
const token = localStorage.getItem('admin_token');
|
|
const userStr = localStorage.getItem('admin_user');
|
|
let user = null;
|
|
|
|
try {
|
|
user = userStr ? JSON.parse(userStr) : null;
|
|
} catch (e) {
|
|
console.error('Failed to parse user data:', e);
|
|
}
|
|
|
|
document.getElementById('currentUrl').textContent = window.location.href;
|
|
|
|
const hasTokenEl = document.getElementById('hasToken');
|
|
hasTokenEl.textContent = token ? 'Yes' : 'No';
|
|
hasTokenEl.className = token ? 'status-value' : 'status-value false';
|
|
|
|
const hasUserEl = document.getElementById('hasUser');
|
|
hasUserEl.textContent = user ? 'Yes' : 'No';
|
|
hasUserEl.className = user ? 'status-value' : 'status-value false';
|
|
|
|
document.getElementById('tokenPreview').textContent = token
|
|
? token.substring(0, 30) + '...'
|
|
: 'No token';
|
|
|
|
document.getElementById('username').textContent = user?.username || 'Not logged in';
|
|
|
|
console.log('📊 Status Updated:', {
|
|
hasToken: !!token,
|
|
hasUser: !!user,
|
|
user: user
|
|
});
|
|
}
|
|
|
|
// Test functions
|
|
function clearAllData() {
|
|
console.log('🗑️ Clearing all localStorage data...');
|
|
localStorage.clear();
|
|
console.log('✅ All data cleared');
|
|
alert('✅ All localStorage data cleared!\n\nCheck console for details.');
|
|
updateStatus();
|
|
}
|
|
|
|
function navigateToAdmin() {
|
|
console.log('🚀 Navigating to /admin...');
|
|
window.location.href = '/admin';
|
|
}
|
|
|
|
function navigateToLogin() {
|
|
console.log('🚀 Navigating to /admin/login...');
|
|
window.location.href = '/admin/login';
|
|
}
|
|
|
|
function navigateToDashboard() {
|
|
console.log('🚀 Navigating to /admin/dashboard...');
|
|
window.location.href = '/admin/dashboard';
|
|
}
|
|
|
|
function checkAuthStatus() {
|
|
updateStatus();
|
|
alert('Check console and status panel for auth details.');
|
|
}
|
|
|
|
function setExpiredToken() {
|
|
const expiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyMzkwMjJ9.invalidexpiredtoken';
|
|
console.log('⚠️ Setting expired/invalid token...');
|
|
localStorage.setItem('admin_token', expiredToken);
|
|
localStorage.setItem('admin_user', JSON.stringify({
|
|
id: 1,
|
|
username: 'test_expired',
|
|
role: 'admin'
|
|
}));
|
|
console.log('✅ Expired token set');
|
|
alert('⚠️ Expired token set!\n\nNow try navigating to dashboard.');
|
|
updateStatus();
|
|
}
|
|
|
|
function setValidToken() {
|
|
// This is a mock token - won't actually work with backend
|
|
const mockToken = 'mock_valid_token_' + Date.now();
|
|
console.log('✅ Setting mock valid token...');
|
|
localStorage.setItem('admin_token', mockToken);
|
|
localStorage.setItem('admin_user', JSON.stringify({
|
|
id: 1,
|
|
username: 'test_user',
|
|
role: 'admin'
|
|
}));
|
|
console.log('✅ Mock token set (will not work with real backend)');
|
|
alert('✅ Mock token set!\n\nNote: This is a fake token and won\'t work with the real backend.');
|
|
updateStatus();
|
|
}
|
|
|
|
// Log level control
|
|
function setLogLevel(level) {
|
|
console.log(`📊 Setting log level to ${level}...`);
|
|
|
|
// Note: This only works if login.js and api-client.js are loaded
|
|
// In production, you'd need to reload the page or use a more sophisticated approach
|
|
if (typeof LOG_LEVEL !== 'undefined') {
|
|
window.LOG_LEVEL = level;
|
|
document.getElementById('currentLoginLevel').textContent = level;
|
|
console.log('✅ LOGIN log level set to', level);
|
|
} else {
|
|
console.warn('⚠️ LOG_LEVEL not found (login.js not loaded)');
|
|
}
|
|
|
|
if (typeof API_LOG_LEVEL !== 'undefined') {
|
|
window.API_LOG_LEVEL = level;
|
|
document.getElementById('currentApiLevel').textContent = level;
|
|
console.log('✅ API log level set to', level);
|
|
} else {
|
|
console.warn('⚠️ API_LOG_LEVEL not found (api-client.js not loaded)');
|
|
}
|
|
|
|
alert(`Log level set to ${level}\n\n0 = None\n1 = Errors\n2 = Warnings\n3 = Info\n4 = Debug\n\nNote: Changes apply to current page. Reload to apply to all scripts.`);
|
|
}
|
|
|
|
// Initialize status on load
|
|
updateStatus();
|
|
|
|
// Auto-refresh status every 2 seconds
|
|
setInterval(updateStatus, 2000);
|
|
|
|
console.log('🧪 Auth Flow Testing Script Loaded');
|
|
console.log('📊 Use the buttons above to run tests');
|
|
console.log('🔍 Watch browser console and Network tab for details');
|
|
</script>
|
|
</body>
|
|
</html> |