oauth

package
v0.2.19 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2025 License: Apache-2.0 Imports: 29 Imported by: 0

README


title: OAuth Plugin

Provides OAuth 2.0 authentication and claim-based authorization for endpoints.

Type

  • Wrapper
  • Swaggerer

Description

Implements OAuth 2.0 authentication with support for various providers (Google, GitHub, Auth0, Keycloak, Okta). Validates access tokens and manages method-level access permissions.

Authentication Flow

  1. Initial Setup

    • Configure the OAuth plugin with provider credentials
    • Register your application with the OAuth provider
    • Set up redirect URL in both plugin config and provider settings
  2. Authorization Flow

    • Client initiates auth by accessing: /oauth/authorize
    • Gateway redirects to provider's consent page
    • After user consent, provider redirects back to gateway's callback URL
    • Gateway exchanges the code for access token
    • Returns token to client for future requests
  3. Protected Endpoint Access

    • Client includes token in requests: Authorization: Bearer <token>
    • Gateway validates token with provider
    • If valid and has required scopes, request proceeds
    • If invalid or insufficient scopes, returns 401/403

MCP Tools Authorization Flow

  1. Middleware Integration

    • MCP Tools automatically injects authorization middleware
    • Middleware checks for valid session token in requests
  2. Initial Access

    • When user accesses protected endpoint without token:
      • System returns 401 with OAuth authorization URL
      • User is prompted to follow the authorization link
      • OAuth flow initiates through configured provider
  3. Session Management

    • After successful OAuth authorization:
      • System creates a session for the user
      • OAuth token is securely stored and associated with session
      • User receives session token for subsequent requests
  4. Protected Operations

    • All subsequent requests include session token
    • Middleware automatically validates session
    • OAuth token is used internally for protected operations
    • Session persists until explicitly terminated or timeout

Authorization Sequence Diagram

sequenceDiagram
    participant User
    participant MCP_Tools as MCP Tools Middleware
    participant Connector_DB as Connector DB
    participant OAuth as OAuth Provider
    
    Note over User, OAuth: Initial access without authorization
    
    User->>MCP_Tools: Request to protected resource (no token)
    MCP_Tools->>MCP_Tools: Check for session token
    MCP_Tools-->>User: 401 Unauthorized + Authorization URL
    
    Note over User, OAuth: OAuth authorization process
    
    User->>OAuth: Follow authorization link
    OAuth->>User: Authorization/consent page
    User->>OAuth: Enter credentials and consent
    OAuth->>MCP_Tools: Redirect with authorization code
    MCP_Tools->>OAuth: Exchange code for access token
    OAuth-->>MCP_Tools: Access token
    
    Note over User, OAuth: Session management
    
    MCP_Tools->>MCP_Tools: Create user session with unique ID
    MCP_Tools->>MCP_Tools: Store OAuth token linked to session ID
    
    Note over User, OAuth: Subsequent requests
    
    User->>MCP_Tools: Request inside session ID
    MCP_Tools->>MCP_Tools: Validate session ID and retrieve associated OAuth token
    MCP_Tools->>Connector_DB: Request with OAuth token
    Connector_DB-->>MCP_Tools: DB response
    MCP_Tools-->>User: Response to client

Configuration

