148 lines
5.8 KiB
JavaScript

/**
* Authentication Module for Weight Tracker
* Provides password protection for the application
*/
const Auth = (() => {
// Session storage key
const AUTH_KEY = 'weight_tracker_auth';
// Default password hash (can be overridden via environment variables)
// This is a bcrypt hash of "password" - should be replaced in production
let passwordHash = '$2a$10$EgxHKjDDFcZKtQY9hl/N4.QvEQHCXVnQXw9dzFYlUDVKOcLMGp9eq';
// Login state
let isAuthenticated = false;
/**
* Initialize the authentication module
*/
const init = () => {
// Check for password hash in environment variables (passed via meta tag)
const envPasswordHash = document.querySelector('meta[name="password-hash"]')?.getAttribute('content');
if (envPasswordHash) {
passwordHash = envPasswordHash;
}
// Check if already authenticated
checkAuthStatus();
// If not authenticated, show login screen
if (!isAuthenticated) {
showLoginScreen();
}
};
/**
* Check if the user is authenticated
*/
const checkAuthStatus = () => {
const authData = sessionStorage.getItem(AUTH_KEY);
isAuthenticated = authData === 'true';
return isAuthenticated;
};
/**
* Show the login screen
*/
const showLoginScreen = () => {
// Create login overlay
const overlay = document.createElement('div');
overlay.className = 'login-overlay';
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'var(--background-color, #f8f9fa)';
overlay.style.zIndex = '9999';
overlay.style.display = 'flex';
overlay.style.justifyContent = 'center';
overlay.style.alignItems = 'center';
// Create login form
overlay.innerHTML = `
<div class="login-container" style="background-color: var(--card-bg, #ffffff); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); width: 100%; max-width: 400px; padding: 30px;">
<div style="text-align: center; margin-bottom: 30px;">
<h1 style="color: var(--primary-color, #4a6fa5); font-size: 2rem; margin-bottom: 5px;">Keep My Weight</h1>
</div>
<p style="color: #666; font-size: 1rem; margin-bottom: 30px; text-align: center;">Simple, private weight & meal tracking</p>
<form id="login-form">
<div style="margin-bottom: 20px;">
<label for="password" style="display: block; margin-bottom: 8px; font-weight: 500;">Password</label>
<input type="password" id="password" required style="width: 100%; padding: 12px; border-radius: 8px; border: 1px solid #e0e0e0; font-family: inherit; font-size: 1rem;">
</div>
<button type="submit" style="width: 100%; padding: 12px; background-color: var(--primary-color, #4a6fa5); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 1rem;">Login</button>
<div id="error-message" style="color: #d9534f; margin-top: 20px; text-align: center; font-size: 0.9rem;"></div>
</form>
<p style="margin-top: 30px; text-align: center; font-size: 0.8rem; color: #666;">Your data stays private, always. This password protects your personal health information.</p>
</div>
`;
// Add to DOM
document.body.appendChild(overlay);
// Handle form submission
const form = document.getElementById('login-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const password = document.getElementById('password').value;
const errorMessage = document.getElementById('error-message');
try {
// Use bcrypt.js for password verification
const bcrypt = window.dcodeIO?.bcrypt;
if (!bcrypt) {
throw new Error('bcrypt.js not loaded');
}
// Verify password
const isMatch = await new Promise((resolve) => {
bcrypt.compare(password, passwordHash, (err, result) => {
if (err) {
console.error('Error verifying password:', err);
resolve(false);
} else {
resolve(result);
}
});
});
if (isMatch) {
// Set as authenticated
sessionStorage.setItem(AUTH_KEY, 'true');
isAuthenticated = true;
// Remove login overlay
document.body.removeChild(overlay);
} else {
errorMessage.textContent = 'Invalid password. Please try again.';
}
} catch (error) {
console.error('Authentication error:', error);
errorMessage.textContent = 'An error occurred during authentication. Please try again.';
}
});
};
/**
* Log out the user
*/
const logout = () => {
sessionStorage.removeItem(AUTH_KEY);
isAuthenticated = false;
showLoginScreen();
};
// Return public API
return {
init,
checkAuthStatus,
logout
};
})();