Skip to content

Commit

Permalink
Merge pull request #210 from oalexander6/master
Browse files Browse the repository at this point in the history
Implement IgnoreXSRFMethods for v2
  • Loading branch information
umputun authored Jul 29, 2024
2 parents db26833 + 4fb8b53 commit b6f1a79
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 20 deletions.
17 changes: 9 additions & 8 deletions v2/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ type Opts struct {
DisableIAT bool // disable IssuedAt claim

// optional (custom) names for cookies and headers
JWTCookieName string // default "JWT"
JWTCookieDomain string // default empty
JWTHeaderKey string // default "X-JWT"
XSRFCookieName string // default "XSRF-TOKEN"
XSRFHeaderKey string // default "X-XSRF-TOKEN"
JWTQuery string // default "token"
SendJWTHeader bool // if enabled send JWT as a header instead of cookie
SameSiteCookie http.SameSite // limit cross-origin requests with SameSite cookie attribute
JWTCookieName string // default "JWT"
JWTCookieDomain string // default empty
JWTHeaderKey string // default "X-JWT"
XSRFCookieName string // default "XSRF-TOKEN"
XSRFHeaderKey string // default "X-XSRF-TOKEN"
XSRFIgnoreMethods []string // disable XSRF protection for the specified request methods (ex. []string{"GET", "POST")}, default empty
JWTQuery string // default "token"
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"

Expand Down
34 changes: 22 additions & 12 deletions v2/token/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -49,6 +50,10 @@ const (
defaultTokenQuery = "token"
)

var (
defaultXSRFIgnoreMethods = []string{}
)

// Opts holds constructor params
type Opts struct {
SecretReader Secret
Expand All @@ -59,17 +64,18 @@ type Opts struct {
DisableXSRF bool
DisableIAT bool // disable IssuedAt claim
// optional (custom) names for cookies and headers
JWTCookieName string
JWTCookieDomain string
JWTHeaderKey string
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
SameSite http.SameSite // define a cookie attribute making it impossible for the browser to send this cookie cross-site
JWTCookieName string
JWTCookieDomain string
JWTHeaderKey string
XSRFCookieName string
XSRFHeaderKey string
XSRFIgnoreMethods []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
SameSite http.SameSite // define a cookie attribute making it impossible for the browser to send this cookie cross-site
}

// NewService makes JWT service
Expand All @@ -90,6 +96,10 @@ func NewService(opts Opts) *Service {
setDefault(&res.Issuer, defaultIssuer)
setDefault(&res.JWTCookieDomain, defaultJWTCookieDomain)

if opts.XSRFIgnoreMethods == nil {
opts.XSRFIgnoreMethods = defaultXSRFIgnoreMethods
}

if opts.TokenDuration == 0 {
res.TokenDuration = defaultTokenDuration
}
Expand Down Expand Up @@ -293,7 +303,7 @@ func (j *Service) Get(r *http.Request) (Claims, string, error) {
return Claims{}, "", fmt.Errorf("token expired")
}

if j.DisableXSRF {
if j.DisableXSRF || slices.Contains(j.XSRFIgnoreMethods, r.Method) {
return claims, tokenString, nil
}

Expand Down
47 changes: 47 additions & 0 deletions v2/token/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,53 @@ func TestJWT_SetAndGetWithXsrfMismatch(t *testing.T) {
assert.Equal(t, claims, c)
}

func TestJWT_GetWithXsrfMismatchOnIgnoredMethod(t *testing.T) {
j := NewService(Opts{SecretReader: SecretFunc(mockKeyStore), SecureCookies: false,
TokenDuration: time.Hour, CookieDuration: days31,
JWTCookieName: jwtCustomCookieName, JWTHeaderKey: jwtCustomHeaderKey,
XSRFCookieName: xsrfCustomCookieName, XSRFHeaderKey: xsrfCustomHeaderKey,
ClaimsUpd: ClaimsUpdFunc(func(claims Claims) Claims {
claims.User.SetStrAttr("stra", "stra-val")
claims.User.SetBoolAttr("boola", true)
return claims
}),
Issuer: "remark42",
DisableIAT: true,
})

claims := testClaims
claims.SessionOnly = true
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/valid" {
_, e := j.Set(w, claims)
require.NoError(t, e)
w.WriteHeader(200)
}
}))
defer ts.Close()

resp, err := http.Get(ts.URL + "/valid")
require.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)

j.XSRFIgnoreMethods = []string{"GET"}
req := httptest.NewRequest("GET", "/valid", nil)
req.AddCookie(resp.Cookies()[0])
req.Header.Add(xsrfCustomHeaderKey, "random id wrong")
_, _, err = j.Get(req)
require.NoError(t, err, "xsrf mismatch, but ignored")

j.DisableXSRF = true
j.XSRFIgnoreMethods = []string{}
req = httptest.NewRequest("GET", "/valid", nil)
req.AddCookie(resp.Cookies()[0])
req.Header.Add(xsrfCustomHeaderKey, "random id wrong")
c, _, err := j.Get(req)
require.NoError(t, err, "xsrf mismatch, but ignored")
claims.User.Audience = c.Audience // set aud to user because we don't do the normal Get call
assert.Equal(t, claims, c)
}

func TestJWT_SetAndGetWithCookiesExpired(t *testing.T) {
j := NewService(Opts{SecretReader: SecretFunc(mockKeyStore), SecureCookies: false,
TokenDuration: time.Hour, CookieDuration: days31,
Expand Down

0 comments on commit b6f1a79

Please sign in to comment.