oauth:
  provider: "google"           # OAuth provider (google, github, auth0, keycloak, okta)
  client_id: "xxx"            # OAuth Client ID
  client_secret: "xxx"        # OAuth Client Secret
  redirect_url: "http://localhost:8080/oauth/callback"  # OAuth callback URL
  auth_url: "/oauth/authorize" # Gateway's authorization endpoint (optional)
  callback_url: "/oauth/callback" # Gateway's callback endpoint (optional)
  scopes:                     # Required access scopes
    - "profile"
    - "email"
  token_header: "Authorization" # Header name for the token (default: "Authorization")
  user_info_url: ""          # User info endpoint URL (required for Auth0)
  introspection_url: ""      # Token introspection endpoint (required for Keycloak/Okta)
  mcp_protocol_version: "2025-03-26" # If larget than 2025-03-26 - sse URL will return 401, to force OAuth flow 
  authorization_rules:
    # Public access to health check methods
    - methods: ["GetHealth", "GetVersion"]
      allow_public: true

    # Only administrators can manage users
    - methods: ["CreateUser", "DeleteUser", "UpdateUser"]
      require_all_claims: true
      claim_rules:
        - claim: "roles"
          operation: "contains"
          value: "admin"
        - claim: "org.type"
          operation: "eq"
          value: "internal"

    # API access only for verified users
    - methods: ["GetUserProfile"]
      claim_rules:
        - claim: "email_verified"
          operation: "eq"
          value: "true"

    # Access only for specific organization
    - methods: ["GetOrganizationData", "UpdateOrganizationData"]
      require_all_claims: true
      claim_rules:
        - claim: "org.id"
          operation: "eq"
          value: "{{.OrganizationID}}"  # Support for request parameters
        - claim: "org.status"
          operation: "eq"
          value: "active"

    # Regular expression check
    - methods: ["GetDepartmentData"]
      claim_rules:
        - claim: "department"
          operation: "regex"
          value: "^(HR|Finance|IT)$"

Built-in Endpoints

The plugin automatically adds these endpoints to your gateway:

1. Authorization Endpoint

GET /oauth/authorize

Initiates the OAuth flow by redirecting to the provider's consent page.

2. Callback Endpoint

GET /oauth/callback

Handles the OAuth provider's redirect callback:

  • Exchanges authorization code for access token
  • Returns token to client
  • Optionally redirects to specified URL with token

Provider-Specific Configuration

Google

oauth:
  provider: "google"
  client_id: "xxx.apps.googleusercontent.com"
  client_secret: "xxx"
  scopes:
    - "https://www.googleapis.com/auth/userinfo.profile"
    - "https://www.googleapis.com/auth/userinfo.email"

GitHub

oauth:
  provider: "github"
  client_id: "xxx"
  client_secret: "xxx"
  scopes:
    - "read:user"
    - "user:email"

Auth0

oauth:
  provider: "auth0"
  client_id: "xxx"
  client_secret: "xxx"
  user_info_url: "https://your-tenant.auth0.com/userinfo"
  provider_auth_url: "https://your-tenant.auth0.com/authorize"
  provider_token_url: "https://your-tenant.auth0.com/oauth/token"
  scopes:
    - "openid"
    - "profile"
    - "email"

Keycloak

oauth:
  provider: "keycloak"
  client_id: "xxx"
  client_secret: "xxx"
  introspection_url: "https://your-keycloak/auth/realms/your-realm/protocol/openid-connect/token/introspect"
  scopes:
    - "openid"
    - "profile"

Okta

oauth:
  provider: "okta"
  client_id: "xxx"
  client_secret: "xxx"
  introspection_url: "https://your-org.okta.com/oauth2/v1/introspect"
  scopes:
    - "openid"
    - "profile"

Security Considerations

  1. Always use HTTPS in production
  2. Keep client_secret secure
  3. Validate redirect_urls to prevent open redirect vulnerabilities
  4. Implement state parameter to prevent CSRF attacks (implemented by default)
  5. Validate tokens on every request
  6. Use appropriate scopes - principle of least privilege
  7. Configure proper CORS settings when using as API gateway
  8. Regularly rotate client secrets
  9. Monitor and log authentication attempts

Authorization Rules

The plugin supports flexible claim-based authorization rules that can be applied to specific methods or groups of methods.

Rule Configuration

