solventum-image-metadata/templates/login.html

369 lines
11 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Oliver Metadata Tool</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--primary-gold: #FFC407;
--primary-gold-dark: #e6b007;
--primary-gold-light: #ffcf33;
--dark-primary: #2c2c2c;
--dark-secondary: #1a1a1a;
--white: #ffffff;
--text-primary: #1f2937;
--text-muted: #6b7280;
--overlay-light: rgba(255, 255, 255, 0.95);
--border-light: rgba(255, 255, 255, 0.2);
--shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.1);
--radius-md: 12px;
--radius-xl: 20px;
--font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--transition-fast: 0.15s ease;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
body {
font-family: var(--font-family);
background: linear-gradient(135deg, var(--dark-primary) 0%, var(--dark-secondary) 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-container {
background: var(--overlay-light);
backdrop-filter: blur(20px);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
border: 1px solid var(--border-light);
width: 100%;
max-width: 450px;
padding: 40px;
}
.logo {
text-align: center;
margin-bottom: 30px;
position: relative;
}
.logo h1 {
color: var(--primary-gold-dark);
font-size: 32px;
margin-bottom: 10px;
font-weight: 700;
text-shadow: 0 2px 4px rgba(255, 196, 7, 0.2);
}
.logo p {
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
}
.divider {
text-align: center;
margin: 30px 0;
position: relative;
}
.divider::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: 50%;
height: 2px;
background: linear-gradient(90deg, transparent, var(--primary-gold-light), transparent);
}
.divider span {
background: var(--overlay-light);
padding: 0 15px;
color: var(--text-muted);
font-size: 13px;
font-weight: 600;
position: relative;
z-index: 1;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 8px;
font-size: 14px;
}
.form-group input {
width: 100%;
padding: 12px;
border: 2px solid #dee2e6;
border-radius: var(--radius-md);
font-size: 14px;
font-family: var(--font-family);
transition: all var(--transition-fast);
}
.form-group input:focus {
outline: none;
border-color: var(--primary-gold);
box-shadow: 0 0 0 3px rgba(255, 196, 7, 0.1);
}
.btn {
width: 100%;
padding: 14px;
border: none;
border-radius: var(--radius-md);
font-size: 16px;
font-weight: 600;
font-family: var(--font-family);
cursor: pointer;
transition: all var(--transition-fast);
}
.btn:hover {
transform: translateY(-2px);
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-gold), var(--primary-gold-dark));
color: var(--dark-secondary);
margin-bottom: 15px;
box-shadow: 0 4px 12px rgba(255, 196, 7, 0.3);
}
.btn-primary:hover {
box-shadow: 0 6px 16px rgba(255, 196, 7, 0.4);
}
.btn-sso {
background: var(--white);
color: var(--text-primary);
border: 2px solid var(--primary-gold);
}
.btn-sso:hover {
border-color: var(--primary-gold-dark);
background: #fffbf0;
color: var(--primary-gold-dark);
}
.alert {
padding: 12px;
border-radius: var(--radius-md);
margin-bottom: 20px;
font-size: 14px;
font-weight: 500;
}
.alert-error {
background: #fee;
color: #c33;
border: 2px solid #fcc;
}
.alert-info {
background: #fffbf0;
color: var(--primary-gold-dark);
border: 2px solid var(--primary-gold-light);
}
.test-user-info {
background: #fffbf0;
border: 2px dashed var(--primary-gold);
border-radius: var(--radius-md);
padding: 15px;
margin-bottom: 20px;
font-size: 13px;
color: var(--text-primary);
animation: pulse 3s infinite;
}
.test-user-info strong {
color: var(--primary-gold-dark);
font-weight: 600;
}
.test-user-info code {
background: rgba(255, 196, 7, 0.15);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
color: var(--primary-gold-dark);
font-weight: 600;
}
.footer-text {
text-align: center;
margin-top: 20px;
font-size: 12px;
color: var(--text-muted);
font-weight: 500;
}
.microsoft-icon {
display: inline-block;
margin-right: 8px;
}
</style>
</head>
<body>
<div class="login-container">
<div class="logo">
<h1>🎯 Oliver Metadata Tool</h1>
<p>Sign in to continue</p>
</div>
{% if error %}
<div class="alert alert-error">
⚠️ {{ error }}
</div>
{% endif %}
{% if info %}
<div class="alert alert-info">
{{ info }}
</div>
{% endif %}
<form method="POST" action="/solventum-image-metadata/login">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autofocus placeholder="Enter your username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required placeholder="Enter your password">
</div>
<button type="submit" class="btn btn-primary">
🔐 Sign In
</button>
</form>
{% if sso_enabled %}
<div class="divider">
<span>OR</span>
</div>
<button type="button" class="btn btn-sso" onclick="loginWithMicrosoft()">
<span class="microsoft-icon">
<svg width="20" height="20" viewBox="0 0 23 23" style="vertical-align: middle;">
<path fill="#f25022" d="M1 1h10v10H1z"/>
<path fill="#00a4ef" d="M12 1h10v10H12z"/>
<path fill="#7fba00" d="M1 12h10v10H1z"/>
<path fill="#ffb900" d="M12 12h10v10H12z"/>
</svg>
</span>
Sign in with Microsoft
</button>
<script src="https://alcdn.msauth.net/browser/2.38.0/js/msal-browser.min.js"></script>
<script>
const msalConfig = {
auth: {
clientId: "{{ azure_client_id }}",
authority: "https://login.microsoftonline.com/{{ azure_tenant_id }}",
redirectUri: "{{ azure_redirect_uri }}"
},
cache: {
cacheLocation: "sessionStorage"
}
};
let msalInstance = null;
let msalReady = false;
// Initialize MSAL and handle any pending redirects
async function initMsal() {
try {
msalInstance = new msal.PublicClientApplication(msalConfig);
await msalInstance.initialize();
// Handle any pending redirect (in case user lands on login page after OAuth)
const response = await msalInstance.handleRedirectPromise();
if (response && response.accessToken) {
// User completed auth, send token to server
await sendTokenToServer(response.accessToken);
return;
}
msalReady = true;
} catch (error) {
console.error("MSAL init error:", error);
// Clear any stuck interaction state
sessionStorage.clear();
msalReady = true;
}
}
initMsal();
async function loginWithMicrosoft() {
if (!msalReady) {
alert("Please wait, authentication is initializing...");
return;
}
const loginRequest = {
scopes: ["User.Read", "openid", "profile"]
};
try {
await msalInstance.loginRedirect(loginRequest);
} catch (error) {
console.error("Login error:", error);
if (error.errorCode === "interaction_in_progress") {
// Clear stuck state and retry
sessionStorage.clear();
alert("Please click the button again.");
} else {
alert("Login failed: " + error.message);
}
}
}
async function sendTokenToServer(accessToken) {
try {
const response = await fetch("/solventum-image-metadata/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ access_token: accessToken })
});
const data = await response.json();
if (data.success && data.redirect) {
window.location.href = data.redirect;
}
} catch (error) {
console.error("Server error:", error);
}
}
</script>
{% endif %}
<div class="footer-text">
Oliver Metadata Tool v3.1 | Enterprise Edition
</div>
</div>
</body>
</html>