Same site (#850)
* switch to auth master for same-site * add same-site policy support #784 * add parse same site param
This commit is contained in:
@@ -134,6 +134,8 @@ _this is the recommended way to run remark42_
|
||||
| image.resize-height | IMAGE_RESIZE_HEIGHT | `900` | height of resized image |
|
||||
| auth.ttl.jwt | AUTH_TTL_JWT | `5m` | jwt TTL |
|
||||
| auth.ttl.cookie | AUTH_TTL_COOKIE | `200h` | cookie TTL |
|
||||
| auth.send-jwt-header | AUTH_SEND_JWT_HEADER | `false` | send JWT as a header instead of cookie |
|
||||
| auth.same-site | AUTH_SAME_SITE | `default` | set same site policy for cookies (`default`, `none`, `lax` or `strict`)|
|
||||
| auth.google.cid | AUTH_GOOGLE_CID | | Google OAuth client ID |
|
||||
| auth.google.csec | AUTH_GOOGLE_CSEC | | Google OAuth client secret |
|
||||
| auth.facebook.cid | AUTH_FACEBOOK_CID | | Facebook OAuth client ID |
|
||||
|
||||
@@ -79,10 +79,13 @@ type ServerCommand struct {
|
||||
|
||||
Auth struct {
|
||||
TTL struct {
|
||||
JWT time.Duration `long:"jwt" env:"JWT" default:"5m" description:"jwt TTL"`
|
||||
SendJWTHeader bool `long:"send-jwt-header" env:"SEND_JWT_HEADER" description:"send JWT as a header instead of cookie"`
|
||||
Cookie time.Duration `long:"cookie" env:"COOKIE" default:"200h" description:"auth cookie TTL"`
|
||||
JWT time.Duration `long:"jwt" env:"JWT" default:"5m" description:"jwt TTL"`
|
||||
Cookie time.Duration `long:"cookie" env:"COOKIE" default:"200h" description:"auth cookie TTL"`
|
||||
} `group:"ttl" namespace:"ttl" env-namespace:"TTL"`
|
||||
|
||||
SendJWTHeader bool `long:"send-jwt-header" env:"SEND_JWT_HEADER" description:"send JWT as a header instead of cookie"`
|
||||
SameSite string `long:"same-site" env:"SAME_SITE" description:"set same site policy for cookies" choice:"default" choice:"none" choice:"lax" choice:"strict" default:"default"` // nolint
|
||||
|
||||
Google AuthGroup `group:"google" namespace:"google" env-namespace:"GOOGLE" description:"Google OAuth"`
|
||||
Github AuthGroup `group:"github" namespace:"github" env-namespace:"GITHUB" description:"Github OAuth"`
|
||||
Facebook AuthGroup `group:"facebook" namespace:"facebook" env-namespace:"FACEBOOK" description:"Facebook OAuth"`
|
||||
@@ -445,7 +448,7 @@ func (s *ServerCommand) newServerApp() (*serverApp, error) {
|
||||
SimpleView: s.SimpleView,
|
||||
ProxyCORS: s.ProxyCORS,
|
||||
AllowedAncestors: s.AllowedHosts,
|
||||
SendJWTHeader: s.Auth.TTL.SendJWTHeader,
|
||||
SendJWTHeader: s.Auth.SendJWTHeader,
|
||||
}
|
||||
|
||||
srv.ScoreThresholds.Low, srv.ScoreThresholds.Critical = s.LowScore, s.CriticalScore
|
||||
@@ -899,8 +902,9 @@ func (s *ServerCommand) makeAuthenticator(ds *service.DataStore, avas avatar.Sto
|
||||
URL: strings.TrimSuffix(s.RemarkURL, "/"),
|
||||
Issuer: "remark42",
|
||||
TokenDuration: s.Auth.TTL.JWT,
|
||||
SendJWTHeader: s.Auth.TTL.SendJWTHeader,
|
||||
CookieDuration: s.Auth.TTL.Cookie,
|
||||
SendJWTHeader: s.Auth.SendJWTHeader,
|
||||
SameSiteCookie: s.parseSameSite(s.Auth.SameSite),
|
||||
SecureCookies: strings.HasPrefix(s.RemarkURL, "https://"),
|
||||
SecretReader: token.SecretFunc(func(aud string) (string, error) { // get secret per site
|
||||
return admns.Key()
|
||||
@@ -960,6 +964,21 @@ func (s *ServerCommand) makeAuthenticator(ds *service.DataStore, avas avatar.Sto
|
||||
return authenticator, nil
|
||||
}
|
||||
|
||||
func (s *ServerCommand) parseSameSite(ss string) http.SameSite {
|
||||
switch strings.ToLower(ss) {
|
||||
case "default":
|
||||
return http.SameSiteDefaultMode
|
||||
case "none":
|
||||
return http.SameSiteNoneMode
|
||||
case "lax":
|
||||
return http.SameSiteLaxMode
|
||||
case "strict":
|
||||
return http.SameSiteStrictMode
|
||||
default:
|
||||
return http.SameSiteDefaultMode
|
||||
}
|
||||
}
|
||||
|
||||
// authRefreshCache used by authenticator to minimize repeatable token refreshes
|
||||
type authRefreshCache struct {
|
||||
cache.LoadingCache
|
||||
|
||||
@@ -586,6 +586,29 @@ func TestServer_loadEmailTemplate(t *testing.T) {
|
||||
assert.Equal(t, r, "")
|
||||
}
|
||||
|
||||
func TestServerCommand_parseSameSite(t *testing.T) {
|
||||
|
||||
tbl := []struct {
|
||||
inp string
|
||||
res http.SameSite
|
||||
}{
|
||||
{"", http.SameSiteDefaultMode},
|
||||
{"default", http.SameSiteDefaultMode},
|
||||
{"blah", http.SameSiteDefaultMode},
|
||||
{"none", http.SameSiteNoneMode},
|
||||
{"lax", http.SameSiteLaxMode},
|
||||
{"strict", http.SameSiteStrictMode},
|
||||
}
|
||||
|
||||
cmd := ServerCommand{}
|
||||
for i, tt := range tbl {
|
||||
tt := tt
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
assert.Equal(t, tt.res, cmd.parseSameSite(tt.inp))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func chooseRandomUnusedPort() (port int) {
|
||||
for i := 0; i < 10; i++ {
|
||||
port = 40000 + int(rand.Int31n(10000))
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ require (
|
||||
github.com/go-chi/chi v4.1.1+incompatible
|
||||
github.com/go-chi/cors v1.1.1
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/go-pkgz/auth v1.13.1
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881
|
||||
github.com/go-pkgz/jrpc v0.2.0
|
||||
github.com/go-pkgz/lcw v0.8.1
|
||||
github.com/go-pkgz/lgr v0.10.4
|
||||
|
||||
@@ -60,6 +60,8 @@ github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/go-pkgz/auth v1.13.1 h1:+N9f2fSbvczMzOEaiGSxz1018JtvIaVM017GbmF7dF4=
|
||||
github.com/go-pkgz/auth v1.13.1/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881 h1:BlRK66nY8N7xkV51kQ0XEQD3No4Txk6QGedza5uaaGI=
|
||||
github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881/go.mod h1:1GVd61pXZcuJ0ZnOUdCTY08V8SreO7MJtsvEd5/WEWA=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc=
|
||||
github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ=
|
||||
github.com/go-pkgz/jrpc v0.2.0 h1:CLy/eZyekjraVrxZV18N2R1mYLMJ/nWrgdfyIOGPY/E=
|
||||
|
||||
+2
-1
@@ -53,7 +53,8 @@ type Opts struct {
|
||||
XSRFHeaderKey string // default "X-XSRF-TOKEN"
|
||||
JWTQuery string // default "token"
|
||||
|
||||
SendJWTHeader bool // if enabled send JWT as a header instead of cookie
|
||||
SendJWTHeader bool // if enabled send JWT as a header instead of cookie
|
||||
SameSiteCookie http.SameSite // limit cross-origin requests with SameSite cookie attribute
|
||||
|
||||
Issuer string // optional value for iss claim, usually the application name, default "go-pkgz/auth"
|
||||
|
||||
|
||||
+9
-8
@@ -63,10 +63,11 @@ type Opts struct {
|
||||
XSRFCookieName string
|
||||
XSRFHeaderKey string
|
||||
JWTQuery string
|
||||
AudienceReader Audience // allowed aud values
|
||||
Issuer string // optional value for iss claim, usually application name
|
||||
AudSecrets bool // uses different secret for differed auds. important: adds pre-parsing of unverified token
|
||||
SendJWTHeader bool // if enabled send JWT as a header instead of cookie
|
||||
AudienceReader Audience // allowed aud values
|
||||
Issuer string // optional value for iss claim, usually application name
|
||||
AudSecrets bool // uses different secret for differed auds. important: adds pre-parsing of unverified token
|
||||
SendJWTHeader bool // if enabled send JWT as a header instead of cookie
|
||||
SameSite http.SameSite // define a cookie attribute making it impossible for the browser to send this cookie cross-site
|
||||
}
|
||||
|
||||
// NewService makes JWT service
|
||||
@@ -238,11 +239,11 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
|
||||
}
|
||||
|
||||
jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: tokenString, HttpOnly: true, Path: "/",
|
||||
MaxAge: cookieExpiration, Secure: j.SecureCookies}
|
||||
MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
|
||||
http.SetCookie(w, &jwtCookie)
|
||||
|
||||
xsrfCookie := http.Cookie{Name: j.XSRFCookieName, Value: claims.Id, HttpOnly: false, Path: "/",
|
||||
MaxAge: cookieExpiration, Secure: j.SecureCookies}
|
||||
MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
|
||||
http.SetCookie(w, &xsrfCookie)
|
||||
|
||||
return claims, nil
|
||||
@@ -311,11 +312,11 @@ func (j *Service) IsExpired(claims Claims) bool {
|
||||
// Reset token's cookies
|
||||
func (j *Service) Reset(w http.ResponseWriter) {
|
||||
jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: "", HttpOnly: false, Path: "/",
|
||||
MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies}
|
||||
MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies, SameSite: j.SameSite}
|
||||
http.SetCookie(w, &jwtCookie)
|
||||
|
||||
xsrfCookie := http.Cookie{Name: j.XSRFCookieName, Value: "", HttpOnly: false, Path: "/",
|
||||
MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies}
|
||||
MaxAge: -1, Expires: time.Unix(0, 0), Secure: j.SecureCookies, SameSite: j.SameSite}
|
||||
http.SetCookie(w, &xsrfCookie)
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -90,7 +90,7 @@ github.com/go-chi/cors
|
||||
# github.com/go-chi/render v1.0.1
|
||||
## explicit
|
||||
github.com/go-chi/render
|
||||
# github.com/go-pkgz/auth v1.13.1
|
||||
# github.com/go-pkgz/auth v1.13.2-0.20210114092942-d2b22c3f3881
|
||||
## explicit
|
||||
github.com/go-pkgz/auth
|
||||
github.com/go-pkgz/auth/avatar
|
||||
|
||||
Reference in New Issue
Block a user