oauth:
  # ... other oauth config ...
  authorization_rules:
    # Public access to health check methods
    - methods: ["GetHealth", "GetVersion"]
      allow_public: true

    # Only administrators can manage users
    - methods: ["CreateUser", "DeleteUser", "UpdateUser"]
      require_all_claims: true
      claim_rules:
        - claim: "roles"
          operation: "contains"
          value: "admin"
        - claim: "org.type"
          operation: "eq"
          value: "internal"

    # API access only for verified users
    - methods: ["GetUserProfile"]
      claim_rules:
        - claim: "email_verified"
          operation: "eq"
          value: "true"

    # Access only for specific organization
    - methods: ["GetOrganizationData", "UpdateOrganizationData"]
      require_all_claims: true
      claim_rules:
        - claim: "org.id"
          operation: "eq"
          value: "{{.OrganizationID}}"  # Support for request parameters
        - claim: "org.status"
          operation: "eq"
          value: "active"

    # Regular expression check
    - methods: ["GetDepartmentData"]
      claim_rules:
        - claim: "department"
          operation: "regex"
          value: "^(HR|Finance|IT)$"

Supported Operations

  • eq - Exact value match
  • ne - Value is not equal
  • contains - Array contains value
  • regex - Value matches regular expression
  • exists - Check if claim exists

Claim Path Syntax

Supports access to nested fields using dot notation and array indices:

  • email - simple field
  • org.name - nested field
  • groups[0] - array element
  • org.departments[0].name - combination of nesting

Dynamic Values

Request parameters are supported in rule values using Go template syntax:

  • {{.ParamName}} - value from request parameters
  • {{.Method}} - method name
  • {{.Path}} - request path

Examples

  1. Basic Role-Based Authorization:
authorization_rules:
  - methods: ["CreateUser", "DeleteUser"]
    claim_rules:
      - claim: "roles"
        operation: "contains"
        value: "admin"
  1. Organization Membership Check:
authorization_rules:
  - methods: ["GetOrganizationData"]
    require_all_claims: true
    claim_rules:
      - claim: "org.id"
        operation: "eq"
        value: "{{.OrganizationID}}"
      - claim: "org.status"
        operation: "eq"
        value: "active"
  1. Email Domain Check:
authorization_rules:
  - methods: ["InternalAPI"]
    claim_rules:
      - claim: "email"
        operation: "regex"
        value: "@company\\.com$"
  1. Public Methods:
authorization_rules:
  - methods: ["GetPublicData", "Health"]
    allow_public: true

Configuration Examples

1. Public API with Location-Restricted Events

This example demonstrates how to configure a public API where all methods are accessible to everyone, except for specific methods that require users to be located in Berlin. This can be useful for local community events, regional features, or location-specific content.

oauth:
  provider: "github"
  client_id: "xxx"
  client_secret: "xxx"
  redirect_url: "http://localhost:8080/oauth/callback"
  scopes:
    - "read:user"
    - "user:email"
  authorization_rules:
    # Default rule - make all methods public
    - methods: ["*"]
      allow_public: true

    # Override for Berlin-only community events
    - methods: 
        - "RegisterForBerlinCommunityEvent"
        - "GetBerlinEventDetails"
        - "UpdateBerlinEventRegistration"
      require_all_claims: true
      claim_rules:
        - claim: "location"
          operation: "eq"
          value: "Berlin"

    # Optional: Additional rules can be added for specific methods
    - methods: ["UpdateUserProfile"]
      claim_rules:
        - claim: "type"
          operation: "eq"
          value: "User"

Documentation

Index

Constants

View Source
const (
	// DefaultClientSecretExpirySeconds is the default expiry time for client secrets (30 days)
	DefaultClientSecretExpirySeconds int64 = 30 * 24 * 60 * 60
)

Variables

