Skip to content

Commit

Permalink
Add cookie subject extractor (#536)
Browse files Browse the repository at this point in the history
* Add cookie subject extractor

* Fix typo

---------

Co-authored-by: Zhuojie Zhou <[email protected]>
  • Loading branch information
nothing0012 and zhouzhuojie authored Jun 30, 2023
1 parent 2eee864 commit 9ac5043
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pkg/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:""`
Expand Down
15 changes: 15 additions & 0 deletions pkg/handler/subject.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 claims, ok := token.Claims.(jwt.MapClaims); ok {
return util.SafeString(claims[config.Config.CookieAuthUserFieldJWTClaim])
}
}
return c.Value
}

return ""
Expand Down
28 changes: 28 additions & 0 deletions pkg/handler/subject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,31 @@ func TestGetSubjectFromOauthProxy(t *testing.T) {
r.Header.Set(config.Config.HeaderAuthUserField, "[email protected]")
assert.Equal(t, getSubjectFromRequest(r.WithContext(ctx)), "[email protected]")
}

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)), "[email protected]")
})

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)), "")
})
}

0 comments on commit 9ac5043

Please sign in to comment.