diff --git a/client/html/auth.html b/client/html/auth.html
index 192488c9..0010f4bc 100644
--- a/client/html/auth.html
+++ b/client/html/auth.html
@@ -201,10 +201,14 @@ navigator.serviceWorker.register(workerURL, {
document.getElementById("login").addEventListener("submit", (e) => {
const enableEncryption = document.getElementById('clientEncryption').checked;
const params = new URLSearchParams();
+ const rememberMe = document.getElementById('rememberMe').checked;
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
params.append('username', username);
params.append('password', password);
+ if (rememberMe) {
+ params.append('rememberMe', 'true');
+ }
fetch('.auth', {
method: 'POST',
headers: {
diff --git a/server/auth.go b/server/auth.go
index 96a47854..0a20328a 100644
--- a/server/auth.go
+++ b/server/auth.go
@@ -125,11 +125,15 @@ func addAuthEndpoints(r chi.Router, config *ServerConfig) {
}
host := extractHost(r)
- inAWeek := time.Now().Add(time.Duration(authenticationExpirySeconds) * time.Second)
+ expirySeconds := authenticationExpirySeconds
+ if rememberMe != "" {
+ expirySeconds = spaceConfig.Auth.RememberMeHours * 60 * 60
+ }
+ expires := time.Now().Add(time.Duration(expirySeconds) * time.Second)
cookieOptions := CookieOptions{
Path: fmt.Sprintf("%s/", config.HostURLPrefix),
- Expires: inAWeek,
+ Expires: expires,
}
setCookie(w, authCookieName(host), jwt, cookieOptions)
@@ -245,28 +249,44 @@ func authMiddleware(config *ServerConfig) func(http.Handler) http.Handler {
return
}
- refreshLogin(w, r, config, host)
+ refreshLogin(w, r, config, host, spaceConfig)
next.ServeHTTP(w, r)
})
}
}
// refreshLogin refreshes the login cookie if needed
-func refreshLogin(w http.ResponseWriter, r *http.Request, config *ServerConfig, host string) {
+func refreshLogin(w http.ResponseWriter, r *http.Request, config *ServerConfig, host string, spaceConfig *SpaceConfig) {
+ // if cookie doesn't exist, return
if getCookie(r, "refreshLogin") != "" {
- inAWeek := time.Now().Add(time.Duration(authenticationExpirySeconds) * time.Second)
- jwt := getCookie(r, authCookieName(host))
-
- if jwt != "" {
- cookieOptions := CookieOptions{
- Path: fmt.Sprintf("%s/", config.HostURLPrefix),
- Expires: inAWeek,
- }
-
- setCookie(w, authCookieName(host), jwt, cookieOptions)
- setCookie(w, "refreshLogin", "true", cookieOptions)
- }
+ return
}
+ oldJwt := getCookie(r, authCookieName(host))
+ if oldJwt == "" {
+ return
+ }
+
+ claims, err := spaceConfig.JwtIssuer.VerifyAndDecodeJWT(oldJwt)
+ if err != nil {
+ return
+ }
+
+ // Create new JWT with fresh expiry
+ expirySeconds := spaceConfig.Auth.RememberMeHours * 60 * 60
+ payload := map[string]any{"username": claims["username"]}
+ newJwt, err := spaceConfig.JwtIssuer.CreateJWT(payload, expirySeconds)
+ if err != nil {
+ return
+ }
+
+ expires := time.Now().Add(time.Duration(expirySeconds) * time.Second)
+ cookieOptions := CookieOptions{
+ Path: fmt.Sprintf("%s/", config.HostURLPrefix),
+ Expires: expires,
+ }
+
+ setCookie(w, authCookieName(host), newJwt, cookieOptions)
+ setCookie(w, "refreshLogin", "true", cookieOptions)
}
// LockoutTimer implements a simple rate limiter to prevent brute force attacks