Rate limit login keyed by username

This commit is contained in:
Daniel Ponte 2025-01-06 08:22:51 -05:00
parent a5fc6825b1
commit 108c6ec62f
3 changed files with 27 additions and 6 deletions

View file

@ -3,11 +3,13 @@ package auth
import (
"errors"
"net/http"
"time"
_ "embed"
"dynatron.me/x/stillbox/pkg/config"
"github.com/go-chi/chi/v5"
"github.com/go-chi/httprate"
"github.com/go-chi/jwtauth/v5"
)
@ -30,6 +32,7 @@ type Authenticator interface {
}
type Auth struct {
rl *httprate.RateLimiter
jwt *jwtauth.JWTAuth
cfg config.Auth
}
@ -37,6 +40,7 @@ type Auth struct {
// NewAuthenticator creates a new Authenticator with the provided config.
func NewAuthenticator(cfg config.Auth) *Auth {
a := &Auth{
rl: httprate.NewRateLimiter(5, time.Minute),
cfg: cfg,
}
a.initJWT()

View file

@ -225,6 +225,10 @@ func (a *Auth) routeAuth(w http.ResponseWriter, r *http.Request) {
return
}
if a.rl.RespondOnLimit(w, r, creds.Username) {
return
}
if creds.Username == "" || creds.Password == "" {
http.Error(w, "blank credentials", http.StatusBadRequest)
return

View file

@ -10,10 +10,7 @@ import (
"dynatron.me/x/stillbox/client"
"dynatron.me/x/stillbox/internal/version"
"dynatron.me/x/stillbox/pkg/config"
"dynatron.me/x/stillbox/pkg/database"
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/httprate"
"github.com/go-chi/render"
)
@ -29,8 +26,7 @@ func (s *Server) setupRoutes() {
}
r := s.r
r.Use(middleware.WithValue(database.DBCtxKey, s.db))
r.Use(middleware.WithValue(tgstore.StoreCtxKey, s.tgs))
r.Use(s.WithCtxStores())
s.installPprof()
@ -47,10 +43,15 @@ func (s *Server) setupRoutes() {
s.rateLimit(r)
r.Use(render.SetContentType(render.ContentTypeJSON))
// public routes
s.auth.PublicRoutes(r)
s.sources.PublicRoutes(r)
})
r.Group(func(r chi.Router) {
// auth routes get rate-limited heavily, but not using middleware
r.Use(render.SetContentType(render.ContentTypeJSON))
s.auth.PublicRoutes(r)
})
r.Group(func(r chi.Router) {
s.rateLimit(r)
r.Use(s.auth.VerifyMiddleware())
@ -61,11 +62,23 @@ func (s *Server) setupRoutes() {
})
}
// WithCtxStores is a middleware that installs all stores in the request context.
func (s *Server) WithCtxStores() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(s.addStoresTo(r.Context()))
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}
func (s *Server) rateLimit(r chi.Router) {
if s.conf.RateLimit.Verify() {
r.Use(rateLimiter(&s.conf.RateLimit))
}
}
func rateLimiter(cfg *config.RateLimit) func(http.Handler) http.Handler {
return httprate.LimitByRealIP(cfg.Requests, cfg.Over)
}