412 lines
19 KiB
Python
412 lines
19 KiB
Python
"""
|
|
HTML Templates für OIDC Server
|
|
Modernes Design mit Dark Mode Support
|
|
"""
|
|
|
|
USER_ANALYTICS_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>My Active Sessions</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
|
|
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
|
|
</svg>
|
|
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="container">
|
|
<header>
|
|
<h1>My Active Sessions</h1>
|
|
<p>{{ user.name }} ({{ user.email }})</p>
|
|
</header>
|
|
|
|
<div style="margin-bottom: 24px;">
|
|
<a href="/dashboard" style="text-decoration: none;">
|
|
<button>Back to Dashboard</button>
|
|
</a>
|
|
<a href="/logout" style="text-decoration: none;">
|
|
<button class="danger">Logout</button>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Summary Stats -->
|
|
<div class="analytics-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px;">
|
|
<div class="analytics-card" style="background: var(--bg-secondary); padding: 24px; border-radius: 8px;">
|
|
<h3 style="font-size: 14px; color: var(--text-secondary); margin-bottom: 8px;">Active Sessions</h3>
|
|
<div class="metric" style="font-size: 32px; font-weight: 600; color: var(--primary-color);">{{ summary.total_active_sessions }}</div>
|
|
<div class="label" style="font-size: 12px; color: var(--text-secondary); margin-top: 4px;">Currently active</div>
|
|
</div>
|
|
<div class="analytics-card" style="background: var(--bg-secondary); padding: 24px; border-radius: 8px;">
|
|
<h3 style="font-size: 14px; color: var(--text-secondary); margin-bottom: 8px;">Applications</h3>
|
|
<div class="metric" style="font-size: 32px; font-weight: 600; color: var(--primary-color);">{{ summary.total_clients }}</div>
|
|
<div class="label" style="font-size: 12px; color: var(--text-secondary); margin-top: 4px;">You're using</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Sessions -->
|
|
<h2 style="margin-bottom: 20px;">Active Sessions</h2>
|
|
|
|
{% if active_sessions %}
|
|
{% set current_client = namespace(value='') %}
|
|
{% for session in active_sessions %}
|
|
{% if session.client_name != current_client.value %}
|
|
{% set current_client.value = session.client_name %}
|
|
{% if not loop.first %}
|
|
</div>
|
|
{% endif %}
|
|
<div class="client-section" style="background: var(--bg-secondary); padding: 24px; border-radius: 8px; margin-bottom: 16px;">
|
|
<h3 style="margin-bottom: 16px;">{{ session.client_name }}</h3>
|
|
{% endif %}
|
|
|
|
<div class="session-item" style="padding: 16px; background: var(--bg-primary); border-radius: 6px; margin-bottom: 12px;">
|
|
<div class="session-info" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
|
<div>
|
|
<strong>Session</strong>
|
|
</div>
|
|
<span class="status-badge status-available" style="padding: 4px 12px; background: #10b981; color: white; border-radius: 4px; font-size: 12px;">Active</span>
|
|
</div>
|
|
<div class="session-meta" style="font-size: 14px; color: var(--text-secondary);">
|
|
Created: {{ session.created_at.strftime('%Y-%m-%d %H:%M:%S') }} |
|
|
Expires: {{ session.expires_at.strftime('%Y-%m-%d %H:%M:%S') }}
|
|
</div>
|
|
</div>
|
|
|
|
{% if loop.last %}
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="import-results" style="background: var(--bg-secondary); padding: 20px; border-radius: 8px;">
|
|
No active sessions. Log in to an application to see sessions here.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-mode');
|
|
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
|
|
}
|
|
|
|
if (localStorage.getItem('darkMode') === 'true') {
|
|
document.body.classList.add('dark-mode');
|
|
}
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setTimeout(function() {
|
|
location.reload();
|
|
}, 30000);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
LOGIN_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OIDC IdP - Login</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
|
|
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
|
|
</svg>
|
|
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="container">
|
|
<header>
|
|
<h1>🔐 Homelab OIDC Login</h1>
|
|
<p>Secure authentication for your homelab services</p>
|
|
</header>
|
|
|
|
<div class="modal-content" style="max-width: 450px; margin: 0 auto;">
|
|
{% if error %}
|
|
<div class="import-results error">
|
|
<strong>Error:</strong> {{ error }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if success %}
|
|
<div class="import-results success">
|
|
<strong>Success:</strong> {{ success }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="POST" style="margin-top: 24px;">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" name="username" placeholder="Enter your username" required autofocus>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<input type="password" id="password" name="password" placeholder="Enter your password" required>
|
|
</div>
|
|
|
|
<button type="submit" style="width: 100%; margin-top: 8px;">Sign In</button>
|
|
</form>
|
|
|
|
<div style="text-align: center; margin-top: 24px; padding-top: 24px; border-top: 2px solid var(--border-main);">
|
|
<p style="color: var(--text-secondary); margin-bottom: 12px;">Don't have an account?</p>
|
|
<a href="/register" style="color: var(--primary); text-decoration: none; font-weight: 600;">Create new account →</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-mode');
|
|
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
|
|
}
|
|
|
|
// Load saved theme
|
|
if (localStorage.getItem('darkMode') === 'true') {
|
|
document.body.classList.add('dark-mode');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
REGISTER_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OIDC IdP - Registration</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
|
|
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
|
|
</svg>
|
|
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="container">
|
|
<header>
|
|
<h1>Create New Account</h1>
|
|
<p>Join your homelab authentication system</p>
|
|
</header>
|
|
|
|
<div class="modal-content" style="max-width: 500px; margin: 0 auto;">
|
|
{% if error %}
|
|
<div class="import-results error">
|
|
<strong>Error:</strong> {{ error }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="POST" style="margin-top: 24px;">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" name="username" placeholder="Choose a username" required autofocus>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="email">Email Address</label>
|
|
<input type="email" id="email" name="email" placeholder="your.email@homelab.local" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="name">Full Name</label>
|
|
<input type="text" id="name" name="name" placeholder="John Doe" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<input type="password" id="password" name="password" placeholder="Min. 8 characters" required minlength="8">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="password_confirm">Confirm Password</label>
|
|
<input type="password" id="password_confirm" name="password_confirm" placeholder="Repeat your password" required>
|
|
</div>
|
|
|
|
<button type="submit" class="secondary" style="width: 100%; margin-top: 8px;">Create Account</button>
|
|
</form>
|
|
|
|
<div style="text-align: center; margin-top: 24px; padding-top: 24px; border-top: 2px solid var(--border-main);">
|
|
<p style="color: var(--text-secondary); margin-bottom: 12px;">Already have an account?</p>
|
|
<a href="/" style="color: var(--primary); text-decoration: none; font-weight: 600;">← Back to Login</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-mode');
|
|
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
|
|
}
|
|
|
|
if (localStorage.getItem('darkMode') === 'true') {
|
|
document.body.classList.add('dark-mode');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
CHANGE_PASSWORD_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OIDC IdP - Change Password</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
|
|
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
|
|
</svg>
|
|
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="container">
|
|
<header>
|
|
<h1>Change Password</h1>
|
|
<p>Update your account security</p>
|
|
</header>
|
|
|
|
<div class="modal-content" style="max-width: 500px; margin: 0 auto;">
|
|
{% if error %}
|
|
<div class="import-results error">
|
|
<strong>Error:</strong> {{ error }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if success %}
|
|
<div class="import-results success">
|
|
<strong>Success:</strong> {{ success }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="POST" style="margin-top: 24px;">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" name="username" placeholder="Your username" required autofocus>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="current_password">Current Password</label>
|
|
<input type="password" id="current_password" name="current_password" placeholder="Enter current password" required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="new_password">New Password</label>
|
|
<input type="password" id="new_password" name="new_password" placeholder="Min. 8 characters" required minlength="8">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="new_password_confirm">Confirm New Password</label>
|
|
<input type="password" id="new_password_confirm" name="new_password_confirm" placeholder="Repeat new password" required>
|
|
</div>
|
|
|
|
<button type="submit" style="width: 100%; margin-top: 8px;">Update Password</button>
|
|
</form>
|
|
|
|
<div style="text-align: center; margin-top: 24px; padding-top: 24px; border-top: 2px solid var(--border-main);">
|
|
<a href="/" style="color: var(--primary); text-decoration: none; font-weight: 600;">← Back to Login</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-mode');
|
|
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
|
|
}
|
|
|
|
if (localStorage.getItem('darkMode') === 'true') {
|
|
document.body.classList.add('dark-mode');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
INDEX_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OIDC Identity Provider</title>
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">
|
|
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z" />
|
|
</svg>
|
|
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" />
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="container">
|
|
<header>
|
|
<h1>🔐 OIDC Identity Provider</h1>
|
|
<p>Secure authentication server for your services</p>
|
|
</header>
|
|
|
|
<div class="modal-content" style="max-width: 700px; margin: 0 auto;">
|
|
<h2 style="color: var(--text-main); margin-bottom: 20px;">Welcome</h2>
|
|
<p style="color: var(--text-secondary); line-height: 1.6;">
|
|
This is an OpenID Connect (OIDC) Identity Provider that enables secure authentication
|
|
for your applications using industry-standard protocols.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="controls" style="margin-top: 32px; justify-content: center;">
|
|
<a href="/login" style="text-decoration: none;">
|
|
<button>🔑 Login</button>
|
|
</a>
|
|
<a href="/register" style="text-decoration: none;">
|
|
<button class="secondary">📝 Register</button>
|
|
</a>
|
|
</div>
|
|
|
|
<div style="text-align: center; margin-top: 24px; padding-top: 24px; border-top: 2px solid var(--border-main);">
|
|
<p style="color: var(--text-secondary); font-size: 0.9rem;">
|
|
Administrators: <a href="/admin/login" style="color: var(--primary); text-decoration: none;">Access admin panel</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme() {
|
|
document.body.classList.toggle('dark-mode');
|
|
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
|
|
}
|
|
|
|
if (localStorage.getItem('darkMode') === 'true') {
|
|
document.body.classList.add('dark-mode');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|