In a distributed microservice network, securing APIs from unauthorized internal calls is just as important as securing external endpoints. Machine-to-Machine (M2M) authentication utilizing the OAuth 2.0 Client Credentials Grant provides a robust framework for identifying and authorizing service-to-service communication.
Using Auth0 as the Identity Provider, services can exchange their client ID and secret for a signed JWT, which is then attached to all outbound requests.
1. Caching and Reusing Access Tokens
Requesting a new M2M token from Auth0 on every inter-service call introduces unacceptable network latency and will quickly hit rate limits. The Go client must cache the token in memory and only request a new one when the cached token is close to expiry:
type M2MTokenProvider struct {
client *http.Client
tokenURL string
clientID string
clientSecret string
audience string
cachedToken string
expiresAt time.Time
mu sync.RWMutex
}
func (p *M2MTokenProvider) GetToken(ctx context.Context) (string, error) {
p.mu.RLock()
if p.cachedToken != "" && time.Now().Before(p.expiresAt) {
defer p.mu.RUnlock()
return p.cachedToken, nil
}
p.mu.RUnlock()
// Acquire writer lock to refresh
p.mu.Lock()
defer p.mu.Unlock()
// Re-verify in case another thread refreshed it
if time.Now().Before(p.expiresAt) {
return p.cachedToken, nil
}
token, duration, err := p.fetchNewToken(ctx)
if err != nil {
return "", err
}
p.cachedToken = token
p.expiresAt = time.Now().Add(duration - 5*time.Minute) // buffer
return token, nil
}
2. Middleware Validation
On the receiving service, a Go middleware intercepts incoming requests, parses the Bearer JWT, and verifies its signature against Auth0's JWKS (JSON Web Key Set) endpoint. This ensures that only authorized services can invoke the endpoint.
Pre-Expiry Caching of Auth0 M2M Bearer Tokens
Cache Machine-to-Machine (client_credentials) access tokens in memory/Redis until 60 seconds before their exp claim so microservices avoid redundant Auth0 token exchanges and rate limits.