View Source
var (
	// ErrClientNotFound is returned when a client with the specified ID is not found
	ErrClientNotFound = errors.New("oauth client not found")

	// ErrClientSecretExpired is returned when a client's secret has expired
	ErrClientSecretExpired = errors.New("oauth client secret has expired")

	// ErrInvalidClientMetadata is returned when client metadata is invalid
	ErrInvalidClientMetadata = errors.New("invalid oauth client metadata")

	// ErrMissingRedirectURIs is returned when no redirect URIs are provided
	ErrMissingRedirectURIs = errors.New("redirect_uris is required")

	// ErrRateLimitExceeded is returned when rate limit is exceeded
	ErrRateLimitExceeded = errors.New("rate limit exceeded")

	// ErrInvalidRequest is returned when the request is malformed
	ErrInvalidRequest = &OAuthError{ErrorType: "invalid_request", Description: "Invalid request"}

	// ErrUnsupportedGrantType is returned when the grant type is not supported
	ErrUnsupportedGrantType = &OAuthError{ErrorType: "unsupported_grant_type", Description: "Unsupported grant type"}
)

OAuth error definitions

View Source
var ErrServerError = &OAuthError{ErrorType: "server_error", Description: "Internal server error"}

Define ErrServerError if not already defined

Functions

func CORSMiddleware added in v0.2.9

func CORSMiddleware(handler http.Handler) http.Handler

CORSMiddleware applies standard CORS headers to the response

Types

type AuthResponse

type AuthResponse struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in,omitempty"`
}

type AuthorizationRule

type AuthorizationRule struct {
	// Methods defines the list of methods to which the rule applies
	Methods []string `yaml:"methods"`

	// AllowPublic allows public access without a token
	AllowPublic bool `yaml:"allow_public"`

	// RequireAllClaims determines if all ClaimRules must be true (AND)
	// If false, one true rule is sufficient (OR)
	RequireAllClaims bool `yaml:"require_all_claims"`

	// ClaimRules list of claim validation rules
	ClaimRules []ClaimRule `yaml:"claim_rules"`
}

AuthorizationRule defines an authorization rule for a method or group of methods

type ClaimRule

type ClaimRule struct {
	// Claim defines the path to the value in JWT or user data (e.g., "email", "groups[0]", "org.name")
	Claim string `yaml:"claim"`

	// Operation defines the comparison operation ("eq", "ne", "contains", "regex", "exists")
	Operation string `yaml:"operation"`

	// Value is the expected value for comparison
	Value string `yaml:"value"`
}

ClaimRule represents a rule for checking a claim value

type ClientRegistrationConfig added in v0.2.9

type ClientRegistrationConfig struct {
	// Enabled indicates whether dynamic client registration is enabled
	Enabled bool `yaml:"enabled"`

	// ClientSecretExpirySeconds is the number of seconds after which client secrets expire
	// If 0, client secrets will not expire (not recommended)
	ClientSecretExpirySeconds int64 `yaml:"client_secret_expiry_seconds"`

	// RateLimitRequestsPerHour is the maximum number of registration requests per hour
	// If 0, rate limiting is disabled
	RateLimitRequestsPerHour float64 `yaml:"rate_limit_requests_per_hour"`
}

ClientRegistrationConfig represents configuration for dynamic client registration

type Config

type Config struct {
	// Provider specifies the OAuth provider ("google", "github", "auth0", "keycloak", "okta")
	Provider string `yaml:"provider"`

	// ProviderAuthURL specifies oauth2.Endpoint AuthURL if Provider is unknown
	ProviderAuthURL string `yaml:"provider_auth_url"`

	// ProviderTokenURL specifies oauth2.Endpoint TokenURL if Provider is unknown
	ProviderTokenURL string `yaml:"provider_token_url"`

	// ClientID is the OAuth Client ID
	ClientID string `yaml:"client_id"`

	// ClientSecret is the OAuth Client Secret
	ClientSecret string `yaml:"client_secret"`

	// RedirectURL for OAuth flow
	RedirectURL string `yaml:"redirect_url"`

	// IssuerURL for OAuth MCP flow
	IssuerURL string `yaml:"issuer_url"`

	// Scopes defines required access scopes
	Scopes []string `yaml:"scopes"`

	// TokenHeader defines the header name for the token (default: "Authorization")
	TokenHeader string `yaml:"token_header"`

	// AuthURL is the gateway's authorization endpoint path (default: "/oauth/authorize")
	AuthURL string `yaml:"auth_url"`

	// CallbackURL is the gateway's callback endpoint path (default: "/oauth/callback")
	CallbackURL string `yaml:"callback_url"`

	// TokenURL is the gateway's token endpoint path (default: "/oauth/token")
	TokenURL string `yaml:"token_url"`

	// RegisterURL is the gateway's client registration endpoint path (default: "/oauth/register")
	RegisterURL string `yaml:"register_url"`

	// UserInfoURL is the endpoint for retrieving user information (required for Auth0)
	UserInfoURL string `yaml:"user_info_url"`

	// IntrospectionURL is the token introspection endpoint (required for Keycloak and Okta)
	IntrospectionURL string `yaml:"introspection_url"`

	// AuthorizationRules defines authorization rules for methods
	AuthorizationRules []AuthorizationRule `yaml:"authorization_rules"`

	// ClientRegistration contains configuration for dynamic client registration
	ClientRegistration ClientRegistrationConfig `yaml:"client_registration"`

	// Version of MCP protocol auth
	MCPProtocolVersion string `yaml:"mcp_protocol_version"`
}

