Overview
Custom token sources allow you to implement advanced token management strategies, such as:
Sharing cached tokens across multiple services
Integrating with external token providers
Implementing custom refresh logic to reduce M2M token usage
Adding custom authentication flows
The SDK accepts any oauth2.TokenSource implementation, giving you full control over token acquisition and caching.
Using Custom Token Sources
The WithTokenSource option allows you to provide your own token source implementation:
import (
" context "
" github.com/auth0/go-auth0/v2/management/client "
" github.com/auth0/go-auth0/v2/management/option "
" golang.org/x/oauth2 "
)
mgmt , err := client . New (
"your-tenant.auth0.com" ,
option . WithTokenSource ( oauth2 . ReuseTokenSource ( nil , myCustomTokenSource )),
)
The authentication options (WithClientCredentials, WithClientCredentialsAndAudience, WithClientCredentialsPrivateKeyJwt, WithClientCredentialsPrivateKeyJwtAndAudience, and WithTokenSource) are mutually exclusive. They all configure the underlying token source, so if multiple are provided, the last one applied takes effect. Similarly, WithToken sets a static token, but any token source option takes priority over it.
Example: Shared Token Cache
Implement a token source that shares tokens across multiple SDK instances:
package main
import (
" context "
" sync "
" time "
" github.com/auth0/go-auth0/v2/management/client "
" github.com/auth0/go-auth0/v2/management/option "
" golang.org/x/oauth2 "
" golang.org/x/oauth2/clientcredentials "
)
// SharedTokenCache implements oauth2.TokenSource with cross-instance caching
type SharedTokenCache struct {
mu sync . RWMutex
token * oauth2 . Token
config * clientcredentials . Config
refreshBuffer time . Duration
}
func NewSharedTokenCache ( domain , clientID , clientSecret string ) * SharedTokenCache {
return & SharedTokenCache {
config : & clientcredentials . Config {
ClientID : clientID ,
ClientSecret : clientSecret ,
TokenURL : "https://" + domain + "/oauth/token" ,
EndpointParams : url . Values {
"audience" : { "https://" + domain + "/api/v2/" },
},
},
refreshBuffer : 5 * time . Minute , // Refresh 5 minutes before expiry
}
}
func ( s * SharedTokenCache ) Token () ( * oauth2 . Token , error ) {
s . mu . RLock ()
if s . token != nil && s . token . Valid () {
// Check if token is still valid with buffer
if time . Until ( s . token . Expiry ) > s . refreshBuffer {
token := s . token
s . mu . RUnlock ()
return token , nil
}
}
s . mu . RUnlock ()
// Acquire write lock for refresh
s . mu . Lock ()
defer s . mu . Unlock ()
// Double-check after acquiring write lock
if s . token != nil && time . Until ( s . token . Expiry ) > s . refreshBuffer {
return s . token , nil
}
// Fetch new token
ctx := context . Background ()
token , err := s . config . Token ( ctx )
if err != nil {
return nil , err
}
s . token = token
return token , nil
}
func main () {
// Create shared token cache
tokenCache := NewSharedTokenCache (
"your-tenant.auth0.com" ,
"YOUR_CLIENT_ID" ,
"YOUR_CLIENT_SECRET" ,
)
// Use with ReuseTokenSource for automatic token refresh
tokenSource := oauth2 . ReuseTokenSource ( nil , tokenCache )
// Create multiple management clients sharing the same token
mgmt1 , err := client . New (
"your-tenant.auth0.com" ,
option . WithTokenSource ( tokenSource ),
)
mgmt2 , err := client . New (
"your-tenant.auth0.com" ,
option . WithTokenSource ( tokenSource ),
)
// Both clients will use the same cached token
ctx := context . Background ()
clients1 , _ := mgmt1 . Clients . List ( ctx , nil )
clients2 , _ := mgmt2 . Clients . List ( ctx , nil )
}
Example: Redis-Backed Token Cache
Store tokens in Redis for sharing across distributed services:
package main
import (
" context "
" encoding/json "
" time "
" github.com/go-redis/redis/v8 "
" golang.org/x/oauth2 "
" golang.org/x/oauth2/clientcredentials "
)
type RedisTokenSource struct {
redis * redis . Client
cacheKey string
config * clientcredentials . Config
refreshBuffer time . Duration
}
func NewRedisTokenSource (
redisClient * redis . Client ,
cacheKey string ,
domain string ,
clientID string ,
clientSecret string ,
) * RedisTokenSource {
return & RedisTokenSource {
redis : redisClient ,
cacheKey : cacheKey ,
config : & clientcredentials . Config {
ClientID : clientID ,
ClientSecret : clientSecret ,
TokenURL : "https://" + domain + "/oauth/token" ,
EndpointParams : url . Values {
"audience" : { "https://" + domain + "/api/v2/" },
},
},
refreshBuffer : 5 * time . Minute ,
}
}
func ( r * RedisTokenSource ) Token () ( * oauth2 . Token , error ) {
ctx := context . Background ()
// Try to get token from Redis
data , err := r . redis . Get ( ctx , r . cacheKey ). Bytes ()
if err == nil {
var token oauth2 . Token
if err := json . Unmarshal ( data , & token ); err == nil {
if time . Until ( token . Expiry ) > r . refreshBuffer {
return & token , nil
}
}
}
// Fetch new token
token , err := r . config . Token ( ctx )
if err != nil {
return nil , err
}
// Cache in Redis with expiry
tokenData , _ := json . Marshal ( token )
ttl := time . Until ( token . Expiry )
r . redis . Set ( ctx , r . cacheKey , tokenData , ttl )
return token , nil
}
func main () {
// Initialize Redis client
rdb := redis . NewClient ( & redis . Options {
Addr : "localhost:6379" ,
})
// Create Redis-backed token source
tokenSource := NewRedisTokenSource (
rdb ,
"auth0:management:token" ,
"your-tenant.auth0.com" ,
"YOUR_CLIENT_ID" ,
"YOUR_CLIENT_SECRET" ,
)
// Use with management client
mgmt , err := client . New (
"your-tenant.auth0.com" ,
option . WithTokenSource ( oauth2 . ReuseTokenSource ( nil , tokenSource )),
)
}
Example: External Token Provider
Integrate with an external token management service:
package main
import (
" encoding/json "
" fmt "
" net/http "
" time "
" golang.org/x/oauth2 "
)
type ExternalTokenProvider struct {
tokenServiceURL string
apiKey string
httpClient * http . Client
}
func NewExternalTokenProvider ( serviceURL , apiKey string ) * ExternalTokenProvider {
return & ExternalTokenProvider {
tokenServiceURL : serviceURL ,
apiKey : apiKey ,
httpClient : & http . Client { Timeout : 10 * time . Second },
}
}
func ( e * ExternalTokenProvider ) Token () ( * oauth2 . Token , error ) {
req , err := http . NewRequest ( "GET" , e . tokenServiceURL + "/token/auth0" , nil )
if err != nil {
return nil , err
}
req . Header . Set ( "Authorization" , "Bearer " + e . apiKey )
req . Header . Set ( "Accept" , "application/json" )
resp , err := e . httpClient . Do ( req )
if err != nil {
return nil , err
}
defer resp . Body . Close ()
if resp . StatusCode != http . StatusOK {
return nil , fmt . Errorf ( "token service returned status: %d " , resp . StatusCode )
}
var tokenResp struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresAt time . Time `json:"expires_at"`
}
if err := json . NewDecoder ( resp . Body ). Decode ( & tokenResp ); err != nil {
return nil , err
}
return & oauth2 . Token {
AccessToken : tokenResp . AccessToken ,
TokenType : tokenResp . TokenType ,
Expiry : tokenResp . ExpiresAt ,
}, nil
}
func main () {
// Create external token provider
tokenProvider := NewExternalTokenProvider (
"https://token-service.example.com" ,
"YOUR_API_KEY" ,
)
// Use with management client
mgmt , err := client . New (
"your-tenant.auth0.com" ,
option . WithTokenSource ( oauth2 . ReuseTokenSource ( nil , tokenProvider )),
)
}
Best Practices
Use ReuseTokenSource Always wrap your custom token source with oauth2.ReuseTokenSource to automatically handle token refresh and avoid unnecessary token requests.
Implement Refresh Buffer Refresh tokens before they expire (e.g., 5 minutes early) to avoid authentication failures during requests.
Thread Safety Make your token source implementation thread-safe using mutexes or channels, as it may be called concurrently.
Error Handling Implement proper error handling and retry logic for token acquisition failures.
Static Tokens
For simple use cases where you already have a valid token, use WithToken:
mgmt , err := client . New (
"your-tenant.auth0.com" ,
option . WithToken ( "YOUR_ACCESS_TOKEN" ),
)
Static tokens don’t automatically refresh. For production use, prefer token sources that handle refresh automatically.