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:
Umputun
2021-01-14 13:06:29 -06:00
committed by GitHub
parent 7f7973a78f
commit aff79a6fa9
8 changed files with 64 additions and 16 deletions
+2
View File
@@ -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 |
+24 -5
View File
@@ -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
+23
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+1 -1
View File
@@ -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