Config represents OAuth plugin configuration

func (Config) Doc

func (c Config) Doc() string

func (Config) GetOAuthConfig

func (c Config) GetOAuthConfig() *oauth2.Config

GetOAuthConfig returns oauth2.Config for the specified provider

func (Config) Tag

func (c Config) Tag() string

func (*Config) WithDefaults

func (c *Config) WithDefaults()

WithDefaults sets default values for the config fields

type Connector

type Connector struct {
	connectors.Connector
	// contains filtered or unexported fields
}

func (*Connector) Query

func (c *Connector) Query(ctx context.Context, endpoint model.Endpoint, params map[string]any) ([]map[string]any, error)

type Metadata added in v0.2.9

type Metadata struct {
	Issuer                            string   `json:"issuer"`
	ServiceDocumentation              *string  `json:"service_documentation,omitempty"`
	AuthorizationEndpoint             string   `json:"authorization_endpoint"`
	ResponseTypesSupported            []string `json:"response_types_supported"`
	CodeChallengeMethodsSupported     []string `json:"code_challenge_methods_supported"`
	TokenEndpoint                     string   `json:"token_endpoint"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
	GrantTypesSupported               []string `json:"grant_types_supported"`
	RegistrationEndpoint              string   `json:"registration_endpoint,omitempty"`
}

func NewMetadata added in v0.2.9

func NewMetadata(issuer url.URL, authorizationEndpoint, tokenEndpoint string, registrationEndpoint string) Metadata

type OAuthClientInformation added in v0.2.9

type OAuthClientInformation struct {
	// Fields from client metadata
	OAuthClientMetadata

	// Generated fields
	ClientID              string `json:"client_id"`
	ClientSecret          string `json:"client_secret,omitempty"`
	ClientIDIssuedAt      int64  `json:"client_id_issued_at"`
	ClientSecretExpiresAt int64  `json:"client_secret_expires_at,omitempty"`
}

OAuthClientInformation represents the full client information including credentials

type OAuthClientMetadata added in v0.2.9

type OAuthClientMetadata struct {
	// Required fields
	RedirectURIs []string `json:"redirect_uris"`

	// Optional fields
	ClientName              string   `json:"client_name,omitempty"`
	ClientURI               string   `json:"client_uri,omitempty"`
	LogoURI                 string   `json:"logo_uri,omitempty"`
	Scope                   string   `json:"scope,omitempty"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty"`
	ContactsEmails          []string `json:"contacts,omitempty"`
	PolicyURI               string   `json:"policy_uri,omitempty"`
	TermsOfServiceURI       string   `json:"tos_uri,omitempty"`
	JwksURI                 string   `json:"jwks_uri,omitempty"`
	SoftwareID              string   `json:"software_id,omitempty"`
	SoftwareVersion         string   `json:"software_version,omitempty"`
}

