From 81206d93f3d45728d58f30f8698d94fc10ea71c5 Mon Sep 17 00:00:00 2001 From: tsmethurst Date: Thu, 22 Jul 2021 11:52:17 +0200 Subject: [PATCH] further oidc --- cmd/gotosocial/oidcflags.go | 6 --- example/config.yaml | 72 ++++++++++++++++++++++++++- internal/api/client/auth/auth.go | 1 + internal/api/client/auth/authorize.go | 38 +++++++------- internal/api/client/auth/callback.go | 72 ++++++++++++++++++++++++--- internal/api/client/auth/signin.go | 19 +++++-- internal/api/client/auth/util.go | 20 ++++++++ internal/config/config.go | 8 --- internal/config/default.go | 11 ++-- internal/config/oidc.go | 1 - internal/oidc/claims.go | 6 ++- internal/oidc/handlecallback.go | 13 +++-- internal/oidc/idp.go | 9 +--- internal/router/session.go | 21 +++++--- 14 files changed, 227 insertions(+), 70 deletions(-) create mode 100644 internal/api/client/auth/util.go diff --git a/cmd/gotosocial/oidcflags.go b/cmd/gotosocial/oidcflags.go index 1fefcd25c..93b86b166 100644 --- a/cmd/gotosocial/oidcflags.go +++ b/cmd/gotosocial/oidcflags.go @@ -31,12 +31,6 @@ func oidcFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli Value: defaults.OIDCEnabled, EnvVars: []string{envNames.OIDCEnabled}, }, - &cli.StringFlag{ - Name: flagNames.OIDCIdpID, - Usage: "ID of the OIDC identity provider.", - Value: defaults.OIDCIdpID, - EnvVars: []string{envNames.OIDCIdpID}, - }, &cli.StringFlag{ Name: flagNames.OIDCIdpName, Usage: "Name of the OIDC identity provider. Will be shown to the user when logging in.", diff --git a/example/config.yaml b/example/config.yaml index 149da46dd..26845e411 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -17,6 +17,7 @@ ########################### ##### GENERAL CONFIG ###### ########################### + # String. Log level to use throughout the application. Must be lower-case. # Options: ["trace","debug","info","warn","error","fatal"] # Default: "info" @@ -54,8 +55,10 @@ protocol: "https" ############################ ##### DATABASE CONFIG ###### ############################ + # Config pertaining to the Gotosocial database connection db: + # String. Database type. # Options: ["postgres"] # Default: "postgres" @@ -105,8 +108,10 @@ db: ############################### ##### WEB TEMPLATE CONFIG ##### ############################### + # Config pertaining to templating of web pages/email notifications and the like template: + # String. Directory from which gotosocial will attempt to load html templates (.tmpl files). # Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"] # Default: "./web/template/" @@ -120,8 +125,10 @@ template: ########################### ##### ACCOUNTS CONFIG ##### ########################### + # Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts. accounts: + # Bool. Do we want people to be able to just submit sign up requests, or do we want invite only? # Options: [true, false] # Default: true @@ -140,8 +147,10 @@ accounts: ######################## ##### MEDIA CONFIG ##### ######################## + # Config pertaining to user media uploads (videos, image, image descriptions). media: + # Int. Maximum allowed image upload size in bytes. # Examples: [2097152, 10485760] # Default: 2097152 -- aka 2MB @@ -165,8 +174,10 @@ media: ########################## ##### STORAGE CONFIG ##### ########################## + # Config pertaining to storage of user-created uploads (videos, images, etc). storage: + # String. Type of storage backend to use. # Examples: ["local", "s3"] # Default: "local" (storage on local disk) @@ -203,8 +214,10 @@ storage: ########################### ##### STATUSES CONFIG ##### ########################### + # Config pertaining to the creation of statuses/posts, and permitted limits. statuses: + # Int. Maximum amount of characters permitted for a new status. # Note that going way higher than the default might break federation. # Examples: [140, 500, 5000] @@ -238,8 +251,10 @@ statuses: ############################## ##### LETSENCRYPT CONFIG ##### ############################## + # Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates. letsEncrypt: + # Bool. Whether or not letsencrypt should be enabled for the server. # If true, the server will serve on port 443 (https) and obtain letsencrypt # certificates automatically. @@ -248,7 +263,7 @@ letsEncrypt: # You should only change this if you want to serve GoToSocial behind a reverse proxy # like Traefik, HAProxy, or Nginx. # Options: [true, false] - # Default: true + # Default: true enabled: true # String. Directory in which to store LetsEncrypt certificates. @@ -265,3 +280,58 @@ letsEncrypt: # Examples: ["admin@example.org"] # Default: "" emailAddress: "" + +####################### +##### OIDC CONFIG ##### +####################### + +# Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc). +oidc: + + # Bool. Enable authentication with external OIDC provider. If set to true, then + # the other OIDC options must be set as well. If this is set to false, then the standard + # internal oauth flow will be used, where users sign in to GtS with username/password. + # Options: [true, false] + # Default: false + enabled: false + + # String. Name of the oidc idp (identity provider). This will be shown to users when + # they log in. + # Examples: ["Google", "Dex", "Auth0"] + # Default: "" + idpName: "" + + # Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie., + # don't check the expiry or signature. This should only be used in debugging or testing, + # never ever in a production environment as it's extremely unsafe! + # Options: [true, false] + # Default: false + skipVerification: false + + # String. The OIDC issuer URI. This is where GtS will redirect users to for login. + # Typically this will look like a standard web URL. + # Examples: ["https://auth.example.org", "https://example.org/auth"] + # Default: "" + issuer: "" + + # String. The ID for this client as registered with the OIDC provider. + # Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"] + # Default: "" + clientID: "" + + # String. The secret for this client as registered with the OIDC provider. + # Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"] + # Default: "" + clientSecret: "" + + # Array of string. Scopes to request from the OIDC provider. The returned values will be used to + # populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required. + # 'profile' is used to extract a username for the newly created user. + # 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins'). + # Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes + # Default: ["openid", "email", "profile", "groups"] + scopes: + - "openid" + - "email" + - "profile" + - "groups" diff --git a/internal/api/client/auth/auth.go b/internal/api/client/auth/auth.go index 83f2cee1e..047ccd8b9 100644 --- a/internal/api/client/auth/auth.go +++ b/internal/api/client/auth/auth.go @@ -50,6 +50,7 @@ const ( sessionResponseType = "response_type" sessionCode = "code" sessionScope = "scope" + sessionState = "state" ) var sessionKeys []string = []string{ diff --git a/internal/api/client/auth/authorize.go b/internal/api/client/auth/authorize.go index fb16b00fc..27a3cc7f9 100644 --- a/internal/api/client/auth/authorize.go +++ b/internal/api/client/auth/authorize.go @@ -23,6 +23,7 @@ import ( "fmt" "net/http" "net/url" + "strings" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" @@ -46,6 +47,8 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { form := &model.OAuthAuthorize{} if err := c.Bind(form); err != nil { l.Debugf("invalid auth form: %s", err) + m.clearSession(s) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } l.Tracef("parsed auth form: %+v", form) @@ -69,6 +72,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { ClientID: clientID, } if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil { + m.clearSession(s) c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)}) return } @@ -78,6 +82,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { ID: userID, } if err := m.db.GetByID(user.ID, user); err != nil { + m.clearSession(s) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -87,6 +92,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { } if err := m.db.GetByID(acct.ID, acct); err != nil { + m.clearSession(s) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } @@ -94,11 +100,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { // Finally we should also get the redirect and scope of this particular request, as stored in the session. redirect, ok := s.Get(sessionRedirectURI).(string) if !ok || redirect == "" { + m.clearSession(s) c.JSON(http.StatusInternalServerError, gin.H{"error": "no redirect_uri found in session"}) return } scope, ok := s.Get(sessionScope).(string) if !ok || scope == "" { + m.clearSession(s) c.JSON(http.StatusInternalServerError, gin.H{"error": "no scope found in session"}) return } @@ -128,48 +136,42 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) { // recreate it on the request so that it can be used further by the oauth2 library. // So first fetch all the values from the session. + errs := []string{} + forceLogin, ok := s.Get(sessionForceLogin).(string) if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing force_login"}) - return + errs = append(errs, "session missing force_login") } responseType, ok := s.Get(sessionResponseType).(string) if !ok || responseType == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing response_type"}) - return + errs = append(errs, "session missing response_type") } clientID, ok := s.Get(sessionClientID).(string) if !ok || clientID == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing client_id"}) - return + errs = append(errs, "session missing client_id") } redirectURI, ok := s.Get(sessionRedirectURI).(string) if !ok || redirectURI == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing redirect_uri"}) - return + errs = append(errs, "session missing redirect_uri") } scope, ok := s.Get(sessionScope).(string) if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing scope"}) - return + errs = append(errs, "session missing scope") } userID, ok := s.Get(sessionUserID).(string) if !ok { - c.JSON(http.StatusBadRequest, gin.H{"error": "session missing userid"}) - return + errs = append(errs, "session missing userid") } - // we're done with the session so we can clear it now - for _, key := range sessionKeys { - s.Delete(key) - } - if err := s.Save(); err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + m.clearSession(s) + + if len(errs) != 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": strings.Join(errs, ": ")}) return } diff --git a/internal/api/client/auth/callback.go b/internal/api/client/auth/callback.go index a3f56a77a..a9e22a2d9 100644 --- a/internal/api/client/auth/callback.go +++ b/internal/api/client/auth/callback.go @@ -19,21 +19,81 @@ package auth import ( + "errors" "net/http" + "strings" + "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/oidc" ) // CallbackGETHandler parses a token from an external auth provider. func (m *Module) CallbackGETHandler(c *gin.Context) { - state := c.Query(callbackStateParam) - code := c.Query(callbackCodeParam) + s := sessions.Default(c) - claims, err := m.idp.HandleCallback(c.Request.Context(), state, code) - if err != nil { - c.String(http.StatusForbidden, err.Error()) + // first make sure the state set in the cookie is the same as the state returned from the external provider + state := c.Query(callbackStateParam) + if state == "" { + m.clearSession(s) + c.JSON(http.StatusForbidden, gin.H{"error": "state query not found on callback"}) return } - c.JSON(http.StatusOK, claims) + savedStateI := s.Get(sessionState) + savedState, ok := savedStateI.(string) + if !ok { + m.clearSession(s) + c.JSON(http.StatusForbidden, gin.H{"error": "state not found in session"}) + return + } + + if state != savedState { + m.clearSession(s) + c.JSON(http.StatusForbidden, gin.H{"error": "state mismatch"}) + return + } + + code := c.Query(callbackCodeParam) + + claims, err := m.idp.HandleCallback(c.Request.Context(), code) + if err != nil { + m.clearSession(s) + c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) + return + } + + user, err := m.parseUserFromClaims(claims) + if err != nil { + m.clearSession(s) + c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) + return + } + + s.Set(sessionUserID, user.ID) + if err := s.Save(); err != nil { + m.clearSession(s) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.Redirect(http.StatusFound, OauthAuthorizePath) +} + +func (m *Module) parseUserFromClaims(claims *oidc.Claims) (*gtsmodel.User, error) { + if claims.Email == "" { + return nil, errors.New("no email returned in claims") + } + // see if we already have a user for this email address + + + if claims.Name == "" { + return nil, errors.New("no name returned in claims") + } + username := "" + nameParts := strings.Split(claims.Name, " ") + for i, n := range nameParts { +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } } diff --git a/internal/api/client/auth/signin.go b/internal/api/client/auth/signin.go index 94b8fd2a2..0e6895814 100644 --- a/internal/api/client/auth/signin.go +++ b/internal/api/client/auth/signin.go @@ -24,6 +24,7 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "github.com/google/uuid" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "golang.org/x/crypto/bcrypt" @@ -41,9 +42,18 @@ type login struct { func (m *Module) SignInGETHandler(c *gin.Context) { l := m.log.WithField("func", "SignInGETHandler") l.Trace("entering sign in handler") - if m.idp != nil && m.config.OIDCConfig.Issuer != "" { - l.Debug("redirecting to external idp at %s", m.config.OIDCConfig.Issuer) - c.Redirect(http.StatusFound, m.config.OIDCConfig.Issuer) + if m.idp != nil { + s := sessions.Default(c) + state := uuid.NewString() + s.Set(sessionState, state) + if err := s.Save(); err != nil { + m.clearSession(s) + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + redirect := m.idp.AuthCodeURL(state) + l.Debugf("redirecting to external idp at %s", redirect) + c.Redirect(http.StatusSeeOther, redirect) return } c.HTML(http.StatusOK, "sign-in.tmpl", gin.H{}) @@ -58,6 +68,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) { form := &login{} if err := c.ShouldBind(form); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + m.clearSession(s) return } l.Tracef("parsed form: %+v", form) @@ -65,12 +76,14 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) { userid, err := m.ValidatePassword(form.Email, form.Password) if err != nil { c.String(http.StatusForbidden, err.Error()) + m.clearSession(s) return } s.Set(sessionUserID, userid) if err := s.Save(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + m.clearSession(s) return } diff --git a/internal/api/client/auth/util.go b/internal/api/client/auth/util.go new file mode 100644 index 000000000..cbba78871 --- /dev/null +++ b/internal/api/client/auth/util.go @@ -0,0 +1,20 @@ +package auth + +import ( + "github.com/gin-contrib/sessions" + "github.com/superseriousbusiness/gotosocial/internal/router" +) + +func (m *Module) clearSession(s sessions.Session) { + for _, key := range sessionKeys { + s.Delete(key) + } + + newOptions := router.SessionOptions(m.config) + newOptions.MaxAge = -1 // instruct browser to delete cookie immediately + s.Options(newOptions) + + if err := s.Save(); err != nil { + panic(err) + } +} diff --git a/internal/config/config.go b/internal/config/config.go index 7f59cd38d..117b8efb5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -275,10 +275,6 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error { c.OIDCConfig.Enabled = f.Bool(fn.OIDCEnabled) } - if c.OIDCConfig.IDPID == "" || f.IsSet(fn.OIDCIdpID) { - c.OIDCConfig.IDPID = f.String(fn.OIDCIdpID) - } - if c.OIDCConfig.IDPName == "" || f.IsSet(fn.OIDCIdpName) { c.OIDCConfig.IDPName = f.String(fn.OIDCIdpName) } @@ -372,7 +368,6 @@ type Flags struct { LetsEncryptEmailAddress string OIDCEnabled string - OIDCIdpID string OIDCIdpName string OIDCSkipVerification string OIDCIssuer string @@ -429,7 +424,6 @@ type Defaults struct { LetsEncryptEmailAddress string OIDCEnabled bool - OIDCIdpID string OIDCIdpName string OIDCSkipVerification bool OIDCIssuer string @@ -487,7 +481,6 @@ func GetFlagNames() Flags { LetsEncryptEmailAddress: "letsencrypt-email", OIDCEnabled: "oidc-enabled", - OIDCIdpID: "oidc-idp-id", OIDCIdpName: "oidc-idp-name", OIDCSkipVerification: "oidc-skip-verification", OIDCIssuer: "oidc-issuer", @@ -546,7 +539,6 @@ func GetEnvNames() Flags { LetsEncryptEmailAddress: "GTS_LETSENCRYPT_EMAIL", OIDCEnabled: "GTS_OIDC_ENABLED", - OIDCIdpID: "GTS_OIDC_IDP_ID", OIDCIdpName: "GTS_OIDC_IDP_NAME", OIDCSkipVerification: "GTS_OIDC_SKIP_VERIFICATION", OIDCIssuer: "GTS_OIDC_ISSUER", diff --git a/internal/config/default.go b/internal/config/default.go index 27b6d3a45..61940eff4 100644 --- a/internal/config/default.go +++ b/internal/config/default.go @@ -56,7 +56,6 @@ func TestDefault() *Config { }, OIDCConfig: &OIDCConfig{ Enabled: defaults.OIDCEnabled, - IDPID: defaults.OIDCIdpID, IDPName: defaults.OIDCIdpName, SkipVerification: defaults.OIDCSkipVerification, Issuer: defaults.OIDCIssuer, @@ -121,7 +120,6 @@ func Default() *Config { }, OIDCConfig: &OIDCConfig{ Enabled: defaults.OIDCEnabled, - IDPID: defaults.OIDCIdpID, IDPName: defaults.OIDCIdpName, SkipVerification: defaults.OIDCSkipVerification, Issuer: defaults.OIDCIssuer, @@ -181,7 +179,6 @@ func GetDefaults() Defaults { LetsEncryptEmailAddress: "", OIDCEnabled: false, - OIDCIdpID: "", OIDCIdpName: "", OIDCSkipVerification: false, OIDCIssuer: "", @@ -235,5 +232,13 @@ func GetTestDefaults() Defaults { LetsEncryptEnabled: false, LetsEncryptCertDir: "", LetsEncryptEmailAddress: "", + + OIDCEnabled: false, + OIDCIdpName: "", + OIDCSkipVerification: false, + OIDCIssuer: "", + OIDCClientID: "", + OIDCClientSecret: "", + OIDCScopes: []string{oidc.ScopeOpenID, "profile", "email", "groups"}, } } diff --git a/internal/config/oidc.go b/internal/config/oidc.go index bf8c49924..06158bbb7 100644 --- a/internal/config/oidc.go +++ b/internal/config/oidc.go @@ -21,7 +21,6 @@ package config // OIDCConfig contains configuration values for openID connect (oauth) authorization by an external service such as Dex. type OIDCConfig struct { Enabled bool `yaml:"enabled"` - IDPID string `yaml:"idpId"` IDPName string `yaml:"idpName"` SkipVerification bool `yaml:"skipVerification"` Issuer string `yaml:"issuer"` diff --git a/internal/oidc/claims.go b/internal/oidc/claims.go index 28ff21391..b58c4662b 100644 --- a/internal/oidc/claims.go +++ b/internal/oidc/claims.go @@ -20,6 +20,8 @@ package oidc // Claims represents claims as found in an id_token returned from an OIDC flow. type Claims struct { - Email string `json:"email"` - Groups []string `json:"groups"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Groups []string `json:"groups"` + Name string `json:"name"` } diff --git a/internal/oidc/handlecallback.go b/internal/oidc/handlecallback.go index 45db201fd..5bfaef41e 100644 --- a/internal/oidc/handlecallback.go +++ b/internal/oidc/handlecallback.go @@ -24,13 +24,8 @@ import ( "fmt" ) -func (i *idp) HandleCallback(ctx context.Context, state string, code string) (*Claims, error) { +func (i *idp) HandleCallback(ctx context.Context, code string) (*Claims, error) { l := i.log.WithField("func", "HandleCallback") - - if state == "" { - return nil, errors.New("state was empty string") - } - if code == "" { return nil, errors.New("code was empty string") } @@ -48,7 +43,7 @@ func (i *idp) HandleCallback(ctx context.Context, state string, code string) (*C if !ok { return nil, errors.New("no id_token in oauth2token") } - l.Debug("raw id token: %s", rawIDToken) + l.Debugf("raw id token: %s", rawIDToken) // Parse and verify ID Token payload. l.Debug("verifying id_token") @@ -66,3 +61,7 @@ func (i *idp) HandleCallback(ctx context.Context, state string, code string) (*C return claims, nil } + +func (i *idp) AuthCodeURL(state string) string { + return i.oauth2Config.AuthCodeURL(state) +} diff --git a/internal/oidc/idp.go b/internal/oidc/idp.go index a9b3e53fb..913caee37 100644 --- a/internal/oidc/idp.go +++ b/internal/oidc/idp.go @@ -31,13 +31,11 @@ import ( const ( // CallbackPath is the API path for receiving callback tokens from external OIDC providers CallbackPath = "/auth/callback" - profileScope = "profile" - emailScope = "email" - groupsScope = "groups" ) type IDP interface { - HandleCallback(ctx context.Context, state string, code string) (*Claims, error) + HandleCallback(ctx context.Context, code string) (*Claims, error) + AuthCodeURL(state string) string } type idp struct { @@ -55,9 +53,6 @@ func NewIDP(config *config.Config, log *logrus.Logger) (IDP, error) { } // validate config fields - if config.OIDCConfig.IDPID == "" { - return nil, fmt.Errorf("not set: IDPID") - } if config.OIDCConfig.IDPName == "" { return nil, fmt.Errorf("not set: IDPName") } diff --git a/internal/router/session.go b/internal/router/session.go index 2d00f7677..2ba4f1b5f 100644 --- a/internal/router/session.go +++ b/internal/router/session.go @@ -33,6 +33,18 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/id" ) +// SessionOptions returns the standard set of options to use for each session. +func SessionOptions(cfg *config.Config) sessions.Options { + return sessions.Options{ + Path: "/", + Domain: cfg.Host, + MaxAge: 120, // 2 minutes + Secure: true, // only use cookie over https + HttpOnly: true, // exclude javascript from inspecting cookie + SameSite: http.SameSiteStrictMode, // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1 + } +} + func useSession(cfg *config.Config, dbService db.DB, engine *gin.Engine) error { // check if we have a saved router session already routerSessions := []*gtsmodel.RouterSession{} @@ -64,14 +76,7 @@ func useSession(cfg *config.Config, dbService db.DB, engine *gin.Engine) error { } store := memstore.NewStore(rs.Auth, rs.Crypt) - store.Options(sessions.Options{ - Path: "/", - Domain: cfg.Host, - MaxAge: 120, // 2 minutes - Secure: true, // only use cookie over https - HttpOnly: true, // exclude javascript from inspecting cookie - SameSite: http.SameSiteStrictMode, // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1 - }) + store.Options(SessionOptions(cfg)) sessionName := fmt.Sprintf("gotosocial-%s", cfg.Host) engine.Use(sessions.Sessions(sessionName, store)) return nil