oauth

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package oauth provides small, composable OAuth 2.0/2.1 primitives used by AppTheory components (e.g. Remote MCP protected resources) and by Autheory (an OAuth Authorization Server built on AppTheory).

The package intentionally focuses on wire-level helpers: - RFC9728 protected resource discovery (WWW-Authenticate + metadata) - RFC8414 authorization server metadata helpers - RFC7591 Dynamic Client Registration request/response types + validation - PKCE utilities

Index

Constants

View Source
const ContextKeyBearerToken = "oauth.bearer_token" //nolint:gosec // context key, not a credential

ContextKeyBearerToken is set on apptheory.Context by RequireBearerTokenMiddleware.

Variables

View Source
var (
	// ErrMissingBearerToken is returned when an Authorization header is missing.
	ErrMissingBearerToken = errors.New("missing bearer token")
	// ErrInvalidAuthorizationHeader is returned when the Authorization header cannot be parsed as Bearer.
	ErrInvalidAuthorizationHeader = errors.New("invalid authorization header")
	// ErrInvalidURL is returned when a required URL is not a valid absolute URL.
	ErrInvalidURL = errors.New("invalid url")

	// ErrAuthorizationCodeNotFound indicates an unknown/consumed authorization code.
	ErrAuthorizationCodeNotFound = errors.New("authorization code not found")
	// ErrAuthorizationCodeExpired indicates an expired authorization code.
	ErrAuthorizationCodeExpired = errors.New("authorization code expired")

	// ErrRefreshTokenNotFound indicates an unknown/revoked refresh token.
	ErrRefreshTokenNotFound = errors.New("refresh token not found")
	// ErrRefreshTokenExpired indicates an expired refresh token.
	ErrRefreshTokenExpired = errors.New("refresh token expired")
)

Functions

func AuthorizationServerMetadataHandler

func AuthorizationServerMetadataHandler(md *AuthorizationServerMetadata) apptheory.Handler

AuthorizationServerMetadataHandler returns an AppTheory handler that serves the RFC8414 authorization server metadata document.

func BearerTokenFromHeaders

func BearerTokenFromHeaders(headers map[string][]string) (string, error)

BearerTokenFromHeaders extracts the bearer token from normalized headers.

func CanonicalResourceURL

func CanonicalResourceURL(raw string) string

CanonicalResourceURL trims whitespace and a trailing slash.

func CanonicalizeIssuerURL

func CanonicalizeIssuerURL(raw string) (string, bool)

CanonicalizeIssuerURL trims trailing slashes from an issuer/base URL.

func NewOpaqueToken

func NewOpaqueToken() (string, error)

NewOpaqueToken generates a URL-safe opaque token suitable for authorization codes and refresh tokens.

func NewPKCECodeVerifier

func NewPKCECodeVerifier() (string, error)

NewPKCECodeVerifier generates a random PKCE code verifier (length ~43).

func PKCEChallengeS256

func PKCEChallengeS256(verifier string) (string, error)

PKCEChallengeS256 computes the S256 code challenge for a verifier.

func PKCEVerifyS256

func PKCEVerifyS256(verifier, expectedChallenge string) (bool, error)

PKCEVerifyS256 verifies a verifier matches an expected S256 challenge.

func ProtectedResourceMetadataHandler

func ProtectedResourceMetadataHandler(md *ProtectedResourceMetadata) apptheory.Handler

ProtectedResourceMetadataHandler returns an AppTheory handler that serves the RFC9728 protected resource metadata document.

func ProtectedResourceMetadataURLForRequest

func ProtectedResourceMetadataURLForRequest(headers map[string][]string) (string, bool)

ProtectedResourceMetadataURLForRequest attempts to derive an absolute `/.well-known/oauth-protected-resource` URL from common proxy headers.

Prefer using ResourceMetadataURLFromMcpEndpoint with MCP_ENDPOINT for AWS deployments; this helper is primarily for local/test environments.

func ProtectedResourceWWWAuthenticate

func ProtectedResourceWWWAuthenticate(resourceMetadataURL string) string

ProtectedResourceWWWAuthenticate builds the RFC9728 MCP-style discovery challenge.

