cookie add domain & path, change expiration to duration from seconds.

This commit is contained in:
Miroslav Šedivý 2025-02-17 19:33:52 +01:00
parent 6957f51300
commit a7287dca25
6 changed files with 90 additions and 27 deletions

View file

@ -8,6 +8,16 @@ import (
"github.com/spf13/viper"
)
type SessionCookie struct {
Enabled bool
Name string
Expiration time.Duration
Secure bool
HTTPOnly bool
Domain string
Path string
}
type Session struct {
File string
@ -21,10 +31,7 @@ type Session struct {
HeartbeatInterval int
APIToken string
CookieEnabled bool
CookieName string
CookieExpiration time.Duration
CookieSecure bool
Cookie SessionCookie
}
func (Session) Init(cmd *cobra.Command) error {
@ -89,7 +96,7 @@ func (Session) Init(cmd *cobra.Command) error {
return err
}
cmd.PersistentFlags().Int("session.cookie.expiration", 365*24, "expiration of the cookie in hours")
cmd.PersistentFlags().Duration("session.cookie.expiration", 24*time.Hour, "expiration of the cookie")
if err := viper.BindPFlag("session.cookie.expiration", cmd.PersistentFlags().Lookup("session.cookie.expiration")); err != nil {
return err
}
@ -99,6 +106,21 @@ func (Session) Init(cmd *cobra.Command) error {
return err
}
cmd.PersistentFlags().Bool("session.cookie.http_only", true, "use http only cookies")
if err := viper.BindPFlag("session.cookie.http_only", cmd.PersistentFlags().Lookup("session.cookie.http_only")); err != nil {
return err
}
cmd.PersistentFlags().String("session.cookie.domain", "", "domain of the cookie")
if err := viper.BindPFlag("session.cookie.domain", cmd.PersistentFlags().Lookup("session.cookie.domain")); err != nil {
return err
}
cmd.PersistentFlags().String("session.cookie.path", "", "path of the cookie")
if err := viper.BindPFlag("session.cookie.path", cmd.PersistentFlags().Lookup("session.cookie.path")); err != nil {
return err
}
return nil
}
@ -139,10 +161,13 @@ func (s *Session) Set() {
s.HeartbeatInterval = viper.GetInt("session.heartbeat_interval")
s.APIToken = viper.GetString("session.api_token")
s.CookieEnabled = viper.GetBool("session.cookie.enabled")
s.CookieName = viper.GetString("session.cookie.name")
s.CookieExpiration = time.Duration(viper.GetInt("session.cookie.expiration")) * time.Hour
s.CookieSecure = viper.GetBool("session.cookie.secure")
s.Cookie.Enabled = viper.GetBool("session.cookie.enabled")
s.Cookie.Name = viper.GetString("session.cookie.name")
s.Cookie.Expiration = viper.GetDuration("session.cookie.expiration")
s.Cookie.Secure = viper.GetBool("session.cookie.secure")
s.Cookie.HTTPOnly = viper.GetBool("session.cookie.http_only")
s.Cookie.Domain = viper.GetString("session.cookie.domain")
s.Cookie.Path = viper.GetString("session.cookie.path")
}
func (s *Session) SetV2() {

View file

@ -11,22 +11,24 @@ import (
func (manager *SessionManagerCtx) CookieSetToken(w http.ResponseWriter, token string) {
sameSite := http.SameSiteDefaultMode
if manager.config.CookieSecure {
if manager.config.Cookie.Secure {
sameSite = http.SameSiteNoneMode
}
http.SetCookie(w, &http.Cookie{
Name: manager.config.CookieName,
Name: manager.config.Cookie.Name,
Value: token,
Expires: time.Now().Add(manager.config.CookieExpiration),
Secure: manager.config.CookieSecure,
Expires: time.Now().Add(manager.config.Cookie.Expiration),
Secure: manager.config.Cookie.Secure,
SameSite: sameSite,
HttpOnly: true,
HttpOnly: manager.config.Cookie.HTTPOnly,
Domain: manager.config.Cookie.Domain,
Path: manager.config.Cookie.Path,
})
}
func (manager *SessionManagerCtx) CookieClearToken(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie(manager.config.CookieName)
cookie, err := r.Cookie(manager.config.Cookie.Name)
if err != nil {
return
}
@ -57,7 +59,7 @@ func (manager *SessionManagerCtx) Authenticate(r *http.Request) (types.Session,
func (manager *SessionManagerCtx) getToken(r *http.Request) (string, bool) {
if manager.CookieEnabled() {
// get from Cookie
cookie, err := r.Cookie(manager.config.CookieName)
cookie, err := r.Cookie(manager.config.Cookie.Name)
if err == nil {
return cookie.Value, true
}

View file

@ -476,7 +476,7 @@ func (manager *SessionManagerCtx) Settings() types.Settings {
}
func (manager *SessionManagerCtx) CookieEnabled() bool {
return manager.config.CookieEnabled
return manager.config.Cookie.Enabled
}
// ---

View file

@ -349,16 +349,21 @@ If you disable the cookies, the token will be sent to the client in the login re
```yaml title="config.yaml"
session:
cookie:
# Whether the cookies are enabled or not.
enabled: true
# Name of the cookie used to store the session.
name: "NEKO_SESSION"
# Expiration time of the cookie in seconds.
expiration: 86400
# Whether the cookie is secure (HTTPS only) or not.
expiration: "24h"
secure: true
http_only: true
domain: ""
path: ""
```
- `enabled` - Whether the cookies are enabled or not.
- `name` - Name of the cookie used to store the session.
- `expiration` - Expiration time of the cookie, use [go duration format](https://pkg.go.dev/time#ParseDuration) (e.g., `24h`, `1h30m`, `60m`).
- `secure` and `http_only` - Ensures that the cookie is only sent over HTTPS and cannot be accessed by JavaScript, see [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#block_access_to_your_cookies) for more information.
- `domain` and `path` - Define where the cookie is valid, see [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent) for more information.
:::info
The `session.cookie.secure` is set to `true` by default, which means that the cookie is only sent over HTTPS. If you are using HTTP, you should really consider using HTTPS. Only for testing and development purposes should you consider setting it to `false`.
The `secure` and `http_only` are set to `true` by default, which means that the cookie is only sent over HTTPS. If you are using HTTP, you should really consider using HTTPS. Only for testing and development purposes should you consider setting it to `false`.
:::

View file

@ -486,6 +486,15 @@
"defaultValue": "true",
"description": "users can gain control only if at least one admin is in the room"
},
{
"key": [
"session",
"cookie",
"domain"
],
"type": "string",
"description": "domain of the cookie"
},
{
"key": [
"session",
@ -502,9 +511,19 @@
"cookie",
"expiration"
],
"type": "int",
"defaultValue": "8760",
"description": "expiration of the cookie in hours"
"type": "duration",
"defaultValue": "24h0m0s",
"description": "expiration of the cookie"
},
{
"key": [
"session",
"cookie",
"http_only"
],
"type": "boolean",
"defaultValue": "true",
"description": "use http only cookies"
},
{
"key": [
@ -516,6 +535,15 @@
"defaultValue": "NEKO_SESSION",
"description": "name of the cookie that holds token"
},
{
"key": [
"session",
"cookie",
"path"
],
"type": "string",
"description": "path of the cookie"
},
{
"key": [
"session",

View file

@ -50,9 +50,12 @@
--server.static string path to neko client files to serve
--session.api_token string API token for interacting with external services
--session.control_protection users can gain control only if at least one admin is in the room
--session.cookie.domain string domain of the cookie
--session.cookie.enabled whether cookies authentication should be enabled (default true)
--session.cookie.expiration int expiration of the cookie in hours (default 8760)
--session.cookie.expiration duration expiration of the cookie (default 24h0m0s)
--session.cookie.http_only use http only cookies (default true)
--session.cookie.name string name of the cookie that holds token (default "NEKO_SESSION")
--session.cookie.path string path of the cookie
--session.cookie.secure use secure cookies (default true)
--session.file string if sessions should be stored in a file, otherwise they will be stored only in memory
--session.heartbeat_interval int interval in seconds for sending heartbeat messages (default 120)