vednor with auth 0.2.1 to minimize multiple token refreshes

This commit is contained in:
Umputun
2019-01-03 15:58:43 -06:00
parent 72a264b771
commit dcd40c42e2
6 changed files with 42 additions and 15 deletions
+3 -3
View File
@@ -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"
+6 -4
View File
@@ -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,
}
+22 -6
View File
@@ -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 {
+6
View File
@@ -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)
}
+2 -2
View File
@@ -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
+3
View File
@@ -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")