or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

auth-handlers.mdclient-credentials.mdcore-oauth2.mdendpoints.mdgoogle-auth.mdgoogle-downscope.mdgoogle-external-account.mdindex.mdjira-oauth.mdjwt-jws.md
tile.json

client-credentials.mddocs/

Client Credentials Flow

The OAuth2 "client credentials" flow (2-legged OAuth) for server-to-server authentication where the client acts on its own behalf without user involvement.

Package

import "golang.org/x/oauth2/clientcredentials"

Overview

The client credentials flow is used when:

  • The client is acting on its own behalf
  • The client is the resource owner
  • Access to protected resources is based on prior authorization with the server

This flow does not involve user authorization and is suitable for machine-to-machine authentication.

See: https://tools.ietf.org/html/rfc6749#section-4.4

Configuration

type Config struct {
	// ClientID is the application's ID
	ClientID string

	// ClientSecret is the application's secret
	ClientSecret string

	// TokenURL is the resource server's token endpoint URL
	TokenURL string

	// Scopes specifies optional requested permissions
	Scopes []string

	// EndpointParams specifies additional parameters for token endpoint requests
	EndpointParams url.Values

	// AuthStyle optionally specifies how the endpoint wants credentials sent
	// Zero value means auto-detect
	AuthStyle oauth2.AuthStyle
}

Example:

import (
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/clientcredentials"
)

conf := &clientcredentials.Config{
	ClientID:     "your-client-id",
	ClientSecret: "your-client-secret",
	TokenURL:     "https://provider.com/oauth/token",
	Scopes:       []string{"read", "write"},
	AuthStyle:    oauth2.AuthStyleInHeader,
}

Creating Authenticated Clients

func (c *Config) Client(ctx context.Context) *http.Client

Returns an HTTP client that automatically handles token retrieval and refresh. This is the most common method for using client credentials.

The provided context optionally controls which HTTP client is used via the oauth2.HTTPClient context key. The returned client and its Transport should not be modified.

Example:

ctx := context.Background()
client := conf.Client(ctx)

// All requests automatically include authentication
resp, err := client.Get("https://api.example.com/resource")
if err != nil {
	log.Fatal(err)
}
defer resp.Body.Close()

Token Retrieval

func (c *Config) Token(ctx context.Context) (*oauth2.Token, error)

Retrieves a token using client credentials. The provided context optionally controls which HTTP client is used.

Example:

token, err := conf.Token(ctx)
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Access Token: %s\n", token.AccessToken)
fmt.Printf("Token Type: %s\n", token.Type())
fmt.Printf("Expires: %s\n", token.Expiry)

Token Source

func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource

Returns a TokenSource that automatically refreshes tokens as necessary using the client ID and client secret. Most users should use Config.Client instead.

Example:

// Advanced usage with custom token source
ts := conf.TokenSource(ctx)

// Manually get token
token, err := ts.Token()

// Or create custom client
client := oauth2.NewClient(ctx, ts)

Custom Endpoint Parameters

The EndpointParams field allows sending additional parameters to the token endpoint for providers with non-standard requirements.

Example:

conf := &clientcredentials.Config{
	ClientID:     "client-id",
	ClientSecret: "client-secret",
	TokenURL:     "https://provider.com/token",
	EndpointParams: url.Values{
		"resource":  {"https://api.example.com"},
		"audience":  {"https://api.example.com"},
	},
}

Note: The grant_type parameter can be overridden via EndpointParams for interoperability with non-compliant implementations, but other standard parameters cannot be overwritten.

Authentication Styles

The AuthStyle field controls how client credentials are sent to the token endpoint:

import "golang.org/x/oauth2"

// Auto-detect the style (default)
conf.AuthStyle = oauth2.AuthStyleAutoDetect

// Send credentials in POST body
conf.AuthStyle = oauth2.AuthStyleInParams

// Send credentials via HTTP Basic Authorization
conf.AuthStyle = oauth2.AuthStyleInHeader

Error Handling

Errors during token retrieval return oauth2.RetrieveError with detailed information:

token, err := conf.Token(ctx)
if err != nil {
	if rerr, ok := err.(*oauth2.RetrieveError); ok {
		fmt.Printf("Token error: %s - %s\n",
			rerr.ErrorCode, rerr.ErrorDescription)
	}
	return err
}

Complete Example

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"net/http"

	"golang.org/x/oauth2/clientcredentials"
)

func main() {
	ctx := context.Background()

	// Configure client credentials
	conf := &clientcredentials.Config{
		ClientID:     "your-client-id",
		ClientSecret: "your-client-secret",
		TokenURL:     "https://auth.example.com/oauth/token",
		Scopes:       []string{"api.read", "api.write"},
	}

	// Create authenticated HTTP client
	client := conf.Client(ctx)

	// Make authenticated API request
	resp, err := client.Get("https://api.example.com/data")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// Read response
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Response: %s\n", body)
}

Differences from Authorization Code Flow

FeatureClient CredentialsAuthorization Code
User involvementNo user authorization requiredRequires user to authorize
Use caseMachine-to-machineUser-delegated access
Refresh tokensNot issuedTypically issued with offline access
Flow type2-legged3-legged
Token lifespanUsually short-lived, auto-renewedCan be long-lived with refresh

Security Considerations

  1. Protect client secrets: Store client secrets securely, never in source code
  2. Use HTTPS: Always use HTTPS for token endpoint communication
  3. Scope limitation: Request only the scopes necessary for your application
  4. Token storage: If caching tokens, store them securely
  5. Credential rotation: Implement regular credential rotation policies

Custom HTTP Client

To use a custom HTTP client for token requests:

customClient := &http.Client{
	Timeout: 10 * time.Second,
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			MinVersion: tls.VersionTLS12,
		},
	},
}

ctx := context.WithValue(context.Background(), oauth2.HTTPClient, customClient)
client := conf.Client(ctx)