Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT limiter #221

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open

JWT limiter #221

wants to merge 20 commits into from

Conversation

jigsaw373
Copy link

Implement JWT token instead of using IP

network.go Outdated Show resolved Hide resolved
Store Store
Rate Rate
Options Options
ErrValidation error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this ErrValidation, if you should return an error, return it directly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was the simplest way to stop the handler by calling OnError function

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work because either you have one request at a time, and even then it doesn't work because you never reset the ErrValidation for the next request.

Or, you have multiple request at the same time, and then you have a huge data race / race condition.

This is clear now that the API for KeyGetter is not enough but, if we change it, it would break backward compatibility. My bad for this!

You'll have to wait for the v4 engine to add support for JWT.

network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
@jigsaw373
Copy link
Author

jigsaw373 commented Mar 31, 2023

Excuse me for delay, it was Nowruz 😄
Please check it again @novln

@jigsaw373 jigsaw373 requested a review from novln March 31, 2023 22:23
@novln
Copy link
Contributor

novln commented Apr 6, 2023

I haven't forgotten. I'll probably review this the following weekend.

network.go Outdated Show resolved Hide resolved
@@ -33,9 +33,32 @@ func NewMiddleware(limiter *limiter.Limiter, options ...Option) *Middleware {
return middleware
}

// NewJWTMiddleware return a new instance of a JWT token middleware.
func NewJWTMiddleware(limiter *limiter.Limiter, options ...Option) *Middleware {
Copy link
Contributor

@novln novln Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this function because it doesn't provide any value.

You could define KeyGetter with your JWTKeyGetter by declaring an option.
You can find inspiration on how to do this there:

// WithKeyGetter will configure the Middleware to use the given KeyGetter.
func WithKeyGetter(handler KeyGetter) Option {
return option(func(middleware *Middleware) {
middleware.KeyGetter = handler
})
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then, you could declare your middleware with something like this:

stdlib.NewMiddleware(limiter, stdlib.WithJWTKeyGetter(JWTKeyGetter(limiter)))

Or, simply do a function that does something like this if you want syntactic sugar for this.

@@ -79,6 +92,15 @@ func GetIP(r *http.Request, options ...Options) net.IP {
return net.ParseIP(host)
}

// GetJWTSub returns sub from request JWT.
func GetJWTSub(r *http.Request, secret string) (string, error) {
if token, valid := getAuthorizationToken(r); valid {
Copy link
Contributor

@novln novln Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reverse your condition handling to have the edge/corner case on the right.

Suggested change
if token, valid := getAuthorizationToken(r); valid {
token, valid := getAuthorizationToken(r)
if !valid {
return "", ErrInvalidJWT

@@ -26,6 +26,8 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use version v4.5.0

network.go Outdated Show resolved Hide resolved
network.go Outdated Show resolved Hide resolved
}

// Verify the token format (Bearer <token>)
lowerToken := strings.ToLower(headerToken[:len(bearer)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your code could panic there if len(headerToken) < len(bearer)

return "", err
}
if !token.Valid {
return "", ErrInvalidJWT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you wrap your errors there in order to create a stacktrace ?

return "", errors.Wrap(ErrInvalidJWT, "failed to extract sub from jwt")

@@ -24,6 +24,7 @@ type Options struct {
// proxy is not configured properly to forward a trustworthy client IP.
// Please read the section "Limiter behind a reverse proxy" in the README for further information.
ClientIPHeader string
JWTSecret string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment to explain what this option does, how/where it's used.

// Handler handles a HTTP request.
func (middleware *Middleware) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := middleware.Limiter.ErrValidation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please read my comment about data race / race condition.
It's a blocker.

jigsaw373 and others added 3 commits April 17, 2023 13:38
Co-authored-by: Thomas LE ROUX  <thomas@leroux.io>
Co-authored-by: Thomas LE ROUX  <thomas@leroux.io>
Co-authored-by: Thomas LE ROUX  <thomas@leroux.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants