auth 0.3.3 prevnets expired headers to pe passed in

This commit is contained in:
Umputun
2019-01-15 13:09:56 -06:00
parent e9585dc568
commit 78debf05d9
8 changed files with 81 additions and 55 deletions
+3 -3
View File
@@ -112,7 +112,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:1e397244efaf3322184d8fe59caa62dae159d925aa74a5efafa670964d860d67"
digest = "1:5048ee5f04267e166b81a65f6944575750ebc1fc896124d1cf3e57f5d710e49d"
name = "github.com/go-pkgz/auth"
packages = [
".",
@@ -122,8 +122,8 @@
"token",
]
pruneopts = "UT"
revision = "06e223a293c64ebcaaccda26edfeaf7fba1b8746"
version = "v0.3.2"
revision = "203b40f77cd2d1941d1a8fcdd8474c3bec3cc6b5"
version = "v0.3.3"
[[projects]]
digest = "1:1933dabfb0e07548ed9684fdef857cc6f2413bbbb74ba838a7c1f77407b8fd90"
+9 -2
View File
@@ -2,16 +2,23 @@ module github.com/go-pkgz/auth
require (
cloud.google.com/go v0.34.0 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/coreos/bbolt v1.3.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/go-pkgz/lgr v0.1.4
github.com/go-pkgz/lgr v0.2.2
github.com/go-pkgz/mongo v1.0.0
github.com/go-pkgz/rest v1.2.0
github.com/hashicorp/golang-lru v0.5.0
github.com/kr/pretty v0.1.0 // indirect
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022
github.com/pkg/errors v0.8.1
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.3.0
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 // indirect
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb // indirect
google.golang.org/appengine v1.4.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
+21 -21
View File
@@ -1,5 +1,7 @@
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/coreos/bbolt v1.3.0 h1:HIgH5xUWXT914HCI671AxuTTqjj64UOFr7pHn48LUTI=
github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -9,28 +11,21 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-pkgz/lgr v0.0.0-20190107224007-7d791fb529cb h1:HBzmL2t7mb8A14Vc1uMInWQlKAY5rq1i1M4svUIQTNQ=
github.com/go-pkgz/lgr v0.0.0-20190107224007-7d791fb529cb/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
github.com/go-pkgz/lgr v0.1.0 h1:JzSBxyNW9gli9PMVGI8IUhmFsQZaK2usJ62pmUGj0BY=
github.com/go-pkgz/lgr v0.1.0/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
github.com/go-pkgz/lgr v0.1.3 h1:jGPFbzfXWSt3r8qyjXnuKFNM6J10bs3YnD8wPSGdsww=
github.com/go-pkgz/lgr v0.1.3/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
github.com/go-pkgz/lgr v0.1.4 h1:mOV129LVgHLq65kYs1Abz0dSk3Sba6PuuJ/S+x2yDaw=
github.com/go-pkgz/lgr v0.1.4/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
github.com/go-pkgz/lgr v0.2.2 h1:HSOqMVoetAfvA40Gpy/X/HGyV0UUafIMPgp+SdZends=
github.com/go-pkgz/lgr v0.2.2/go.mod h1:hBM1NM/SoYdlrykgdgJWGrZ/TM/XaZIjRbJfx7NkMm8=
github.com/go-pkgz/mongo v1.0.0 h1:9jijAK7prCRMetiyTu3c1rv/2lMypzuf2DWcVpTlwzw=
github.com/go-pkgz/mongo v1.0.0/go.mod h1:R9si/F2aJsjz4MUxhzuppIHY8yLV3YCeuCpgcI50cu4=
github.com/go-pkgz/rest v1.1.3 h1:rMf+xJn8i1Ip9OKohusZsRxwntM0BwYu8OX8BuEwN80=
github.com/go-pkgz/rest v1.1.3/go.mod h1:DIxxm3vSt6e+IY+UQUOFsfB2YaHLmGoOfPLWN5pxQSA=
github.com/go-pkgz/rest v1.1.4 h1:/Lrg9kBWBjNah7nmCDHLszRAfVVBIy5ajf0vVgpHPi0=
github.com/go-pkgz/rest v1.1.4/go.mod h1:DIxxm3vSt6e+IY+UQUOFsfB2YaHLmGoOfPLWN5pxQSA=
github.com/go-pkgz/rest v1.1.5 h1:5br4mnscfLb27yxv5hJFLBVmAt09PrmIBP+meA3CfHc=
github.com/go-pkgz/rest v1.1.5/go.mod h1:DIxxm3vSt6e+IY+UQUOFsfB2YaHLmGoOfPLWN5pxQSA=
github.com/go-pkgz/rest v1.1.6 h1:Sqm7uW0X29iEmt4S/vJ82bslQXa6jMMNTaRvHzjTcjo=
github.com/go-pkgz/rest v1.1.6/go.mod h1:fFcrWsYgEp5Xx6HxRFzfV6zTiytCeYv1Jceg8TxorOs=
github.com/go-pkgz/rest v1.2.0 h1:75GVv25NmkV2l4dBr/io/ZApJ6zWQu5aZ4wFJA6QQCw=
github.com/go-pkgz/rest v1.2.0/go.mod h1:COazNj35u3RXAgQNBr6neR599tYP3URiOpsu9p0rOtk=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022 h1:Ys0rDzh8s4UMlGaDa1UTA0sfKgvF0hQZzTYX8ktjiDc=
github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022/go.mod h1:x4NsS+uc7ecH/Cbm9xKQ6XzmJM57rWTkjywjfB2yQ18=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
@@ -40,16 +35,21 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b h1:VHyIDlv3XkfCa5/a81uzaoDkHH4rr81Z62g+xlnO8uM=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 h1:1DW40AJQ7AP4nY6ORUGUdkpXyEC9W2GAXcOPaMZK0K8=
golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb h1:1w588/yEchbPNpa9sEvOcMZYbWHedwJjg4VOAdDHWHk=
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+37 -22
View File
@@ -2,10 +2,11 @@
package middleware
import (
"math/rand"
"net/http"
"sync"
"github.com/go-pkgz/lgr"
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
"github.com/go-pkgz/auth/provider"
@@ -20,12 +21,19 @@ type Authenticator struct {
Validator token.Validator
AdminPasswd string
RefreshFactor int
refresh struct {
cache *lru.Cache
once sync.Once
}
}
const refreshCacheSize = 1000
// TokenService defines interface accessing tokens
type TokenService interface {
Parse(tokenString string) (claims token.Claims, err error)
Set(w http.ResponseWriter, claims token.Claims) error
Set(w http.ResponseWriter, claims token.Claims) (token.Claims, error)
Get(r *http.Request) (claims token.Claims, token string, err error)
IsExpired(claims token.Claims) bool
Reset(w http.ResponseWriter)
@@ -83,7 +91,7 @@ func (a *Authenticator) auth(reqAuth bool) func(http.Handler) http.Handler {
}
if claims.User == nil {
onError(h, w, r, errors.New("failed auth, no user info presented in the claim"))
onError(h, w, r, errors.New("no user info presented in the claim"))
return
}
@@ -95,13 +103,12 @@ func (a *Authenticator) auth(reqAuth bool) func(http.Handler) http.Handler {
return
}
if a.shouldRefresh(claims) {
if claims, err = a.refreshExpiredToken(w, claims); err != nil {
if a.JWTService.IsExpired(claims) {
if claims, err = a.refreshExpiredToken(w, claims, tkn); err != nil {
a.JWTService.Reset(w)
onError(h, w, r, errors.Wrap(err, "can't refresh token"))
return
}
a.Logf("[DEBUG] token refreshed for %+v", claims.User)
}
r = token.SetUserInfo(r, *claims.User) // populate user info to request context
@@ -115,27 +122,35 @@ 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 {
func (a *Authenticator) refreshExpiredToken(w http.ResponseWriter, claims token.Claims, tkn string) (token.Claims, error) {
a.refresh.once.Do(func() {
var e error
if a.refresh.cache, e = lru.New(refreshCacheSize); e != nil {
a.Logf("[WARN] can't make refresh cache, %v", e)
a.refresh.cache = nil
}
})
if a.refresh.cache != nil {
if c, ok := a.refresh.cache.Get(tkn); ok {
// already in cache
return c.(token.Claims), nil
}
}
claims.ExpiresAt = 0 // this will cause now+duration for refreshed token
c, err := a.JWTService.Set(w, claims) // Set changes token
if err != nil {
return token.Claims{}, err
}
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
if a.refresh.cache != nil {
a.refresh.cache.Add(tkn, c)
}
// disable randomizing with 0 factor
if a.RefreshFactor == 0 {
return true
}
return rand.Int31n(int32(a.RefreshFactor)) == 0 // randomize selection
a.Logf("[DEBUG] token refreshed for %+v", claims.User)
return c, nil
}
// AdminOnly middleware allows access for admins only
+1 -1
View File
@@ -65,7 +65,7 @@ func (p DirectHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
SessionOnly: sessOnly,
}
if err = p.TokenService.Set(w, claims); err != nil {
if _, err = p.TokenService.Set(w, claims); err != nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to set token")
return
}
+2 -2
View File
@@ -105,7 +105,7 @@ func (p Oauth2Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
},
}
if err := p.JwtService.Set(w, claims); err != nil {
if _, err := p.JwtService.Set(w, claims); err != nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to set token")
return
}
@@ -192,7 +192,7 @@ func (p Oauth2Handler) AuthHandler(w http.ResponseWriter, r *http.Request) {
SessionOnly: oauthClaims.SessionOnly,
}
if err = p.JwtService.Set(w, claims); err != nil {
if _, err = p.JwtService.Set(w, claims); err != nil {
rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to set token")
return
}
+1 -1
View File
@@ -30,7 +30,7 @@ type AvatarSaver interface {
// TokenService defines interface accessing tokens
type TokenService interface {
Parse(tokenString string) (claims token.Claims, err error)
Set(w http.ResponseWriter, claims token.Claims) error
Set(w http.ResponseWriter, claims token.Claims) (token.Claims, error)
Get(r *http.Request) (claims token.Claims, token string, err error)
Reset(w http.ResponseWriter)
}
+7 -3
View File
@@ -175,7 +175,7 @@ func (j *Service) validate(claims *Claims) error {
// Set creates token cookie with xsrf cookie and put it to ResponseWriter
// accepts claims and sets expiration if none defined. permanent flag means long-living cookie,
// false makes it session only.
func (j *Service) Set(w http.ResponseWriter, claims Claims) error {
func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
if claims.ExpiresAt == 0 {
claims.ExpiresAt = time.Now().Add(j.TokenDuration).Unix()
}
@@ -190,7 +190,7 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) error {
tokenString, err := j.Token(claims)
if err != nil {
return errors.Wrap(err, "failed to make token token")
return Claims{}, errors.Wrap(err, "failed to make token token")
}
cookieExpiration := 0 // session cookie
@@ -206,7 +206,7 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) error {
MaxAge: cookieExpiration, Secure: j.SecureCookies}
http.SetCookie(w, &xsrfCookie)
return nil
return claims, nil
}
// Get token from url, header or cookie
@@ -241,6 +241,10 @@ func (j *Service) Get(r *http.Request) (Claims, string, error) {
return Claims{}, "", errors.Wrap(err, "failed to get token")
}
if !fromCookie && j.IsExpired(claims) {
return Claims{}, "", errors.New("token expired")
}
if j.DisableXSRF {
return claims, tokenString, nil
}