vednor with auth 0.2.1 to minimize multiple token refreshes
This commit is contained in:
Generated
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user