chore: reuse user token source (#26144)
This commit is contained in:
parent
2b13eb2668
commit
2691bbda1b
4 changed files with 85 additions and 52 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/cache"
|
||||
"github.com/evcc-io/evcc/util/oauth"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"golang.org/x/oauth2"
|
||||
|
|
@ -38,8 +39,12 @@ type tokenSource struct {
|
|||
user, password string
|
||||
}
|
||||
|
||||
// TokenSource creates an Easee token source
|
||||
// tokenSourceCache stores per-user token sources
|
||||
var tokenSourceCache = cache.New[oauth2.TokenSource]()
|
||||
|
||||
// TokenSource returns a shared oauth2.TokenSource for the given user.
|
||||
func TokenSource(log *util.Logger, user, password string) (oauth2.TokenSource, error) {
|
||||
return tokenSourceCache.GetOrCreate(user, func() (oauth2.TokenSource, error) {
|
||||
c := &tokenSource{
|
||||
Helper: request.NewHelper(log),
|
||||
user: user,
|
||||
|
|
@ -47,8 +52,12 @@ func TokenSource(log *util.Logger, user, password string) (oauth2.TokenSource, e
|
|||
}
|
||||
|
||||
token, err := c.authenticate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return oauth.RefreshTokenSource(token.AsOAuth2Token(), c.refreshToken), err
|
||||
return oauth.RefreshTokenSource(token.AsOAuth2Token(), c.refreshToken), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *tokenSource) authenticate() (*Token, error) {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ func NewZaptec(ctx context.Context, user, password, id string, priority bool, pa
|
|||
})
|
||||
|
||||
// Get shared token source for this user (per-user uniqueness)
|
||||
ts, err := zaptec.GetTokenSource(tsCtx, user, password)
|
||||
ts, err := zaptec.TokenSource(tsCtx, user, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/evcc-io/evcc/util/cache"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
|
|
@ -23,10 +24,9 @@ func (p *passwordTokenSource) Token() (*oauth2.Token, error) {
|
|||
return p.config.PasswordCredentialsToken(p.ctx, p.user, p.pass)
|
||||
}
|
||||
|
||||
// tokenSourceCache stores per-user token sources
|
||||
var (
|
||||
tokenSourceMu sync.Mutex
|
||||
tokenSourceCache = make(map[string]oauth2.TokenSource)
|
||||
// TokenSourceCache stores per-user token sources
|
||||
tokenSourceCache = cache.New[oauth2.TokenSource]()
|
||||
|
||||
oidcProvider *oidc.Provider
|
||||
oidcProviderOnce sync.Once
|
||||
|
|
@ -41,19 +41,9 @@ func getOIDCProvider(ctx context.Context) (*oidc.Provider, error) {
|
|||
return oidcProvider, oidcProviderErr
|
||||
}
|
||||
|
||||
// GetTokenSource returns a shared oauth2.TokenSource for the given user credentials.
|
||||
// Multiple chargers using the same user credentials will share the same TokenSource,
|
||||
// ensuring tokens are reused and authentication is deduplicated.
|
||||
func GetTokenSource(ctx context.Context, user, pass string) (oauth2.TokenSource, error) {
|
||||
tokenSourceMu.Lock()
|
||||
defer tokenSourceMu.Unlock()
|
||||
|
||||
// Use username as the cache key (assuming username is unique)
|
||||
if ts, exists := tokenSourceCache[user]; exists {
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// Get the cached OIDC provider (initialized once)
|
||||
// TokenSource returns a shared oauth2.TokenSource for the given user.
|
||||
func TokenSource(ctx context.Context, user, pass string) (oauth2.TokenSource, error) {
|
||||
return tokenSourceCache.GetOrCreate(user, func() (oauth2.TokenSource, error) {
|
||||
provider, err := getOIDCProvider(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize OIDC provider: %w", err)
|
||||
|
|
@ -80,9 +70,6 @@ func GetTokenSource(ctx context.Context, user, pass string) (oauth2.TokenSource,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap with ReuseTokenSource to cache tokens
|
||||
ts := oauth2.ReuseTokenSource(token, pts)
|
||||
tokenSourceCache[user] = ts
|
||||
|
||||
return ts, nil
|
||||
return oauth2.ReuseTokenSource(token, pts), nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
37
util/cache/cache.go
vendored
Normal file
37
util/cache/cache.go
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Cache provides thread-safe caching keyed by username
|
||||
type Cache[T any] struct {
|
||||
mu sync.Mutex
|
||||
cache map[string]T
|
||||
}
|
||||
|
||||
// New creates a new Cache instance
|
||||
func New[T any]() *Cache[T] {
|
||||
return &Cache[T]{
|
||||
cache: make(map[string]T),
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrCreate atomically gets or creates a cached object
|
||||
func (c *Cache[T]) GetOrCreate(key string, createFn func() (T, error)) (T, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if res, ok := c.cache[key]; ok {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res, err := createFn()
|
||||
if err != nil {
|
||||
var zero T
|
||||
return zero, err
|
||||
}
|
||||
|
||||
c.cache[key] = res
|
||||
return res, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue