mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AuthN: Add in-memory cache for oauth token refresh hook (#63569)
* OAuthSyncHook: Add in-memory cache so we don't have to perform the check on every request Co-authored-by: Jo <joao.guerreiro@grafana.com>
This commit is contained in:
parent
f94b957333
commit
057d9c45fa
@ -148,7 +148,7 @@ func ProvideService(
|
|||||||
s.RegisterPostAuthHook(userSyncService.SyncLastSeenHook, 40)
|
s.RegisterPostAuthHook(userSyncService.SyncLastSeenHook, 40)
|
||||||
|
|
||||||
if features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) {
|
if features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||||
s.RegisterPostAuthHook(sync.ProvideOauthTokenSync(oauthTokenService, sessionService).SyncOauthTokenHook, 60)
|
s.RegisterPostAuthHook(sync.ProvideOAuthTokenSync(oauthTokenService, sessionService).SyncOauthTokenHook, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.RegisterPostAuthHook(userSyncService.FetchSyncedUserHook, 100)
|
s.RegisterPostAuthHook(userSyncService.FetchSyncedUserHook, 100)
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/authn"
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
@ -17,21 +18,23 @@ var (
|
|||||||
errExpiredAccessToken = errutil.NewBase(errutil.StatusUnauthorized, "oauth.expired-token")
|
errExpiredAccessToken = errutil.NewBase(errutil.StatusUnauthorized, "oauth.expired-token")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideOauthTokenSync(service oauthtoken.OAuthTokenService, sessionService auth.UserTokenService) *OauthTokenSync {
|
func ProvideOAuthTokenSync(service oauthtoken.OAuthTokenService, sessionService auth.UserTokenService) *OAuthTokenSync {
|
||||||
return &OauthTokenSync{
|
return &OAuthTokenSync{
|
||||||
log.New("oauth_token.sync"),
|
log.New("oauth_token.sync"),
|
||||||
|
localcache.New(maxOAuthTokenCacheTTL, 15*time.Minute),
|
||||||
service,
|
service,
|
||||||
sessionService,
|
sessionService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type OauthTokenSync struct {
|
type OAuthTokenSync struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
|
cache *localcache.CacheService
|
||||||
service oauthtoken.OAuthTokenService
|
service oauthtoken.OAuthTokenService
|
||||||
sessionService auth.UserTokenService
|
sessionService auth.UserTokenService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OauthTokenSync) SyncOauthTokenHook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
func (s *OAuthTokenSync) SyncOauthTokenHook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
||||||
namespace, id := identity.NamespacedID()
|
namespace, id := identity.NamespacedID()
|
||||||
// only perform oauth token check if identity is a user
|
// only perform oauth token check if identity is a user
|
||||||
if namespace != authn.NamespaceUser {
|
if namespace != authn.NamespaceUser {
|
||||||
@ -43,6 +46,11 @@ func (s *OauthTokenSync) SyncOauthTokenHook(ctx context.Context, identity *authn
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we recently have performed this it would be cached, so we can skip the hook
|
||||||
|
if _, ok := s.cache.Get(identity.ID); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
token, exists, _ := s.service.HasOAuthEntry(ctx, &user.SignedInUser{UserID: id})
|
token, exists, _ := s.service.HasOAuthEntry(ctx, &user.SignedInUser{UserID: id})
|
||||||
// user is not authenticated through oauth so skip further checks
|
// user is not authenticated through oauth so skip further checks
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -51,11 +59,16 @@ func (s *OauthTokenSync) SyncOauthTokenHook(ctx context.Context, identity *authn
|
|||||||
|
|
||||||
// token has no expire time configured, so we don't have to refresh it
|
// token has no expire time configured, so we don't have to refresh it
|
||||||
if token.OAuthExpiry.IsZero() {
|
if token.OAuthExpiry.IsZero() {
|
||||||
|
// cache the token check, so we don't perform it on every request
|
||||||
|
s.cache.Set(identity.ID, struct{}{}, getOAuthTokenCacheTTL(token.OAuthExpiry))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expires := token.OAuthExpiry.Round(0).Add(-oauthtoken.ExpiryDelta)
|
||||||
// token has not expired, so we don't have to refresh it
|
// token has not expired, so we don't have to refresh it
|
||||||
if !token.OAuthExpiry.Round(0).Add(-oauthtoken.ExpiryDelta).Before(time.Now()) {
|
if !expires.Before(time.Now()) {
|
||||||
|
// cache the token check, so we don't perform it on every request
|
||||||
|
s.cache.Set(identity.ID, struct{}{}, getOAuthTokenCacheTTL(expires))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,3 +90,18 @@ func (s *OauthTokenSync) SyncOauthTokenHook(ctx context.Context, identity *authn
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxOAuthTokenCacheTTL = 10 * time.Minute
|
||||||
|
|
||||||
|
func getOAuthTokenCacheTTL(t time.Time) time.Duration {
|
||||||
|
if t.IsZero() {
|
||||||
|
return maxOAuthTokenCacheTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl := time.Until(t)
|
||||||
|
if ttl > maxOAuthTokenCacheTTL {
|
||||||
|
return maxOAuthTokenCacheTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
return ttl
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
"github.com/grafana/grafana/pkg/services/auth/authtest"
|
||||||
@ -17,7 +18,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOauthTokenSync_SyncOauthTokenHook(t *testing.T) {
|
func TestOauthTokenSync_SyncOAuthTokenHook(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
desc string
|
desc string
|
||||||
identity *authn.Identity
|
identity *authn.Identity
|
||||||
@ -117,8 +118,9 @@ func TestOauthTokenSync_SyncOauthTokenHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sync := &OauthTokenSync{
|
sync := &OAuthTokenSync{
|
||||||
log: log.NewNopLogger(),
|
log: log.NewNopLogger(),
|
||||||
|
cache: localcache.New(0, 0),
|
||||||
service: service,
|
service: service,
|
||||||
sessionService: sessionService,
|
sessionService: sessionService,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user