diff --git a/api/users.go b/api/users.go index 175a70eff..2cc3add55 100644 --- a/api/users.go +++ b/api/users.go @@ -87,7 +87,7 @@ func (r usersRoutes) impersonateUser(c *gin.Context) { }) return } - tools.StartSession(c, &tools.SessionData{Userid: u.ID}) + tools.StartSession(c, &tools.SessionData{Userid: u.ID}, false) } func (r usersRoutes) updateUser(c *gin.Context) { diff --git a/tools/middlewares.go b/tools/middlewares.go index a1de2c36b..432570fec 100644 --- a/tools/middlewares.go +++ b/tools/middlewares.go @@ -6,6 +6,7 @@ import ( "net/url" "strconv" "strings" + "time" "github.com/TUM-Dev/gocast/dao" "github.com/TUM-Dev/gocast/model" @@ -15,6 +16,11 @@ import ( "github.com/golang-jwt/jwt/v4" ) +const ( + MaxTokenLifetimeWithRememberMeInDays = 180 + MinUpdateIntervalInHours = 1 +) + var templateExecutor TemplateExecutor // SetTemplateExecutor sets the templates and template executor for the middlewares to execute error pages @@ -25,8 +31,10 @@ func SetTemplateExecutor(e TemplateExecutor) { // JWTClaims are the claims contained in a session type JWTClaims struct { *jwt.RegisteredClaims + UpdatedAt *jwt.NumericDate UserID uint SamlSubjectID *string // identifier of the SAML session (if any) + RememberMe bool } func InitContext(daoWrapper dao.DaoWrapper) gin.HandlerFunc { @@ -62,6 +70,28 @@ func InitContext(daoWrapper dao.DaoWrapper) gin.HandlerFunc { return } + claims := token.Claims.(*JWTClaims) + + // in case when the user has selected "remember me" when logging in, prolong the validity of the token + // but only when the token has not been updated during the last 1 hour + if claims.RememberMe && time.Since(claims.UpdatedAt.Time).Hours() > MinUpdateIntervalInHours { + // remove jwt cookie older than MaxTokenAgeWithRefreshInDays + expiresAt := &jwt.NumericDate{Time: time.Now().Add(time.Hour * 24 * MaxTokenAgeInDays)} + if expiresAt.Sub(claims.IssuedAt.Time).Hours() > MaxTokenLifetimeWithRememberMeInDays*24 { + c.SetCookie("jwt", "", -1, "/", "", false, true) + return + } + claims.ExpiresAt = expiresAt + claims.UpdatedAt = &jwt.NumericDate{Time: time.Now()} + + token = jwt.NewWithClaims(token.Method, claims) + signedToken, err := token.SignedString(Cfg.GetJWTKey()) + if err == nil { + c.SetCookie("jwt", signedToken, 60*60*24*MaxTokenAgeInDays, "/", "", CookieSecure, true) + } + logger.Error(signedToken) + } + user, err := daoWrapper.UsersDao.GetUserByID(c, token.Claims.(*JWTClaims).UserID) if err != nil { c.Set("TUMLiveContext", TUMLiveContext{}) diff --git a/tools/session.go b/tools/session.go index 41f485ca7..712954156 100644 --- a/tools/session.go +++ b/tools/session.go @@ -7,29 +7,36 @@ import ( "github.com/golang-jwt/jwt/v4" ) +const ( + MaxTokenAgeInDays = 7 +) + type SessionData struct { Userid uint SamlSubjectID *string } -func StartSession(c *gin.Context, data *SessionData) { - token, err := createToken(data.Userid, data.SamlSubjectID) +func StartSession(c *gin.Context, data *SessionData, rememberMe bool) { + token, err := createToken(data.Userid, data.SamlSubjectID, rememberMe) if err != nil { logger.Error("Could not create token", "err", err) return } - c.SetCookie("jwt", token, 60*60*24*7, "/", "", CookieSecure, true) + c.SetCookie("jwt", token, 60*60*24*MaxTokenAgeInDays, "/", "", CookieSecure, true) } -func createToken(user uint, samlSubjectID *string) (string, error) { +func createToken(user uint, samlSubjectID *string, rememberMe bool) (string, error) { t := jwt.New(jwt.GetSigningMethod("RS256")) t.Claims = &JWTClaims{ RegisteredClaims: &jwt.RegisteredClaims{ - ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(time.Hour * 24 * 7)}, // Token expires in one week + IssuedAt: &jwt.NumericDate{Time: time.Now()}, + ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(time.Hour * 24 * MaxTokenAgeInDays)}, // Token expires in one week }, + UpdatedAt: &jwt.NumericDate{Time: time.Now()}, UserID: user, SamlSubjectID: samlSubjectID, + RememberMe: rememberMe, } return t.SignedString(Cfg.GetJWTKey()) } diff --git a/web/template/login.gohtml b/web/template/login.gohtml index 5e45f4150..8425f7b80 100644 --- a/web/template/login.gohtml +++ b/web/template/login.gohtml @@ -24,6 +24,13 @@ + + +
@@ -39,6 +46,19 @@

Login

+ +
+ + + +
+ {{if .UseSAML}}