diff --git a/public/app.js b/public/app.js
index 28e9b9c..fb5e607 100644
--- a/public/app.js
+++ b/public/app.js
@@ -136,6 +136,7 @@ async function handleLogin(e) {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
+ const rememberMe = document.getElementById('remember-me').checked;
try {
const response = await fetch('/api/auth/login', {
@@ -143,7 +144,7 @@ async function handleLogin(e) {
headers: {
'Content-Type': 'application/json'
},
- body: JSON.stringify({ username, password })
+ body: JSON.stringify({ username, password, rememberMe })
});
const data = await response.json();
diff --git a/public/index.html b/public/index.html
index 6f5a0fc..5c23feb 100644
--- a/public/index.html
+++ b/public/index.html
@@ -31,6 +31,12 @@
+
+
+
diff --git a/public/style.css b/public/style.css
index a8a04b1..8e8220b 100644
--- a/public/style.css
+++ b/public/style.css
@@ -612,6 +612,32 @@ body {
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 {
width: 100%;
padding: 10px;
diff --git a/server/routes/auth.js b/server/routes/auth.js
index 1e446ad..1b82a21 100644
--- a/server/routes/auth.js
+++ b/server/routes/auth.js
@@ -34,7 +34,7 @@ const loginLimiter = rateLimit({
// Authenticate user with Emby
router.post('/login', loginLimiter, async (req, res) => {
try {
- const { username, password } = req.body;
+ const { username, password, rememberMe } = req.body;
console.log(`[Auth] Attempting login for user: ${username}`);
@@ -68,15 +68,20 @@ router.post('/login', loginLimiter, async (req, res) => {
storeToken(user.Id, authData.AccessToken);
// 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 signed = !!process.env.COOKIE_SECRET;
- res.cookie('emby_user', cookiePayload, {
+ const cookieOptions = {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
- signed,
- maxAge: 24 * 60 * 60 * 1000 // 24 hours
- });
+ signed
+ };
+ if (rememberMe) {
+ cookieOptions.maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days
+ }
+ res.cookie('emby_user', cookiePayload, cookieOptions);
res.json({
success: true,