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

core-oauth2.mddocs/

Core OAuth2 Flows

Complete documentation for the main golang.org/x/oauth2 package covering authorization code flow, token management, device authorization, PKCE, and HTTP transport.

Package

import "golang.org/x/oauth2"

Authorization Code Flow

The standard 3-legged OAuth2 flow for web and mobile applications where users grant permission via a browser.

Configuration

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

	// ClientSecret is the application's secret
	ClientSecret string

	// Endpoint contains the authorization server's token endpoint URLs
	Endpoint Endpoint

	// RedirectURL is the URL to redirect users going through the OAuth flow
	RedirectURL string

	// Scopes specifies optional requested permissions
	Scopes []string
}

Generating Authorization URLs

func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string

Generates a URL to the OAuth 2.0 provider's consent page. The state parameter is an opaque value used to maintain state between request and callback for CSRF protection.

Example:

conf := &oauth2.Config{
	ClientID:     "your-client-id",
	ClientSecret: "your-client-secret",
	Scopes:       []string{"profile", "email"},
	Endpoint:     google.Endpoint,
	RedirectURL:  "http://localhost:8080/callback",
}

// With PKCE (recommended for CSRF protection)
verifier := oauth2.GenerateVerifier()
url := conf.AuthCodeURL("random-state", oauth2.S256ChallengeOption(verifier))

// With access type for refresh tokens
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)

// Force consent dialog
url := conf.AuthCodeURL("state", oauth2.ApprovalForce)

Exchanging Authorization Codes

func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error)

Converts an authorization code into a token. Used after the provider redirects the user back to your redirect URI with a code parameter.

Example:

// After user authorizes, extract code from callback
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")

// Validate state parameter
if state != expectedState {
	return errors.New("invalid state")
}

// Exchange code for token (with PKCE verifier if used)
token, err := conf.Exchange(ctx, code, oauth2.VerifierOption(verifier))
if err != nil {
	return err
}

Creating Authenticated Clients

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

Returns an HTTP client that automatically adds authentication and refreshes tokens as needed.

func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource

Returns a TokenSource for more granular control. Most users should use Client instead.

Example:

client := conf.Client(ctx, token)

// All requests automatically include authentication
resp, err := client.Get("https://api.example.com/user")

Password Credentials Flow

func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error)

Converts a username/password pair into a token. Only use when there is high trust between resource owner and client (e.g., native OS applications).

Device Authorization Flow

OAuth2 flow for devices with limited input capabilities (smart TVs, IoT devices, CLI tools) following RFC 8628.

Device Auth Response

type DeviceAuthResponse struct {
	// DeviceCode is the device verification code
	DeviceCode string `json:"device_code"`

	// UserCode is the code the user should enter at the verification URI
	UserCode string `json:"user_code"`

	// VerificationURI is where the user should enter the user code
	VerificationURI string `json:"verification_uri"`

	// VerificationURIComplete includes the user code in the URI (e.g., for QR codes)
	VerificationURIComplete string `json:"verification_uri_complete,omitempty"`

	// Expiry is when the device code and user code expire
	Expiry time.Time `json:"expires_in,omitempty"`

	// Interval is the duration in seconds between polling requests
	Interval int64 `json:"interval,omitempty"`
}

func (d DeviceAuthResponse) MarshalJSON() ([]byte, error)
func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error

Initiating Device Authorization

func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error)

Returns device authorization information for the user to complete on another device.

Polling for Token

func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error)

Polls the authorization server to exchange a device code for an access token. Automatically handles polling intervals and continues until authorization is granted, denied, or expired.

Example:

// Initiate device authorization
deviceAuth, err := conf.DeviceAuth(ctx)
if err != nil {
	return err
}

// Display instructions to user
fmt.Printf("Visit %s and enter code: %s\n",
	deviceAuth.VerificationURI, deviceAuth.UserCode)

// Poll for access token (blocks until user authorizes or timeout)
token, err := conf.DeviceAccessToken(ctx, deviceAuth)
if err != nil {
	return err
}

// Use token
client := conf.Client(ctx, token)

PKCE Support

Proof Key for Code Exchange (PKCE) provides additional security against authorization code interception attacks.

Generating Verifiers

func GenerateVerifier() string

Generates a PKCE code verifier with 32 octets of randomness following RFC 7636. A fresh verifier should be generated for each authorization flow.

Challenge Options

func S256ChallengeOption(verifier string) AuthCodeOption

Derives a PKCE code challenge from the verifier using the S256 method. Pass to AuthCodeURL or DeviceAuth.

func S256ChallengeFromVerifier(verifier string) string

Returns the S256 challenge string. Prefer S256ChallengeOption for most use cases.

func VerifierOption(verifier string) AuthCodeOption

Returns the verifier as an AuthCodeOption. Pass to Exchange or DeviceAccessToken.

Example:

// Generate verifier
verifier := oauth2.GenerateVerifier()

// Use challenge when generating auth URL
authURL := conf.AuthCodeURL("state", oauth2.S256ChallengeOption(verifier))

// Use verifier when exchanging code
token, err := conf.Exchange(ctx, code, oauth2.VerifierOption(verifier))

Token Management

Token Type

type Token struct {
	// AccessToken is the token that authorizes and authenticates requests
	AccessToken string `json:"access_token"`

	// TokenType is the type of token (default: Bearer)
	TokenType string `json:"token_type,omitempty"`

	// RefreshToken is used to refresh the access token when it expires
	RefreshToken string `json:"refresh_token,omitempty"`

	// Expiry is the optional expiration time of the access token
	Expiry time.Time `json:"expiry,omitempty"`

	// ExpiresIn is the OAuth2 wire format field (seconds until expiration)
	ExpiresIn int64 `json:"expires_in,omitempty"`
}

Token Methods

func (t *Token) Valid() bool

Reports whether the token is non-nil, has an AccessToken, and is not expired.

func (t *Token) Type() string

Returns the token type if set, otherwise "Bearer".

func (t *Token) SetAuthHeader(r *http.Request)

Sets the Authorization header on an HTTP request. Unnecessary when using Transport or Config.Client.

func (t *Token) Extra(key string) any

Returns extra fields from the token retrieval response.

func (t *Token) WithExtra(extra any) *Token

Returns a clone of the token with the provided raw extra map. For implementing derivative OAuth2 flows.

Example:

// Check token validity
if !token.Valid() {
	// Token needs refresh
}

// Access extra fields
idToken := token.Extra("id_token").(string)

// Manual authorization header (rarely needed)
req, _ := http.NewRequest("GET", url, nil)
token.SetAuthHeader(req)

Token Sources

TokenSource Interface

type TokenSource interface {
	// Token returns a token or an error
	// Must be safe for concurrent use
	// The returned Token must not be modified
	Token() (*Token, error)
}

Static Token Source

func StaticTokenSource(t *Token) TokenSource

Returns a TokenSource that always returns the same token. Only useful for tokens that never expire.

Reusable Token Source

func ReuseTokenSource(t *Token, src TokenSource) TokenSource

Returns a TokenSource that reuses the same token while valid, automatically refreshing from src when expired. Typically used to cache tokens between program runs.

func ReuseTokenSourceWithExpiry(t *Token, src TokenSource, earlyExpiry time.Duration) TokenSource

Like ReuseTokenSource but with configurable expiry buffer. The token is considered expired at t.Expiry.Add(-earlyExpiry).

Example:

// Static token (no refresh)
src := oauth2.StaticTokenSource(&oauth2.Token{
	AccessToken: "permanent-token",
})

// Cached token with automatic refresh
cached := oauth2.ReuseTokenSource(cachedToken, conf.TokenSource(ctx, cachedToken))

// With custom expiry buffer (refresh 1 minute early)
src := oauth2.ReuseTokenSourceWithExpiry(token, conf.TokenSource(ctx, token), time.Minute)

// Create client from token source
client := oauth2.NewClient(ctx, src)

HTTP Transport

Low-level HTTP transport for OAuth2 authentication.

Transport Type

type Transport struct {
	// Source supplies the token to add to outgoing requests
	Source TokenSource

	// Base is the base RoundTripper (defaults to http.DefaultTransport)
	Base http.RoundTripper
}

Transport Methods

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

Authorizes and authenticates the request with an access token from the Transport's Source.

func (t *Transport) CancelRequest(req *http.Request)

Deprecated: No-op. Use contexts for cancellation instead.

Creating Clients

func NewClient(ctx context.Context, src TokenSource) *http.Client

Creates an HTTP client from a TokenSource. The returned client is not valid beyond the lifetime of the context. Most users should use Config.Client instead.

Example:

// Low-level transport usage
transport := &oauth2.Transport{
	Source: oauth2.StaticTokenSource(token),
	Base:   http.DefaultTransport,
}

client := &http.Client{Transport: transport}

// Or use NewClient helper
client := oauth2.NewClient(ctx, tokenSource)

Endpoints

Endpoint Type

type Endpoint struct {
	// AuthURL is the authorization endpoint URL
	AuthURL string

	// DeviceAuthURL is the device authorization endpoint URL (optional)
	DeviceAuthURL string

	// TokenURL is the token endpoint URL
	TokenURL string

	// AuthStyle specifies how to send client credentials (auto-detect if zero)
	AuthStyle AuthStyle
}

Auth Style

type AuthStyle int

const (
	// AuthStyleAutoDetect tries both methods and caches the successful one
	AuthStyleAutoDetect AuthStyle = 0

	// AuthStyleInParams sends client_id and client_secret in POST body
	AuthStyleInParams AuthStyle = 1

	// AuthStyleInHeader sends credentials via HTTP Basic Authorization
	AuthStyleInHeader AuthStyle = 2
)

Example:

// Custom endpoint
endpoint := oauth2.Endpoint{
	AuthURL:  "https://provider.com/oauth/authorize",
	TokenURL: "https://provider.com/oauth/token",
	AuthStyle: oauth2.AuthStyleInHeader,
}

// Using predefined endpoints
import "golang.org/x/oauth2/google"
endpoint := google.Endpoint

Auth Code Options

AuthCodeOption Interface

type AuthCodeOption interface {
	// Has unexported methods
}

Predefined Options

var AccessTypeOnline AuthCodeOption
var AccessTypeOffline AuthCodeOption
var ApprovalForce AuthCodeOption
  • AccessTypeOnline: Default access type
  • AccessTypeOffline: Request refresh token for offline access
  • ApprovalForce: Force user to view consent dialog

Custom Parameters

func SetAuthURLParam(key, value string) AuthCodeOption

Builds an AuthCodeOption that passes custom key/value parameters to the provider's authorization endpoint.

Example:

// Request refresh token
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)

// Custom parameters
url := conf.AuthCodeURL("state",
	oauth2.SetAuthURLParam("prompt", "select_account"),
	oauth2.SetAuthURLParam("login_hint", "user@example.com"))

Error Handling

RetrieveError

type RetrieveError struct {
	// Response is the HTTP response
	Response *http.Response

	// Body is the response body (may be truncated)
	Body []byte

	// ErrorCode is RFC 6749's 'error' parameter
	ErrorCode string

	// ErrorDescription is RFC 6749's 'error_description' parameter
	ErrorDescription string

	// ErrorURI is RFC 6749's 'error_uri' parameter
	ErrorURI string
}

func (r *RetrieveError) Error() string

Error returned when the token endpoint returns a non-2XX status or populates the 'error' parameter.

Example:

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

Context and HTTP Client

HTTP Client Context Key

var HTTPClient internal.ContextKey

Context key to associate a custom *http.Client with a context for token acquisition.

Example:

// Use custom HTTP client for token requests
customClient := &http.Client{Timeout: 10 * time.Second}
ctx := context.WithValue(ctx, oauth2.HTTPClient, customClient)

token, err := conf.Exchange(ctx, code)

Deprecated Functions

var NoContext = context.TODO()

Deprecated: Use context.Background() or context.TODO() instead.

func RegisterBrokenAuthHeaderProvider(tokenURL string)

Deprecated: No-op function. Set Endpoint.AuthStyle instead to avoid auto-probing.

Complete Example

package main

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

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

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

	// Configure OAuth2
	conf := &oauth2.Config{
		ClientID:     "your-client-id",
		ClientSecret: "your-client-secret",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email",
			"https://www.googleapis.com/auth/userinfo.profile",
		},
		Endpoint:    google.Endpoint,
		RedirectURL: "http://localhost:8080/callback",
	}

	// Generate PKCE verifier
	verifier := oauth2.GenerateVerifier()

	// Generate authorization URL
	url := conf.AuthCodeURL("state-token",
		oauth2.AccessTypeOffline,
		oauth2.S256ChallengeOption(verifier))

	fmt.Printf("Visit this URL: %s\n", url)

	// Start callback server
	http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
		// Verify state
		if r.URL.Query().Get("state") != "state-token" {
			http.Error(w, "State mismatch", http.StatusBadRequest)
			return
		}

		// Exchange code for token
		code := r.URL.Query().Get("code")
		token, err := conf.Exchange(ctx, code, oauth2.VerifierOption(verifier))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

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

		// Make API request
		resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		defer resp.Body.Close()

		// Process response...
		fmt.Fprintf(w, "Authentication successful!")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}