From b365c8eddd7e338211f0d037bd5791839f5c1e6f Mon Sep 17 00:00:00 2001 From: Zhuojie Zhou Date: Sun, 25 Jun 2023 14:57:52 -0700 Subject: [PATCH 1/2] Add cookie subject extractor --- pkg/config/env.go | 6 ++++++ pkg/handler/subject.go | 15 +++++++++++++++ pkg/handler/subject_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/pkg/config/env.go b/pkg/config/env.go index ed4bde56..f26df2c2 100644 --- a/pkg/config/env.go +++ b/pkg/config/env.go @@ -204,6 +204,12 @@ var Config = struct { HeaderAuthEnabled bool `env:"FLAGR_HEADER_AUTH_ENABLED" envDefault:"false"` HeaderAuthUserField string `env:"FLAGR_HEADER_AUTH_USER_FIELD" envDefault:"X-Email"` + // Identify users through cookies + // E.g. via cloudflare zero trust, we derive the user email from the JWT token stored in the cookie of CF_Authorization + CookieAuthEnabled bool `env:"FLAGR_COOKIE_AUTH_ENABLED" envDefault:"false"` + CookieAuthUserField string `env:"FLAGR_COOKIE_AUTH_USER_FIELD" envDefault:"CF_Authorization"` + CookieAuthUserFieldJWTClaim string `env:"FLAGR_COOKIE_AUTH_USER_FIELD_JWT_CLAIM" envDefault:"email"` + // Authenticate with basic auth BasicAuthEnabled bool `env:"FLAGR_BASIC_AUTH_ENABLED" envDefault:"false"` BasicAuthUsername string `env:"FLAGR_BASIC_AUTH_USERNAME" envDefault:""` diff --git a/pkg/handler/subject.go b/pkg/handler/subject.go index a35ea44f..fb7658f4 100644 --- a/pkg/handler/subject.go +++ b/pkg/handler/subject.go @@ -26,6 +26,21 @@ func getSubjectFromRequest(r *http.Request) string { } else if config.Config.HeaderAuthEnabled { return r.Header.Get(config.Config.HeaderAuthUserField) + } else if config.Config.CookieAuthEnabled { + c, err := r.Cookie(config.Config.CookieAuthUserField) + if err != nil { + return "" + } + if config.Config.CookieAuthUserFieldJWTClaim != "" { + // for this case, we choose to skip the error check because just like HeaderAuthUserField + // in the future, we can extend this function to support cookie jwt token validation + // this assumes that the cookie we get already passed the auth middleware + token, _ := jwt.Parse(c.Value, func(token *jwt.Token) (interface{}, error) { return "", nil }) + if clams, ok := token.Claims.(jwt.MapClaims); ok { + return util.SafeString(clams[config.Config.CookieAuthUserFieldJWTClaim]) + } + } + return c.Value } return "" diff --git a/pkg/handler/subject_test.go b/pkg/handler/subject_test.go index d3186be8..6ac2bfec 100644 --- a/pkg/handler/subject_test.go +++ b/pkg/handler/subject_test.go @@ -48,3 +48,31 @@ func TestGetSubjectFromOauthProxy(t *testing.T) { r.Header.Set(config.Config.HeaderAuthUserField, "foo@example.com") assert.Equal(t, getSubjectFromRequest(r.WithContext(ctx)), "foo@example.com") } + +func TestGetSubjectFromCookie(t *testing.T) { + var ctx = context.Background() + defer func() { config.Config.CookieAuthEnabled = false }() + config.Config.CookieAuthEnabled = true + + t.Run("test HS256 happy codepath", func(t *testing.T) { + r, _ := http.NewRequest("GET", "", nil) + assert.Equal(t, getSubjectFromRequest(r), "") + + r.AddCookie(&http.Cookie{ + Name: config.Config.CookieAuthUserField, + Value: "eyJhbGciOiJIUzI1NiIsImtpZCI6IjEyMzQ1In0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJhYmNAZXhhbXBsZS5jb20iLCJpYXQiOjE1MTYyMzkwMjJ9.tzRXenFic8Eqg2awzO0eiX6Rozy_mmsJVzLJfUUfREI", + }) + assert.Equal(t, getSubjectFromRequest(r.WithContext(ctx)), "abc@example.com") + }) + + t.Run("test HS256 empty claim", func(t *testing.T) { + r, _ := http.NewRequest("GET", "", nil) + assert.Equal(t, getSubjectFromRequest(r), "") + + r.AddCookie(&http.Cookie{ + Name: config.Config.CookieAuthUserField, + Value: "eyJhbGciOiJIUzI1NiIsImtpZCI6IjEyMzQ1In0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.C_YsEkcHa7aSVQILzJAayFgJk-sj1cmNWIWUm7m7vy4", + }) + assert.Equal(t, getSubjectFromRequest(r.WithContext(ctx)), "") + }) +} From c43c42d4bec1e26447d1703f655a60f637af2e83 Mon Sep 17 00:00:00 2001 From: Zhuojie Zhou Date: Mon, 26 Jun 2023 11:01:25 -0700 Subject: [PATCH 2/2] Fix typo --- pkg/handler/subject.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/handler/subject.go b/pkg/handler/subject.go index fb7658f4..a89cce32 100644 --- a/pkg/handler/subject.go +++ b/pkg/handler/subject.go @@ -36,8 +36,8 @@ func getSubjectFromRequest(r *http.Request) string { // in the future, we can extend this function to support cookie jwt token validation // this assumes that the cookie we get already passed the auth middleware token, _ := jwt.Parse(c.Value, func(token *jwt.Token) (interface{}, error) { return "", nil }) - if clams, ok := token.Claims.(jwt.MapClaims); ok { - return util.SafeString(clams[config.Config.CookieAuthUserFieldJWTClaim]) + if claims, ok := token.Claims.(jwt.MapClaims); ok { + return util.SafeString(claims[config.Config.CookieAuthUserFieldJWTClaim]) } } return c.Value