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
- Variables
- func GenerateAccessToken() (string, error)
- func GenerateAuthorizationCode() (string, error)
- func GenerateClientID() (string, error)
- func GenerateClientSecret() (string, error)
- func GenerateCodeChallenge(verifier string) string
- func GenerateCodeVerifier() (string, error)
- func GenerateRefreshToken() (string, error)
- func GenerateSecureToken(length int) (string, error)
- func GetClientIDFromContext(ctx context.Context) string
- func GetScopeFromContext(ctx context.Context) string
- func GetSubjectFromContext(ctx context.Context) string
- func SetTokenInfoContext(ctx context.Context, info *TokenInfo) context.Context
- func ValidateCodeVerifier(verifier string) error
- func VerifyCodeChallenge(verifier, challenge, method string) error
- type Authenticator
- type AuthorizationCode
- type Client
- type Config
- type MemoryStorage
- func (m *MemoryStorage) Cleanup()
- func (m *MemoryStorage) CreateAuthorizationCode(code *AuthorizationCode) error
- func (m *MemoryStorage) CreateClient(client *Client) error
- func (m *MemoryStorage) CreateToken(token *TokenInfo) error
- func (m *MemoryStorage) DeleteAuthorizationCode(code string) error
- func (m *MemoryStorage) DeleteClient(clientID string) error
- func (m *MemoryStorage) DeleteToken(accessToken string) error
- func (m *MemoryStorage) DeleteTokensByClient(clientID string) error
- func (m *MemoryStorage) GetAuthorizationCode(code string) (*AuthorizationCode, error)
- func (m *MemoryStorage) GetClient(clientID string) (*Client, error)
- func (m *MemoryStorage) GetToken(accessToken string) (*TokenInfo, error)
- func (m *MemoryStorage) GetTokenByRefresh(refreshToken string) (*TokenInfo, error)
- func (m *MemoryStorage) MarkAuthorizationCodeUsed(code string) error
- func (m *MemoryStorage) StartCleanup(interval time.Duration) func()
- type OAuthError
- type PathConfig
- type RegistrationRequest
- type RegistrationResponse
- type Server
- func (s *Server) AuthorizationHandler() http.Handler
- func (s *Server) BearerAuthMiddleware(resourceMetadataURL string) func(http.Handler) http.Handler
- func (s *Server) Config() *Config
- func (s *Server) Logger() *slog.Logger
- func (s *Server) MetadataHandler() http.Handler
- func (s *Server) Paths() *PathConfig
- func (s *Server) ProtectedResourceMetadataHandler(resourcePath string) http.Handler
- func (s *Server) RegisterClient(clientID, clientSecret string, redirectURIs []string) (string, string, error)
- func (s *Server) RegisterHandlers(mux *http.ServeMux)
- func (s *Server) RegistrationHandler() http.Handler
- func (s *Server) TokenHandler() http.Handler
- func (s *Server) TokenVerifier() func(token string) (*TokenInfo, error)
- type Storage
- type TokenInfo
- type TokenRequest
- type TokenResponse
Constants ¶
const ( ErrorInvalidRequest = "invalid_request" 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
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 ¶
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.
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 ¶
GenerateAccessToken generates a secure access token.
func GenerateAuthorizationCode ¶
GenerateAuthorizationCode generates a secure authorization code.
func GenerateClientID ¶
GenerateClientID generates a unique client ID.
func GenerateClientSecret ¶
GenerateClientSecret generates a secure client secret.
func GenerateCodeChallenge ¶
GenerateCodeChallenge generates a code challenge from a code verifier using the S256 method (SHA-256 hash, base64url encoded).
func GenerateCodeVerifier ¶
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 ¶
GenerateRefreshToken generates a secure refresh token.
func GenerateSecureToken ¶
GenerateSecureToken generates a cryptographically secure random token encoded as base64url without padding.
func GetClientIDFromContext ¶
GetClientIDFromContext returns the client ID from the context. Returns empty string if not found.
func GetScopeFromContext ¶
GetScopeFromContext returns the scope from the context. Returns empty string if not found.
func GetSubjectFromContext ¶
GetSubjectFromContext returns the authenticated subject (username) from the context. Returns empty string if not found.
func SetTokenInfoContext ¶
SetTokenInfoContext stores TokenInfo in the context.
func ValidateCodeVerifier ¶
ValidateCodeVerifier validates that a code verifier meets RFC 7636 requirements.
func VerifyCodeChallenge ¶
VerifyCodeChallenge verifies that a code verifier matches a code challenge. Only supports the S256 method.
Types ¶
type Authenticator ¶
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 (*Server) AuthorizationHandler ¶
AuthorizationHandler returns the HTTP handler for the authorization endpoint. This endpoint handles the OAuth 2.1 authorization code flow with PKCE.
func (*Server) BearerAuthMiddleware ¶
BearerAuthMiddleware returns middleware that validates Bearer tokens and sets the token info in the request context.
func (*Server) MetadataHandler ¶
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 ¶
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 ¶
RegisterHandlers registers all OAuth handlers on the given mux using the configured paths. This is a convenience method for simple setups.
func (*Server) RegistrationHandler ¶
RegistrationHandler returns the HTTP handler for dynamic client registration. This endpoint allows clients to register themselves (RFC 7591).
func (*Server) TokenHandler ¶
TokenHandler returns the HTTP handler for the token endpoint. This endpoint exchanges authorization codes for access tokens.
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 ¶
GetTokenInfoContext retrieves TokenInfo from the context. Returns nil if not found.
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.