oauth2

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package oauth2 provides a standalone OAuth 2.1 Authorization Server with PKCE support, designed for MCP server authentication.

Features:

  • OAuth 2.1 Authorization Code Flow with PKCE (RFC 7636)
  • Dynamic Client Registration (RFC 7591)
  • Authorization Server Metadata (RFC 8414)
  • Protected Resource Metadata (RFC 9728)
  • Simple username/password authentication

Usage:

srv, err := oauth2.New(&oauth2.Config{
    Issuer:   "https://example.com",
    Users:    map[string]string{"admin": "password"},
})
if err != nil {
    log.Fatal(err)
}

// Mount handlers on your HTTP mux
mux.Handle("/oauth/authorize", srv.AuthorizationHandler())
mux.Handle("/oauth/token", srv.TokenHandler())
mux.Handle("/oauth/register", srv.RegistrationHandler())
mux.Handle("/.well-known/oauth-authorization-server", srv.MetadataHandler())

Index

Constants

View Source
const (
	ErrorInvalidRequest          = "invalid_request"
	ErrorUnauthorizedClient      = "unauthorized_client"
	ErrorAccessDenied            = "access_denied"
	ErrorUnsupportedResponseType = "unsupported_response_type"
	ErrorInvalidScope            = "invalid_scope"
	ErrorServerError             = "server_error"
	ErrorInvalidClient           = "invalid_client"
	ErrorInvalidGrant            = "invalid_grant"
	ErrorUnsupportedGrantType    = "unsupported_grant_type"
)

OAuth 2.0 error codes per RFC 6749

View Source
const (
	// PKCEMethodS256 is the only supported PKCE method (SHA-256).
	PKCEMethodS256 = "S256"

	// PKCEMethodPlain is not supported for security reasons.
	PKCEMethodPlain = "plain"

	// MinVerifierLength is the minimum length for a code verifier (RFC 7636).
	MinVerifierLength = 43

	// MaxVerifierLength is the maximum length for a code verifier (RFC 7636).
	MaxVerifierLength = 128
)

Variables

View Source
var (
	ErrPKCERequired           = errors.New("PKCE code_challenge is required")
	ErrPKCEMethodNotSupported = errors.New("only S256 code_challenge_method is supported")
	ErrPKCEVerificationFailed = errors.New("PKCE code_verifier verification failed")
	ErrPKCEVerifierTooShort   = errors.New("code_verifier must be at least 43 characters")
	ErrPKCEVerifierTooLong    = errors.New("code_verifier must be at most 128 characters")
	ErrPKCEVerifierInvalid    = errors.New("code_verifier contains invalid characters")
)

PKCE errors.

View Source
var (
	ErrClientNotFound = errors.New("client not found")
	ErrCodeNotFound   = errors.New("authorization code not found")
	ErrCodeExpired    = errors.New("authorization code expired")
	ErrCodeUsed       = errors.New("authorization code already used")
	ErrTokenNotFound  = errors.New("token not found")
	ErrTokenExpired   = errors.New("token expired")
)

Common errors returned by storage operations.

Functions

func GenerateAccessToken

func GenerateAccessToken() (string, error)

GenerateAccessToken generates a secure access token.

func GenerateAuthorizationCode

func GenerateAuthorizationCode() (string, error)

GenerateAuthorizationCode generates a secure authorization code.

func GenerateClientID

func GenerateClientID() (string, error)

GenerateClientID generates a unique client ID.

func GenerateClientSecret

func GenerateClientSecret() (string, error)

GenerateClientSecret generates a secure client secret.

func GenerateCodeChallenge

func GenerateCodeChallenge(verifier string) string

GenerateCodeChallenge generates a code challenge from a code verifier using the S256 method (SHA-256 hash, base64url encoded).

func GenerateCodeVerifier

func GenerateCodeVerifier() (string, error)

GenerateCodeVerifier generates a cryptographically secure code verifier suitable for PKCE. The verifier is 43-128 characters from the unreserved character set [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~".

func GenerateRefreshToken

func GenerateRefreshToken() (string, error)

GenerateRefreshToken generates a secure refresh token.

func GenerateSecureToken

func GenerateSecureToken(length int) (string, error)

GenerateSecureToken generates a cryptographically secure random token encoded as base64url without padding.

func GetClientIDFromContext

func GetClientIDFromContext(ctx context.Context) string

GetClientIDFromContext returns the client ID from the context. Returns empty string if not found.

func GetScopeFromContext

func GetScopeFromContext(ctx context.Context) string

GetScopeFromContext returns the scope from the context. Returns empty string if not found.

func GetSubjectFromContext

func GetSubjectFromContext(ctx context.Context) string

GetSubjectFromContext returns the authenticated subject (username) from the context. Returns empty string if not found.

func SetTokenInfoContext

func SetTokenInfoContext(ctx context.Context, info *TokenInfo) context.Context

SetTokenInfoContext stores TokenInfo in the context.

func ValidateCodeVerifier

func ValidateCodeVerifier(verifier string) error

ValidateCodeVerifier validates that a code verifier meets RFC 7636 requirements.

func VerifyCodeChallenge

func VerifyCodeChallenge(verifier, challenge, method string) error

VerifyCodeChallenge verifies that a code verifier matches a code challenge. Only supports the S256 method.

Types

type Authenticator

type Authenticator func(username, password string) bool

Authenticator is a function that validates user credentials. Returns true if the credentials are valid.

type AuthorizationCode

type AuthorizationCode struct {
	// Code is the authorization code value.
	Code string `json:"code"`

	// ClientID is the client that requested this code.
	ClientID string `json:"client_id"`

	// RedirectURI is the redirect URI used in the authorization request.
	RedirectURI string `json:"redirect_uri"`

	// Scope is the requested scope.
	Scope string `json:"scope,omitempty"`

	// CodeChallenge is the PKCE code challenge.
	CodeChallenge string `json:"code_challenge"`

	// CodeChallengeMethod is the PKCE challenge method (always "S256").
	CodeChallengeMethod string `json:"code_challenge_method"`

	// Subject is the authenticated user.
	Subject string `json:"subject"`

	// ExpiresAt is when this code expires.
	ExpiresAt time.Time `json:"expires_at"`

	// Used indicates if this code has been exchanged.
	Used bool `json:"used"`

	// CreatedAt is when this code was created.
	CreatedAt time.Time `json:"created_at"`
}

AuthorizationCode represents a pending authorization code.

type Client

type Client struct {
	// ClientID is the unique client identifier.
	ClientID string `json:"client_id"`

	// ClientSecret is the client secret (may be empty for public clients).
	ClientSecret string `json:"client_secret,omitempty"`

	// ClientName is the human-readable name of the client.
	ClientName string `json:"client_name,omitempty"`

	// RedirectURIs is the list of allowed redirect URIs.
	RedirectURIs []string `json:"redirect_uris"`

	// GrantTypes is the list of allowed grant types.
	// Defaults to ["authorization_code"] if empty.
	GrantTypes []string `json:"grant_types,omitempty"`

	// ResponseTypes is the list of allowed response types.
	// Defaults to ["code"] if empty.
	ResponseTypes []string `json:"response_types,omitempty"`

	// TokenEndpointAuthMethod is the authentication method for the token endpoint.
	// Values: "none", "client_secret_basic", "client_secret_post"
	TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`

	// CreatedAt is when the client was registered.
	CreatedAt time.Time `json:"created_at"`
}

Client represents a registered OAuth client.

type Config

type Config struct {
	// Issuer is the OAuth issuer URL (e.g., "https://example.com").
	// This is used in metadata and token responses.
	Issuer string

	// Users is a map of username to password for authentication.
	// For production, consider implementing a custom Authenticator.
	Users map[string]string

	// Authenticator is a custom authentication function.
	// If nil, Users map is used for authentication.
	Authenticator Authenticator

	// Storage is the storage backend for clients, codes, and tokens.
	// If nil, an in-memory storage is used.
	Storage Storage

	// AuthorizationCodeExpiry is how long authorization codes are valid.
	// Defaults to 10 minutes.
	AuthorizationCodeExpiry time.Duration

	// AccessTokenExpiry is how long access tokens are valid.
	// Defaults to 1 hour.
	AccessTokenExpiry time.Duration

	// RefreshTokenExpiry is how long refresh tokens are valid.
	// Defaults to 24 hours. Set to 0 to disable refresh tokens.
	RefreshTokenExpiry time.Duration

	// AllowedScopes is the list of scopes this server supports.
	// If empty, no scope validation is performed.
	AllowedScopes []string

	// LoginPageTemplate is custom HTML for the login page.
	// If empty, a default login page is used.
	LoginPageTemplate string

	// Paths configures the endpoint paths.
	Paths *PathConfig

	// Logger is used for debug logging. If nil, uses slog.Default().
	Logger *slog.Logger

	// Debug enables verbose debug logging.
	Debug bool
}

Config configures the OAuth 2.1 server.

type MemoryStorage

type MemoryStorage struct {
	// contains filtered or unexported fields
}

MemoryStorage is an in-memory implementation of Storage. Suitable for development and testing.

func NewMemoryStorage

func NewMemoryStorage() *MemoryStorage

NewMemoryStorage creates a new in-memory storage.

func (*MemoryStorage) Cleanup

func (m *MemoryStorage) Cleanup()

Cleanup removes expired codes and tokens. Call periodically.

func (*MemoryStorage) CreateAuthorizationCode

func (m *MemoryStorage) CreateAuthorizationCode(code *AuthorizationCode) error

CreateAuthorizationCode stores a new authorization code.

func (*MemoryStorage) CreateClient

func (m *MemoryStorage) CreateClient(client *Client) error

CreateClient stores a new client.

func (*MemoryStorage) CreateToken

func (m *MemoryStorage) CreateToken(token *TokenInfo) error

CreateToken stores a new token.

func (*MemoryStorage) DeleteAuthorizationCode

func (m *MemoryStorage) DeleteAuthorizationCode(code string) error

DeleteAuthorizationCode removes an authorization code.

func (*MemoryStorage) DeleteClient

func (m *MemoryStorage) DeleteClient(clientID string) error

DeleteClient removes a client.

func (*MemoryStorage) DeleteToken

func (m *MemoryStorage) DeleteToken(accessToken string) error

DeleteToken removes a token.

func (*MemoryStorage) DeleteTokensByClient

func (m *MemoryStorage) DeleteTokensByClient(clientID string) error

DeleteTokensByClient removes all tokens for a client.

func (*MemoryStorage) GetAuthorizationCode

func (m *MemoryStorage) GetAuthorizationCode(code string) (*AuthorizationCode, error)

GetAuthorizationCode retrieves an authorization code.

func (*MemoryStorage) GetClient

func (m *MemoryStorage) GetClient(clientID string) (*Client, error)

GetClient retrieves a client by ID.

func (*MemoryStorage) GetToken

func (m *MemoryStorage) GetToken(accessToken string) (*TokenInfo, error)

GetToken retrieves a token by access token.

func (*MemoryStorage) GetTokenByRefresh

func (m *MemoryStorage) GetTokenByRefresh(refreshToken string) (*TokenInfo, error)

GetTokenByRefresh retrieves a token by refresh token.

func (*MemoryStorage) MarkAuthorizationCodeUsed

func (m *MemoryStorage) MarkAuthorizationCodeUsed(code string) error

MarkAuthorizationCodeUsed marks a code as used.

func (*MemoryStorage) StartCleanup

func (m *MemoryStorage) StartCleanup(interval time.Duration) func()

StartCleanup starts a background goroutine that periodically cleans up expired codes and tokens. Returns a stop function to halt cleanup.

type OAuthError

type OAuthError struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description,omitempty"`
	ErrorURI         string `json:"error_uri,omitempty"`
}

OAuthError represents an OAuth 2.0 error response.

type PathConfig

type PathConfig struct {
	// Authorization is the authorization endpoint path. Defaults to "/oauth/authorize".
	Authorization string

	// Token is the token endpoint path. Defaults to "/oauth/token".
	Token string

	// Registration is the dynamic client registration path. Defaults to "/oauth/register".
	Registration string

	// Metadata is the authorization server metadata path.
	// Defaults to "/.well-known/oauth-authorization-server".
	Metadata string
}

PathConfig configures the OAuth endpoint paths.

func DefaultPaths

func DefaultPaths() *PathConfig

DefaultPaths returns the default OAuth endpoint paths.

type RegistrationRequest

type RegistrationRequest struct {
	RedirectURIs            []string `json:"redirect_uris"`
	ClientName              string   `json:"client_name,omitempty"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty"`
}

RegistrationRequest is the DCR request body (RFC 7591).

type RegistrationResponse

type RegistrationResponse struct {
	ClientID                string   `json:"client_id"`
	ClientSecret            string   `json:"client_secret,omitempty"`
	ClientName              string   `json:"client_name,omitempty"`
	RedirectURIs            []string `json:"redirect_uris"`
	GrantTypes              []string `json:"grant_types"`
	ResponseTypes           []string `json:"response_types"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method"`
}

RegistrationResponse is the DCR response body (RFC 7591).

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is an OAuth 2.1 Authorization Server with PKCE support.

func New

func New(cfg *Config) (*Server, error)

New creates a new OAuth 2.1 server with the given configuration.

func (*Server) AuthorizationHandler

func (s *Server) AuthorizationHandler() http.Handler

AuthorizationHandler returns the HTTP handler for the authorization endpoint. This endpoint handles the OAuth 2.1 authorization code flow with PKCE.

func (*Server) BearerAuthMiddleware

func (s *Server) BearerAuthMiddleware(resourceMetadataURL string) func(http.Handler) http.Handler

BearerAuthMiddleware returns middleware that validates Bearer tokens and sets the token info in the request context.

func (*Server) Config

func (s *Server) Config() *Config

Config returns a copy of the server configuration.

func (*Server) Logger

func (s *Server) Logger() *slog.Logger

Logger returns the server's logger.

func (*Server) MetadataHandler

func (s *Server) MetadataHandler() http.Handler

MetadataHandler returns the HTTP handler for authorization server metadata. This should be mounted at /.well-known/oauth-authorization-server (RFC 8414).

func (*Server) Paths

func (s *Server) Paths() *PathConfig

Paths returns the configured endpoint paths.

func (*Server) ProtectedResourceMetadataHandler

func (s *Server) ProtectedResourceMetadataHandler(resourcePath string) http.Handler

ProtectedResourceMetadataHandler returns the HTTP handler for protected resource metadata (RFC 9728). The resourcePath is the path to the protected resource (e.g., "/mcp").

func (*Server) RegisterClient

func (s *Server) RegisterClient(clientID, clientSecret string, redirectURIs []string) (string, string, error)

RegisterClient pre-registers a client with the given credentials. This is useful for clients like ChatGPT.com that require you to enter client credentials during configuration rather than using DCR. If clientID or clientSecret is empty, they will be auto-generated. If redirectURIs is empty, all URIs will be allowed.

func (*Server) RegisterHandlers

func (s *Server) RegisterHandlers(mux *http.ServeMux)

RegisterHandlers registers all OAuth handlers on the given mux using the configured paths. This is a convenience method for simple setups.

func (*Server) RegistrationHandler

func (s *Server) RegistrationHandler() http.Handler

RegistrationHandler returns the HTTP handler for dynamic client registration. This endpoint allows clients to register themselves (RFC 7591).

func (*Server) TokenHandler

func (s *Server) TokenHandler() http.Handler

TokenHandler returns the HTTP handler for the token endpoint. This endpoint exchanges authorization codes for access tokens.

func (*Server) TokenVerifier

func (s *Server) TokenVerifier() func(token string) (*TokenInfo, error)

TokenVerifier returns a function that verifies access tokens. This can be used with middleware to protect resources.

type Storage

type Storage interface {
	// Client operations
	CreateClient(client *Client) error
	GetClient(clientID string) (*Client, error)
	DeleteClient(clientID string) error

	// Authorization code operations
	CreateAuthorizationCode(code *AuthorizationCode) error
	GetAuthorizationCode(code string) (*AuthorizationCode, error)
	DeleteAuthorizationCode(code string) error
	MarkAuthorizationCodeUsed(code string) error

	// Token operations
	CreateToken(token *TokenInfo) error
	GetToken(accessToken string) (*TokenInfo, error)
	GetTokenByRefresh(refreshToken string) (*TokenInfo, error)
	DeleteToken(accessToken string) error
	DeleteTokensByClient(clientID string) error
}

Storage defines the interface for OAuth data persistence.

type TokenInfo

type TokenInfo struct {
	// AccessToken is the access token value.
	AccessToken string `json:"access_token"`

	// RefreshToken is the refresh token value (if issued).
	RefreshToken string `json:"refresh_token,omitempty"`

	// TokenType is always "Bearer".
	TokenType string `json:"token_type"`

	// ClientID is the client this token was issued to.
	ClientID string `json:"client_id"`

	// Subject is the authenticated user.
	Subject string `json:"subject"`

	// Scope is the granted scope.
	Scope string `json:"scope,omitempty"`

	// ExpiresAt is when the access token expires.
	ExpiresAt time.Time `json:"expires_at"`

	// RefreshExpiresAt is when the refresh token expires.
	RefreshExpiresAt time.Time `json:"refresh_expires_at,omitempty"`

	// CreatedAt is when this token was created.
	CreatedAt time.Time `json:"created_at"`
}

TokenInfo represents an issued access token.

func GetTokenInfoContext

func GetTokenInfoContext(ctx context.Context) *TokenInfo

GetTokenInfoContext retrieves TokenInfo from the context. Returns nil if not found.

func (*TokenInfo) IsExpired

func (t *TokenInfo) IsExpired() bool

IsExpired returns true if the access token has expired.

type TokenRequest

type TokenRequest struct {
	GrantType    string
	Code         string
	RedirectURI  string
	ClientID     string
	ClientSecret string
	CodeVerifier string
	RefreshToken string
	Scope        string
}

TokenRequest represents a token endpoint request.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse is the token endpoint response body.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL