From dcd40c42e261caaff5208b9f7c0c8cc1716ac731 Mon Sep 17 00:00:00 2001 From: Umputun Date: Thu, 3 Jan 2019 15:58:43 -0600 Subject: [PATCH] vednor with auth 0.2.1 to minimize multiple token refreshes --- backend/Gopkg.lock | 6 ++-- .../vendor/github.com/go-pkgz/auth/auth.go | 10 ++++--- .../go-pkgz/auth/middleware/auth.go | 28 +++++++++++++++---- .../go-pkgz/auth/provider/dev_provider.go | 6 ++++ .../go-pkgz/auth/provider/direct.go | 4 +-- .../github.com/go-pkgz/auth/token/jwt.go | 3 ++ 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/backend/Gopkg.lock b/backend/Gopkg.lock index a58fd36d..06f1dacf 100644 --- a/backend/Gopkg.lock +++ b/backend/Gopkg.lock @@ -112,7 +112,7 @@ version = "v1.0.0" [[projects]] - digest = "1:a4ff2b649472abf046975396ac916b04527fde8d897857c2feea76498aeb762f" + digest = "1:6a297f738eb2aaca7c18129040ce7ac2af2db13c49fc948c13be4c692b2f5e19" name = "github.com/go-pkgz/auth" packages = [ ".", @@ -123,8 +123,8 @@ "token", ] pruneopts = "UT" - revision = "855a238343c3bcea84b352fdeb4393576f9eb217" - version = "v0.2.0" + revision = "3d27762393e5d62d1bd0553d5978226bac2acff7" + version = "v0.2.1" [[projects]] digest = "1:1212e114344a5cdcc834ea69e19d456eef230f9784659080fee67e02ba2cb574" diff --git a/backend/vendor/github.com/go-pkgz/auth/auth.go b/backend/vendor/github.com/go-pkgz/auth/auth.go index 47954a8b..5d5d8f26 100644 --- a/backend/vendor/github.com/go-pkgz/auth/auth.go +++ b/backend/vendor/github.com/go-pkgz/auth/auth.go @@ -53,8 +53,9 @@ type Opts struct { AvatarResizeLimit int // resize avatar's limit in pixels AvatarRoutePath string // avatar routing prefix, i.e. "/api/v1/avatar", default `/avatar` - AdminPasswd string // if presented, allows basic auth with user admin and given password - Logger logger.L // logger interface, default is no logging at all + AdminPasswd string // if presented, allows basic auth with user admin and given password + RefreshFactor int // estimated number of request client sends in parallel during token refresh. + Logger logger.L // logger interface, default is no logging at all } // NewService initializes everything @@ -64,8 +65,9 @@ func NewService(opts Opts) (res *Service) { opts: opts, logger: opts.Logger, authMiddleware: middleware.Authenticator{ - Validator: opts.Validator, - AdminPasswd: opts.AdminPasswd, + Validator: opts.Validator, + AdminPasswd: opts.AdminPasswd, + RefreshFactor: opts.RefreshFactor, }, issuer: opts.Issuer, } diff --git a/backend/vendor/github.com/go-pkgz/auth/middleware/auth.go b/backend/vendor/github.com/go-pkgz/auth/middleware/auth.go index f09432c1..7f557b20 100644 --- a/backend/vendor/github.com/go-pkgz/auth/middleware/auth.go +++ b/backend/vendor/github.com/go-pkgz/auth/middleware/auth.go @@ -2,6 +2,7 @@ package middleware import ( + "math/rand" "net/http" "github.com/pkg/errors" @@ -14,10 +15,11 @@ import ( // Authenticator is top level auth object providing middlewares type Authenticator struct { logger.L - JWTService TokenService - Providers []provider.Service - Validator token.Validator - AdminPasswd string + JWTService TokenService + Providers []provider.Service + Validator token.Validator + AdminPasswd string + RefreshFactor int } // TokenService defines interface accessing tokens @@ -96,7 +98,7 @@ func (a *Authenticator) auth(reqAuth bool) func(http.Handler) http.Handler { return } - if a.JWTService.IsExpired(claims) { + if a.shouldRefresh(claims) { if claims, err = a.refreshExpiredToken(w, claims); err != nil { a.JWTService.Reset(w) onError(h, w, r, errors.Wrap(err, "can't refresh token")) @@ -117,7 +119,6 @@ func (a *Authenticator) auth(reqAuth bool) func(http.Handler) http.Handler { // refreshExpiredToken makes a new token with passed claims func (a *Authenticator) refreshExpiredToken(w http.ResponseWriter, claims token.Claims) (token.Claims, error) { - claims.ExpiresAt = 0 // this will cause now+duration for refreshed token if err := a.JWTService.Set(w, claims); err != nil { return token.Claims{}, err @@ -125,6 +126,21 @@ func (a *Authenticator) refreshExpiredToken(w http.ResponseWriter, claims token. return claims, nil } +// shouldRefresh checks if token expired with an optional random rejection of refresh. +// the goal is to prevent multiple refresh request executed at the same time by allowing only some of them +func (a *Authenticator) shouldRefresh(claims token.Claims) bool { + if !a.JWTService.IsExpired(claims) { + return false + } + + // disable randomizing with 0 factor + if a.RefreshFactor == 0 { + return true + } + + return rand.Int31n(int32(a.RefreshFactor)) == 0 // randomize selection +} + // AdminOnly middleware allows access for admins only // this handler internally wrapped with auth(true) to avoid situation if AdminOnly defined without prior Auth func (a *Authenticator) AdminOnly(next http.Handler) http.Handler { diff --git a/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go b/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go index 8d93fc96..94dc2525 100644 --- a/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go +++ b/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go @@ -130,6 +130,12 @@ func (d *DevAuthServer) Run(ctx context.Context) { } d.lock.Unlock() + go func() { + <-ctx.Done() + d.Logf("[DEBUG] cancellation via context, %v", ctx.Err()) + d.Shutdown() + }() + err = d.httpServer.ListenAndServe() d.Logf("[WARN] dev oauth2 server terminated, %s", err) } diff --git a/backend/vendor/github.com/go-pkgz/auth/provider/direct.go b/backend/vendor/github.com/go-pkgz/auth/provider/direct.go index 1bad3879..f721c10d 100644 --- a/backend/vendor/github.com/go-pkgz/auth/provider/direct.go +++ b/backend/vendor/github.com/go-pkgz/auth/provider/direct.go @@ -4,7 +4,7 @@ import ( "errors" "net/http" - jwt "github.com/dgrijalva/jwt-go" + "github.com/dgrijalva/jwt-go" "github.com/go-pkgz/rest" "github.com/go-pkgz/auth/logger" @@ -72,7 +72,7 @@ func (p DirectHandler) LoginHandler(w http.ResponseWriter, r *http.Request) { rest.RenderJSON(w, r, claims.User) } -// AuthHandler doesn't do anyting for direct login as it has no callbacks +// AuthHandler doesn't do anything for direct login as it has no callbacks func (p DirectHandler) AuthHandler(w http.ResponseWriter, r *http.Request) {} // LogoutHandler - GET /logout diff --git a/backend/vendor/github.com/go-pkgz/auth/token/jwt.go b/backend/vendor/github.com/go-pkgz/auth/token/jwt.go index 13ae2b4d..15c91ccf 100644 --- a/backend/vendor/github.com/go-pkgz/auth/token/jwt.go +++ b/backend/vendor/github.com/go-pkgz/auth/token/jwt.go @@ -122,6 +122,9 @@ func (j *Service) Parse(tokenString string) (Claims, error) { if err != nil { return "", errors.Wrap(err, "can't pre-parse token") } + if _, ok := preToken.Method.(*jwt.SigningMethodHMAC); !ok { + return "", errors.Errorf("unexpected signing method: %v", preToken.Header["alg"]) + } preClaims, ok := preToken.Claims.(*Claims) if !ok { return "", errors.New("invalid token")