Example:

Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"

func RequireBearerTokenMiddleware

func RequireBearerTokenMiddleware(opts RequireBearerTokenOptions) apptheory.Middleware

RequireBearerTokenMiddleware enforces `Authorization: Bearer <token>` and, when missing/invalid, returns 401 with the MCP 2025-06-18 compatible WWW-Authenticate challenge that points to protected resource metadata.

func ResourceMetadataURLFromMcpEndpoint

func ResourceMetadataURLFromMcpEndpoint(mcpEndpoint string) (string, bool)

ResourceMetadataURLFromMcpEndpoint derives the protected resource metadata URL from an MCP endpoint URL (which must end with `/mcp`).

For example:

https://api.example.com/prod/mcp -> https://api.example.com/prod/.well-known/oauth-protected-resource

func ValidateDynamicClientRegistrationRequest

func ValidateDynamicClientRegistrationRequest(req *DynamicClientRegistrationRequest, policy DynamicClientRegistrationPolicy) error

ValidateDynamicClientRegistrationRequest validates an RFC7591 request against a policy.

func ValidatePKCECodeVerifier

func ValidatePKCECodeVerifier(verifier string) error

ValidatePKCECodeVerifier validates a verifier against RFC7636 constraints.

Types

type AuthorizationCodeRecord

type AuthorizationCodeRecord struct {
	Code                string
	ClientID            string
	RedirectURI         string
	Resource            string
	CodeChallenge       string
	CodeChallengeMethod string
	ExpiresAt           time.Time
}

AuthorizationCodeRecord stores the data needed to redeem an authorization code.

type AuthorizationCodeStore

type AuthorizationCodeStore interface {
	Put(ctx context.Context, rec *AuthorizationCodeRecord) error
	Consume(ctx context.Context, code string) (*AuthorizationCodeRecord, error)
}

AuthorizationCodeStore stores short-lived authorization codes.

type AuthorizationServerMetadata

type AuthorizationServerMetadata struct {
	Issuer                            string   `json:"issuer"`
	AuthorizationEndpoint             string   `json:"authorization_endpoint,omitempty"`
	TokenEndpoint                     string   `json:"token_endpoint,omitempty"`
	RegistrationEndpoint              string   `json:"registration_endpoint,omitempty"`
	JWKSURI                           string   `json:"jwks_uri,omitempty"`
	ResponseTypesSupported            []string `json:"response_types_supported,omitempty"`
	GrantTypesSupported               []string `json:"grant_types_supported,omitempty"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
	CodeChallengeMethodsSupported     []string `json:"code_challenge_methods_supported,omitempty"`
	ScopesSupported                   []string `json:"scopes_supported,omitempty"`
	SubjectTypesSupported             []string `json:"subject_types_supported,omitempty"`
	IDTokenSigningAlgValuesSupported  []string `json:"id_token_signing_alg_values_supported,omitempty"`
}

AuthorizationServerMetadata is the RFC8414 Authorization Server metadata document.

func NewAuthorizationServerMetadata

func NewAuthorizationServerMetadata(issuer string) (*AuthorizationServerMetadata, error)

NewAuthorizationServerMetadata builds a Claude-compatible RFC8414 document with conventional root endpoints (/authorize, /token, /register) derived from the issuer/base URL.

func (*AuthorizationServerMetadata) MarshalJSONBytes

func (m *AuthorizationServerMetadata) MarshalJSONBytes() ([]byte, error)

MarshalJSONBytes marshals the metadata document to JSON bytes.

type BearerTokenValidator

type BearerTokenValidator func(ctx context.Context, token string) error

BearerTokenValidator validates a Bearer access token.

Implementations can perform JWT verification using a JWKS, introspection, or any other mechanism suitable for the deployment.

type DynamicClientRegistrationPolicy

type DynamicClientRegistrationPolicy struct {
	AllowedRedirectURIs []string
	RequirePublicClient bool // token_endpoint_auth_method must be "none"
	RequireRefreshToken bool // grant_types must include "refresh_token" (if grant_types provided)
}

DynamicClientRegistrationPolicy controls DCR validation.

func ClaudeDynamicClientRegistrationPolicy

func ClaudeDynamicClientRegistrationPolicy() DynamicClientRegistrationPolicy

ClaudeDynamicClientRegistrationPolicy returns a day-1 policy compatible with Claude connectors.

type DynamicClientRegistrationRequest

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

DynamicClientRegistrationRequest is an RFC7591 client registration request.

type DynamicClientRegistrationResponse

type DynamicClientRegistrationResponse struct {
	ClientID string `json:"client_id"`
	// ClientSecret is omitted for public clients.
	ClientSecret          string `json:"client_secret,omitempty"`
	ClientIDIssuedAt      int64  `json:"client_id_issued_at,omitempty"`
	ClientSecretExpiresAt int64  `json:"client_secret_expires_at,omitempty"`
}

DynamicClientRegistrationResponse is a minimal RFC7591 response.

type MemoryAuthorizationCodeStore

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

MemoryAuthorizationCodeStore is an in-memory AuthorizationCodeStore.

func NewMemoryAuthorizationCodeStore

func NewMemoryAuthorizationCodeStore() *MemoryAuthorizationCodeStore

func (*MemoryAuthorizationCodeStore) Consume

func (*MemoryAuthorizationCodeStore) Put

type MemoryRefreshTokenStore

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

MemoryRefreshTokenStore is an in-memory RefreshTokenStore.

func NewMemoryRefreshTokenStore

func NewMemoryRefreshTokenStore() *MemoryRefreshTokenStore

func (*MemoryRefreshTokenStore) Consume

func (*MemoryRefreshTokenStore) Delete

func (s *MemoryRefreshTokenStore) Delete(_ context.Context, token string) error

func (*MemoryRefreshTokenStore) Get

func (*MemoryRefreshTokenStore) Put

type ProtectedResourceMetadata

type ProtectedResourceMetadata struct {
	Resource               string   `json:"resource"`
	AuthorizationServers   []string `json:"authorization_servers"`
	JWKSURI                string   `json:"jwks_uri,omitempty"`
	ScopesSupported        []string `json:"scopes_supported,omitempty"`
	BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
}

ProtectedResourceMetadata is the RFC9728 discovery document hosted by a protected resource server.

func NewProtectedResourceMetadata

func NewProtectedResourceMetadata(resource string, authorizationServers []string) (*ProtectedResourceMetadata, error)

NewProtectedResourceMetadata creates a minimal metadata document. It requires: - resource: an absolute URL that identifies the protected resource (for MCP this is typically the `/mcp` endpoint) - authorizationServers: one or more OAuth AS issuer/base URLs

func (*ProtectedResourceMetadata) MarshalJSONBytes

func (m *ProtectedResourceMetadata) MarshalJSONBytes() ([]byte, error)

MarshalJSONBytes marshals the metadata document to JSON bytes.

type RefreshTokenRecord

type RefreshTokenRecord struct {
	Token     string
	ClientID  string
	Subject   string
	Resource  string
	ExpiresAt time.Time
}

RefreshTokenRecord stores long-lived refresh token state.

type RefreshTokenStore

type RefreshTokenStore interface {
	Put(ctx context.Context, rec *RefreshTokenRecord) error
	Get(ctx context.Context, token string) (*RefreshTokenRecord, error)
	Consume(ctx context.Context, token string) (*RefreshTokenRecord, error)
	Delete(ctx context.Context, token string) error
}

RefreshTokenStore stores refresh tokens. Implementations should support rotation by Consume+Put.

type RequireBearerTokenOptions

type RequireBearerTokenOptions struct {
	// ResourceMetadataURL is used to build the RFC9728 discovery challenge.
	//
	// If empty, the middleware attempts to derive it from MCP_ENDPOINT, and then
	// from request headers (Host + X-Forwarded-Proto) as a last resort.
	ResourceMetadataURL string

	// Validator, when provided, is called for every request. If it returns an
	// error the request is rejected with 401.
	Validator BearerTokenValidator
}

RequireBearerTokenOptions configures RequireBearerTokenMiddleware.

Jump to

Keyboard shortcuts

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