OAuthClientMetadata represents the metadata for a dynamically registered OAuth client

type OAuthError added in v0.2.9

type OAuthError struct {
	ErrorType   string
	Description string
}

OAuthError represents an OAuth 2.0 error

func (*OAuthError) Error added in v0.2.9

func (e *OAuthError) Error() string

Error implements the error interface

func (*OAuthError) ToResponseObject added in v0.2.9

func (e *OAuthError) ToResponseObject() OAuthErrorResponse

ToResponseObject converts the error to a response object

func (*OAuthError) WithDescription added in v0.2.9

func (e *OAuthError) WithDescription(description string) *OAuthError

WithDescription returns a copy of the error with a new description

type OAuthErrorResponse added in v0.2.9

type OAuthErrorResponse struct {
	Error       string `json:"error"`
	Description string `json:"error_description,omitempty"`
}

OAuthErrorResponse represents an OAuth 2.0 error response

func NewOAuthErrorResponse added in v0.2.9

func NewOAuthErrorResponse(err error) OAuthErrorResponse

NewOAuthErrorResponse creates a new OAuth error response

type Plugin

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

func (*Plugin) Doc

func (p *Plugin) Doc() string

func (*Plugin) Enrich

func (p *Plugin) Enrich(swag *huma.OpenAPI) *huma.OpenAPI

func (*Plugin) EnrichMCP added in v0.2.7

func (p *Plugin) EnrichMCP(tooler plugins.MCPTooler)

func (*Plugin) HandleAuthorize

func (p *Plugin) HandleAuthorize(w http.ResponseWriter, r *http.Request)

func (*Plugin) HandleCallback

func (p *Plugin) HandleCallback(w http.ResponseWriter, r *http.Request)

func (*Plugin) HandleRegister added in v0.2.9

func (p *Plugin) HandleRegister(w http.ResponseWriter, r *http.Request)

HandleRegister handles OAuth client registration requests

func (*Plugin) HandleToken added in v0.2.9

func (p *Plugin) HandleToken(w http.ResponseWriter, r *http.Request)

HandleToken handles OAuth token requests

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(mux *http.ServeMux)

func (*Plugin) Wrap

func (p *Plugin) Wrap(connector connectors.Connector) (connectors.Connector, error)

type PluginBundle

func New

func New(cfg Config) (PluginBundle, error)

type RegistrationHandlerOptions added in v0.2.9

type RegistrationHandlerOptions struct {
	// ClientSecretExpirySeconds is the expiry time for client secrets in seconds
	// If 0, client secrets won't expire (not recommended)
	ClientSecretExpirySeconds int64

	// RateLimitRequests is the maximum number of requests per hour
	// If 0, rate limiting is disabled
	RateLimitRequests float64
}

RegistrationHandlerOptions contains options for the client registration handler

type SimpleRateLimiter added in v0.2.9

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

SimpleRateLimiter implements a basic rate limiter

func NewSimpleRateLimiter added in v0.2.9

func NewSimpleRateLimiter(windowSize time.Duration, maxRequest float64) *SimpleRateLimiter

NewSimpleRateLimiter creates a new rate limiter

func (*SimpleRateLimiter) Allow added in v0.2.9

func (r *SimpleRateLimiter) Allow(key string) bool

Allow checks if a request should be allowed

type TokenHandlerOptions added in v0.2.9

type TokenHandlerOptions struct {
	// RateLimitRequests is the maximum number of requests per window
	// If 0, rate limiting is disabled
	RateLimitRequests float64
}

TokenHandlerOptions contains options for the token endpoint handler

type TokenRequest added in v0.2.9

type TokenRequest struct {
	GrantType    string `json:"grant_type"`
	Code         string `json:"code,omitempty"`
	CodeVerifier string `json:"code_verifier,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
	RedirectURI  string `json:"redirect_uri,omitempty"`
}

TokenRequest represents a request to the token endpoint

Jump to

Keyboard shortcuts

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