Skip to content

Commit

Permalink
Add jwt.WithCookie
Browse files Browse the repository at this point in the history
  • Loading branch information
lestrrat committed Mar 10, 2024
1 parent 12ead02 commit f498bb0
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ v2.0.22 UNRELEASED
* [jwt] Added jwt.ParseCookie() function
* [jwt] jwt.ParseRequest() can now accept a new option, jwt.WithCookieKey() to
specify a cookie name to extract the token from.
* [jwt] jwt.ParseRequest() and jwt.ParseCookie can accept the jwt.WithCookie() option,
which will, upon successful token parsing, make the functions assign the *http.Cookie
used to parse the token. This allows users to further inspect the cookie where the
token came from, should the need arise.
* [jwt] jwt.ParseRequest() no longer automatically looks for "Authorization" header when
only jwt.WithFormKey() is used. This behavior is the same for jwt.WithCookieKey() and
any similar options that may be implemented in the future.
Expand Down
14 changes: 14 additions & 0 deletions examples/jwt_parse_request_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ func ExampleJWT_ParseRequest_Authorization() {
req.Header.Set(`Authorization`, fmt.Sprintf(`Bearer %s`, exampleJWTSignedECDSA))
req.Header.Set(`X-JWT-Token`, exampleJWTSignedRSA)

req.AddCookie(&http.Cookie{Name: "accessToken", Value: exampleJWTSignedHMAC})

var dst *http.Cookie

testcases := []struct {
options []jwt.ParseOption
}{
Expand All @@ -37,6 +41,11 @@ func ExampleJWT_ParseRequest_Authorization() {
{
options: []jwt.ParseOption{jwt.WithFormKey(`access_token`)},
},
// Looks under "accessToken" cookie, and assigns the http.Cookie object
// where the token came from to the variable `dst`
{
options: []jwt.ParseOption{jwt.WithCookieKey(`accessToken`), jwt.WithCookie(&dst)},
},
}

for _, tc := range testcases {
Expand All @@ -55,5 +64,10 @@ func ExampleJWT_ParseRequest_Authorization() {
}
_ = tok
}

if dst == nil {
fmt.Printf("failed to assign cookie to dst\n")
return
}
// OUTPUT:
}
19 changes: 18 additions & 1 deletion jwt/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,28 @@ import (
// ParseCookie parses a JWT stored in a http.Cookie with the given name.
// If the specified cookie is not found, http.ErrNoCookie is returned.
func ParseCookie(req *http.Request, name string, options ...ParseOption) (Token, error) {
var dst **http.Cookie
//nolint:forcetypeassert
for _, option := range options {
switch option.Ident() {
case identCookie{}:
dst = option.Value().(**http.Cookie)
}
}

cookie, err := req.Cookie(name)
if err != nil {
return nil, err
}
return ParseString(cookie.Value, options...)
tok, err := ParseString(cookie.Value, options...)
if err != nil {
return nil, fmt.Errorf(`failed to parse token stored in cookie: %w`, err)
}

if dst != nil {
*dst = cookie
}
return tok, nil
}

// ParseHeader parses a JWT stored in a http.Header.
Expand Down
11 changes: 11 additions & 0 deletions jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,17 @@ func TestParseRequest(t *testing.T) {
}
})
}

// One extra test. Make sure we can extract the cookie object that we used
// when parsing from cookies
t.Run("jwt.WithCookie", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, u, nil)
req.AddCookie(&http.Cookie{Name: "cookie", Value: string(signed)})
var dst *http.Cookie
_, err := jwt.ParseRequest(req, jwt.WithCookieKey("cookie"), jwt.WithCookie(&dst), jwt.WithKey(jwa.ES256, pubkey))
require.NoError(t, err, `jwt.ParseRequest should succeed`)
require.NotNil(t, dst, `cookie should be extracted`)
})
}

func TestGHIssue368(t *testing.T) {
Expand Down
10 changes: 10 additions & 0 deletions jwt/options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ options:
comment: |
WithHeaderKey is used to specify header keys to search for tokens.
While the type system allows this option to be passed to `jwt.Parse()` directly,
doing so will have no effect. Only use it for HTTP request parsing functions
- ident: Cookie
interface: ParseOption
argument_type: '**http.Cookie'
comment: |
WithCookie is used to specify a variable to store the cookie used when `jwt.ParseCookie()`
is called. This allows you to inspect the cookie for additional information after a successful
parsing of the JWT token stored in the cookie.
While the type system allows this option to be passed to `jwt.Parse()` directly,
doing so will have no effect. Only use it for HTTP request parsing functions
- ident: CookieKey
Expand Down
16 changes: 16 additions & 0 deletions jwt/options_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions jwt/options_gen_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f498bb0

Please sign in to comment.