feat: add 'Keep me logged in' checkbox to login form
- index.html: checkbox between password field and login button - app.js: reads #remember-me and passes rememberMe in POST body - auth.js: rememberMe=true sets 30-day maxAge; false = session cookie (expires when browser closes) - style.css: .form-group--checkbox and .checkbox-label styles
This commit is contained in:
@@ -136,6 +136,7 @@ async function handleLogin(e) {
|
|||||||
|
|
||||||
const username = document.getElementById('username').value;
|
const username = document.getElementById('username').value;
|
||||||
const password = document.getElementById('password').value;
|
const password = document.getElementById('password').value;
|
||||||
|
const rememberMe = document.getElementById('remember-me').checked;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/auth/login', {
|
const response = await fetch('/api/auth/login', {
|
||||||
@@ -143,7 +144,7 @@ async function handleLogin(e) {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ username, password })
|
body: JSON.stringify({ username, password, rememberMe })
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|||||||
@@ -31,6 +31,12 @@
|
|||||||
<label for="password">Password:</label>
|
<label for="password">Password:</label>
|
||||||
<input type="password" id="password" name="password" required>
|
<input type="password" id="password" name="password" required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group form-group--checkbox">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="remember-me" name="rememberMe">
|
||||||
|
<span>Keep me logged in</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<button type="submit" class="login-btn">Login</button>
|
<button type="submit" class="login-btn">Login</button>
|
||||||
</form>
|
</form>
|
||||||
<div id="login-error" class="error-message" style="display: none;"></div>
|
<div id="login-error" class="error-message" style="display: none;"></div>
|
||||||
|
|||||||
@@ -612,6 +612,32 @@ body {
|
|||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group--checkbox {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label input[type="checkbox"] {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
accent-color: var(--accent);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label span {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.login-btn {
|
.login-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const loginLimiter = rateLimit({
|
|||||||
// Authenticate user with Emby
|
// Authenticate user with Emby
|
||||||
router.post('/login', loginLimiter, async (req, res) => {
|
router.post('/login', loginLimiter, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { username, password } = req.body;
|
const { username, password, rememberMe } = req.body;
|
||||||
|
|
||||||
console.log(`[Auth] Attempting login for user: ${username}`);
|
console.log(`[Auth] Attempting login for user: ${username}`);
|
||||||
|
|
||||||
@@ -68,15 +68,20 @@ router.post('/login', loginLimiter, async (req, res) => {
|
|||||||
storeToken(user.Id, authData.AccessToken);
|
storeToken(user.Id, authData.AccessToken);
|
||||||
|
|
||||||
// Set authentication cookie (signed when COOKIE_SECRET is set).
|
// Set authentication cookie (signed when COOKIE_SECRET is set).
|
||||||
|
// rememberMe=true → persistent cookie, expires in 30 days
|
||||||
|
// rememberMe=false → session cookie, expires when browser closes
|
||||||
const cookiePayload = JSON.stringify({ id: user.Id, name: user.Name, isAdmin });
|
const cookiePayload = JSON.stringify({ id: user.Id, name: user.Name, isAdmin });
|
||||||
const signed = !!process.env.COOKIE_SECRET;
|
const signed = !!process.env.COOKIE_SECRET;
|
||||||
res.cookie('emby_user', cookiePayload, {
|
const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === 'production',
|
secure: process.env.NODE_ENV === 'production',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
signed,
|
signed
|
||||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
};
|
||||||
});
|
if (rememberMe) {
|
||||||
|
cookieOptions.maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days
|
||||||
|
}
|
||||||
|
res.cookie('emby_user', cookiePayload, cookieOptions);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user