Add OAuth service implementation with configuration and validation

- Introduced the OAuth service structure, including configuration management and user provider integration.
- Implemented default settings and validation for various configuration parameters, ensuring robust service initialization.
- Added methods for retrieving service configuration and user information based on access tokens.
- Defined error handling for configuration issues, enhancing reliability of the OAuth service.
This commit is contained in:
Max 2025-07-17 12:04:42 +08:00
parent 57d61c1ba3
commit 54607bc3ee
8 changed files with 1001 additions and 0 deletions

1
job/README.md Normal file
View file

@ -0,0 +1 @@
# Job

1
job/job.go Normal file
View file

@ -0,0 +1 @@
package job

12
job/types/interfaces.go Normal file
View file

@ -0,0 +1,12 @@
package types
import "context"
// Job interface
type Job interface {
Run(ctx context.Context) error
AddTask(ctx context.Context, task Task) error
}
// Task interface
type Task func(ctx context.Context, job Job) error

1
job/types/types.go Normal file
View file

@ -0,0 +1 @@
package types

148
openapi/oauth/interfaces.go Normal file
View file

@ -0,0 +1,148 @@
package oauth
import (
"context"
)
// OAuth interface defines the complete OAuth 2.1 and MCP authorization server functionality
type OAuth interface {
// AuthorizationServer returns the authorization server endpoint URL
// This endpoint is used to initiate the authorization flow
AuthorizationServer(ctx context.Context) string
// ProtectedResource returns the protected resource endpoint URL
// This endpoint is used to access protected resources using access tokens
ProtectedResource(ctx context.Context) string
// Authorize processes an authorization request and returns an authorization code
// The authorization code can be exchanged for an access token
Authorize(ctx context.Context, request *AuthorizationRequest) (*AuthorizationResponse, error)
// Token exchanges an authorization code for an access token
// This is the core token endpoint functionality
Token(ctx context.Context, grantType string, code string, clientID string, codeVerifier string) (*Token, error)
// Revoke revokes an access token or refresh token
// Once revoked, the token cannot be used for accessing protected resources
Revoke(ctx context.Context, token string, tokenTypeHint string) error
// Introspect returns information about an access token
// This endpoint allows resource servers to validate tokens
Introspect(ctx context.Context, token string) (*TokenIntrospectionResponse, error)
// Register registers a new OAuth client with the authorization server
// This is used for static client registration
Register(ctx context.Context, clientInfo *ClientInfo) (*ClientInfo, error)
// JWKS returns the JSON Web Key Set for token verification
// This endpoint provides public keys for validating JWT tokens
JWKS(ctx context.Context) (*JWKSResponse, error)
// Endpoints returns a map of all available OAuth endpoints
// This provides endpoint discovery for clients
Endpoints(ctx context.Context) (map[string]string, error)
// RefreshToken exchanges a refresh token for a new access token
// This allows clients to obtain fresh access tokens without user interaction
RefreshToken(ctx context.Context, refreshToken string, scope string) (*RefreshTokenResponse, error)
// DeviceAuthorization initiates the device authorization flow
// This is used for devices with limited input capabilities
DeviceAuthorization(ctx context.Context, clientID string, scope string) (*DeviceAuthorizationResponse, error)
// UserInfo returns user information for a given access token
// This endpoint provides user profile information in the format defined by the UserProvider
UserInfo(ctx context.Context, accessToken string) (interface{}, error)
// GenerateCodeChallenge generates a code challenge from a code verifier
// This is used for PKCE (Proof Key for Code Exchange) flow
GenerateCodeChallenge(ctx context.Context, codeVerifier string, method string) (string, error)
// ValidateCodeChallenge validates a code verifier against a code challenge
// This verifies the PKCE code challenge during token exchange
ValidateCodeChallenge(ctx context.Context, codeVerifier string, codeChallenge string, method string) error
// PushAuthorizationRequest processes a pushed authorization request
// This implements RFC 9126 for enhanced security
PushAuthorizationRequest(ctx context.Context, request *PushedAuthorizationRequest) (*PushedAuthorizationResponse, error)
// TokenExchange exchanges one token for another token
// This implements RFC 8693 for token exchange scenarios
TokenExchange(ctx context.Context, subjectToken string, subjectTokenType string, audience string, scope string) (*TokenExchangeResponse, error)
// UpdateClient updates an existing OAuth client configuration
// This allows modification of client metadata
UpdateClient(ctx context.Context, clientID string, clientInfo *ClientInfo) (*ClientInfo, error)
// DeleteClient removes an OAuth client from the authorization server
// This permanently deletes the client and invalidates all associated tokens
DeleteClient(ctx context.Context, clientID string) error
// ValidateScope validates requested scopes against available scopes
// This ensures clients only request permitted scopes
ValidateScope(ctx context.Context, requestedScopes []string, clientID string) (*ValidationResult, error)
// GetServerMetadata returns OAuth 2.0 Authorization Server Metadata
// This implements RFC 8414 for server discovery
GetServerMetadata(ctx context.Context) (*AuthorizationServerMetadata, error)
// MCP Requirements
// ValidateResourceParameter validates an OAuth 2.0 resource parameter
// This ensures the resource parameter is valid and properly formatted
ValidateResourceParameter(ctx context.Context, resource string) (*ValidationResult, error)
// GetCanonicalResourceURI returns the canonical form of a resource URI
// This normalizes resource URIs for consistent processing
GetCanonicalResourceURI(ctx context.Context, serverURI string) (string, error)
// GetProtectedResourceMetadata returns OAuth 2.0 Protected Resource Metadata
// This implements RFC 9728 for MCP server discovery
GetProtectedResourceMetadata(ctx context.Context) (*ProtectedResourceMetadata, error)
// HandleWWWAuthenticate processes WWW-Authenticate challenges
// This handles authentication challenges from protected resources
HandleWWWAuthenticate(ctx context.Context, challenge string) (*WWWAuthenticateChallenge, error)
// DynamicClientRegistration handles dynamic client registration
// This implements RFC 7591 for automatic client registration
DynamicClientRegistration(ctx context.Context, request *DynamicClientRegistrationRequest) (*DynamicClientRegistrationResponse, error)
// ValidateStateParameter validates OAuth state parameters
// This prevents CSRF attacks by verifying state parameters
ValidateStateParameter(ctx context.Context, state string, clientID string) (*ValidationResult, error)
// GenerateStateParameter generates a secure state parameter
// This creates cryptographically secure state values for CSRF protection
GenerateStateParameter(ctx context.Context, clientID string) (*StateParameter, error)
// ValidateTokenAudience validates token audience claims
// This ensures tokens are only used with their intended audiences
ValidateTokenAudience(ctx context.Context, token string, expectedAudience string) (*ValidationResult, error)
// MCP Security Requirements
// ValidateRedirectURI validates redirect URIs against registered URIs
// This prevents open redirect attacks by enforcing exact URI matching
ValidateRedirectURI(ctx context.Context, redirectURI string, registeredURIs []string) (*ValidationResult, error)
// RotateRefreshToken rotates a refresh token and invalidates the old one
// This implements refresh token rotation for enhanced security
RotateRefreshToken(ctx context.Context, oldToken string) (*RefreshTokenResponse, error)
// ValidateTokenBinding validates token binding information
// This ensures tokens are bound to the correct client or device
ValidateTokenBinding(ctx context.Context, token string, binding *TokenBinding) (*ValidationResult, error)
}
// UserProvider interface for user information retrieval
type UserProvider interface {
// GetUserByAccessToken retrieves user information using an access token
GetUserByAccessToken(ctx context.Context, accessToken string) (interface{}, error)
// GetUserBySubject retrieves user information using a subject identifier
GetUserBySubject(ctx context.Context, subject string) (interface{}, error)
// ValidateUserScope validates if a user has access to requested scopes
ValidateUserScope(ctx context.Context, userID string, scopes []string) (bool, error)
}

View file

@ -1 +1,234 @@
package oauth
import (
"context"
"time"
"github.com/yaoapp/gou/store"
)
// Service OAuth service
type Service struct {
config *Config
store store.Store
userProvider UserProvider
}
// Config OAuth service configuration
type Config struct {
// Core storage interface
Store store.Store `json:"-"`
// User provider interface
UserProvider UserProvider `json:"-"`
// Certificate and key management
Signing SigningConfig `json:"signing"`
// Token management settings
Token TokenConfig `json:"token"`
// Security configuration
Security SecurityConfig `json:"security"`
// Default client settings
Client ClientConfig `json:"client"`
// Feature flags
Features FeatureFlags `json:"features"`
// OAuth server metadata
IssuerURL string `json:"issuer_url"` // JWT token issuer URL
}
// FeatureFlags represents feature toggle configuration
type FeatureFlags struct {
// OAuth 2.1 features
OAuth21Enabled bool `json:"oauth21_enabled"`
PKCEEnforced bool `json:"pkce_enforced"`
RefreshTokenRotationEnabled bool `json:"refresh_token_rotation_enabled"`
// Advanced features
DeviceFlowEnabled bool `json:"device_flow_enabled"`
TokenExchangeEnabled bool `json:"token_exchange_enabled"`
PushedAuthorizationEnabled bool `json:"pushed_authorization_enabled"`
DynamicClientRegistrationEnabled bool `json:"dynamic_client_registration_enabled"`
// MCP features
MCPComplianceEnabled bool `json:"mcp_compliance_enabled"`
ResourceParameterEnabled bool `json:"resource_parameter_enabled"`
// Security features
TokenBindingEnabled bool `json:"token_binding_enabled"`
MTLSEnabled bool `json:"mtls_enabled"`
DPoPEnabled bool `json:"dpop_enabled"`
// Experimental features
JWTIntrospectionEnabled bool `json:"jwt_introspection_enabled"`
TokenRevocationEnabled bool `json:"token_revocation_enabled"`
UserInfoJWTEnabled bool `json:"userinfo_jwt_enabled"`
}
// NewService creates a new OAuth service with the given configuration
func NewService(config *Config) (*Service, error) {
if config == nil {
return nil, ErrInvalidConfiguration
}
// Set default values if not provided
if err := setConfigDefaults(config); err != nil {
return nil, err
}
// Validate configuration
if err := validateConfig(config); err != nil {
return nil, err
}
// Use UserProvider from config, or create a default one if not provided
userProvider := config.UserProvider
if userProvider == nil {
userProvider = NewDefaultUserProvider(nil, nil, nil)
}
service := &Service{
config: config,
store: config.Store,
userProvider: userProvider,
}
return service, nil
}
// GetConfig returns the service configuration
func (s *Service) GetConfig() *Config {
return s.config
}
// GetUserProvider returns the user provider for the service
func (s *Service) GetUserProvider() UserProvider {
return s.userProvider
}
// setConfigDefaults sets default values for configuration
func setConfigDefaults(config *Config) error {
// Certificate defaults
if config.Signing.SigningAlgorithm == "" {
config.Signing.SigningAlgorithm = "RS256"
}
// Token defaults
if config.Token.AccessTokenLifetime == 0 {
config.Token.AccessTokenLifetime = time.Hour
}
if config.Token.RefreshTokenLifetime == 0 {
config.Token.RefreshTokenLifetime = 24 * time.Hour
}
if config.Token.AuthorizationCodeLifetime == 0 {
config.Token.AuthorizationCodeLifetime = 10 * time.Minute
}
if config.Token.DeviceCodeLifetime == 0 {
config.Token.DeviceCodeLifetime = 15 * time.Minute
}
if config.Token.AccessTokenFormat == "" {
config.Token.AccessTokenFormat = "jwt"
}
if config.Token.RefreshTokenFormat == "" {
config.Token.RefreshTokenFormat = "opaque"
}
// Security defaults
if len(config.Security.PKCECodeChallengeMethod) == 0 {
config.Security.PKCECodeChallengeMethod = []string{"S256"}
}
if config.Security.PKCECodeVerifierLength == 0 {
config.Security.PKCECodeVerifierLength = 128
}
if config.Security.StateParameterLifetime == 0 {
config.Security.StateParameterLifetime = 10 * time.Minute
}
if config.Security.StateParameterLength == 0 {
config.Security.StateParameterLength = 32
}
// Client defaults
if config.Client.DefaultClientType == "" {
config.Client.DefaultClientType = "confidential"
}
if config.Client.DefaultTokenEndpointAuthMethod == "" {
config.Client.DefaultTokenEndpointAuthMethod = "client_secret_basic"
}
if len(config.Client.DefaultGrantTypes) == 0 {
config.Client.DefaultGrantTypes = []string{"authorization_code", "refresh_token"}
}
if len(config.Client.DefaultResponseTypes) == 0 {
config.Client.DefaultResponseTypes = []string{"code"}
}
if config.Client.ClientIDLength == 0 {
config.Client.ClientIDLength = 32
}
if config.Client.ClientSecretLength == 0 {
config.Client.ClientSecretLength = 64
}
// Feature flags defaults - enable OAuth 2.1 features by default
config.Features.OAuth21Enabled = true
config.Features.PKCEEnforced = true
config.Features.RefreshTokenRotationEnabled = true
return nil
}
// validateConfig validates the configuration
func validateConfig(config *Config) error {
if config.Store == nil {
return ErrStoreMissing
}
// Validate issuer URL
if config.IssuerURL == "" {
return ErrIssuerURLMissing
}
// Validate certificate configuration
if config.Signing.SigningCertPath == "" || config.Signing.SigningKeyPath == "" {
return ErrCertificateMissing
}
// Validate token configuration
if config.Token.AccessTokenLifetime <= 0 {
return ErrInvalidTokenLifetime
}
// Validate security configuration
if config.Security.PKCERequired && len(config.Security.PKCECodeChallengeMethod) == 0 {
return ErrPKCEConfigurationInvalid
}
return nil
}
// Error definitions
var (
ErrInvalidConfiguration = &ErrorResponse{Code: "invalid_configuration", ErrorDescription: "Invalid OAuth service configuration"}
ErrStoreMissing = &ErrorResponse{Code: "store_missing", ErrorDescription: "Store is required for OAuth service"}
ErrIssuerURLMissing = &ErrorResponse{Code: "issuer_url_missing", ErrorDescription: "Issuer URL is required for OAuth service"}
ErrCertificateMissing = &ErrorResponse{Code: "certificate_missing", ErrorDescription: "JWT signing certificate and key are required"}
ErrInvalidTokenLifetime = &ErrorResponse{Code: "invalid_token_lifetime", ErrorDescription: "Token lifetime must be greater than 0"}
ErrPKCEConfigurationInvalid = &ErrorResponse{Code: "pkce_configuration_invalid", ErrorDescription: "PKCE configuration is invalid"}
)
// AuthorizationServer returns the authorization server endpoint URL
func (s *Service) AuthorizationServer(ctx context.Context) string {
return s.config.IssuerURL
}
// ProtectedResource returns the protected resource endpoint URL
func (s *Service) ProtectedResource(ctx context.Context) string {
return s.config.IssuerURL
}
// UserInfo returns user information for a given access token
func (s *Service) UserInfo(ctx context.Context, accessToken string) (interface{}, error) {
return s.userProvider.GetUserByAccessToken(ctx, accessToken)
}

555
openapi/oauth/types.go Normal file
View file

@ -0,0 +1,555 @@
package oauth
import (
"time"
)
// ErrorResponse represents an OAuth 2.1 error response
type ErrorResponse struct {
Code string `json:"error"`
ErrorDescription string `json:"error_description,omitempty"`
ErrorURI string `json:"error_uri,omitempty"`
State string `json:"state,omitempty"`
}
// Error implements the error interface
func (e *ErrorResponse) Error() string {
if e.ErrorDescription != "" {
return e.Code + ": " + e.ErrorDescription
}
return e.Code
}
// OAuth 2.1 Grant Types
const (
GrantTypeAuthorizationCode = "authorization_code"
GrantTypeClientCredentials = "client_credentials"
GrantTypeRefreshToken = "refresh_token"
GrantTypeDeviceCode = "urn:ietf:params:oauth:grant-type:device_code"
GrantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange"
)
// OAuth 2.1 Response Types
const (
ResponseTypeCode = "code"
)
// OAuth 2.1 Token Types
const (
TokenTypeBearer = "Bearer"
TokenTypeMAC = "MAC"
TokenTypeDPoP = "DPoP"
)
// OAuth 2.1 Client Types
const (
ClientTypeConfidential = "confidential"
ClientTypePublic = "public"
ClientTypeCredentialed = "credentialed"
)
// PKCE Code Challenge Methods
const (
CodeChallengeMethodS256 = "S256"
CodeChallengeMethodPlain = "plain"
)
// OAuth 2.1 Error Codes
const (
ErrorInvalidRequest = "invalid_request"
ErrorInvalidClient = "invalid_client"
ErrorInvalidGrant = "invalid_grant"
ErrorUnauthorizedClient = "unauthorized_client"
ErrorUnsupportedGrantType = "unsupported_grant_type"
ErrorInvalidScope = "invalid_scope"
ErrorAccessDenied = "access_denied"
ErrorUnsupportedResponseType = "unsupported_response_type"
ErrorServerError = "server_error"
ErrorTemporarilyUnavailable = "temporarily_unavailable"
ErrorInvalidToken = "invalid_token"
ErrorInsufficientScope = "insufficient_scope"
ErrorExpiredToken = "expired_token"
ErrorAuthorizationPending = "authorization_pending"
ErrorSlowDown = "slow_down"
)
// Token Binding Types
const (
TokenBindingTypeDPoP = "dpop"
TokenBindingTypeMTLS = "mtls"
TokenBindingTypeCertificate = "certificate"
)
// Application Types
const (
ApplicationTypeWeb = "web"
ApplicationTypeNative = "native"
)
// Token Endpoint Authentication Methods
const (
TokenEndpointAuthNone = "none"
TokenEndpointAuthPost = "client_secret_post"
TokenEndpointAuthBasic = "client_secret_basic"
TokenEndpointAuthJWT = "client_secret_jwt"
TokenEndpointAuthPrivateKeyJWT = "private_key_jwt"
TokenEndpointAuthTLSClientAuth = "tls_client_auth"
TokenEndpointAuthSelfSignedTLS = "self_signed_tls_client_auth"
)
// Response Modes
const (
ResponseModeQuery = "query"
ResponseModeFragment = "fragment"
ResponseModeFormPost = "form_post"
)
// Standard OAuth Scopes
const (
ScopeOpenID = "openid"
ScopeProfile = "profile"
ScopeEmail = "email"
ScopeAddress = "address"
ScopePhone = "phone"
ScopeOffline = "offline_access"
)
// MCP Specific Constants
const (
MCPResourceParameter = "resource"
MCPBearerTokenHeader = "Authorization"
MCPBearerTokenPrefix = "Bearer "
)
// WWW-Authenticate Schemes
const (
WWWAuthenticateSchemeBearer = "Bearer"
WWWAuthenticateSchemeBasic = "Basic"
WWWAuthenticateSchemeDPoP = "DPoP"
)
// Token represents an OAuth 2.1 access token
type Token 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"`
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at"`
Audience []string `json:"audience,omitempty"`
Subject string `json:"subject,omitempty"`
Issuer string `json:"issuer,omitempty"`
ClientID string `json:"client_id,omitempty"`
}
// RefreshTokenResponse represents the response from refresh token endpoint
type RefreshTokenResponse 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"`
}
// DeviceAuthorizationResponse represents device authorization response
type DeviceAuthorizationResponse struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval,omitempty"`
}
// UserInfo represents user information from userinfo endpoint
type UserInfo struct {
Subject string `json:"sub"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
Nickname string `json:"nickname,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Profile string `json:"profile,omitempty"`
Picture string `json:"picture,omitempty"`
Website string `json:"website,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Gender string `json:"gender,omitempty"`
Birthdate string `json:"birthdate,omitempty"`
Zoneinfo string `json:"zoneinfo,omitempty"`
Locale string `json:"locale,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
PhoneVerified bool `json:"phone_number_verified,omitempty"`
Address *UserAddress `json:"address,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
CustomClaims map[string]interface{} `json:"-"`
}
// UserAddress represents user address information
type UserAddress struct {
Formatted string `json:"formatted,omitempty"`
StreetAddress string `json:"street_address,omitempty"`
Locality string `json:"locality,omitempty"`
Region string `json:"region,omitempty"`
PostalCode string `json:"postal_code,omitempty"`
Country string `json:"country,omitempty"`
}
// ClientInfo represents OAuth client information
type ClientInfo struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"`
ClientName string `json:"client_name,omitempty"`
ClientType string `json:"client_type"` // "confidential", "public", "credentialed"
RedirectURIs []string `json:"redirect_uris"`
ResponseTypes []string `json:"response_types,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
ApplicationType string `json:"application_type,omitempty"`
Contacts []string `json:"contacts,omitempty"`
ClientURI string `json:"client_uri,omitempty"`
LogoURI string `json:"logo_uri,omitempty"`
Scope string `json:"scope,omitempty"`
TosURI string `json:"tos_uri,omitempty"`
PolicyURI string `json:"policy_uri,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
JwksValue string `json:"jwks,omitempty"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
// AuthorizationServerMetadata represents OAuth 2.0 Authorization Server Metadata (RFC 8414)
type AuthorizationServerMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
JwksURI string `json:"jwks_uri,omitempty"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
ServiceDocumentation string `json:"service_documentation,omitempty"`
UILocalesSupported []string `json:"ui_locales_supported,omitempty"`
OpPolicyURI string `json:"op_policy_uri,omitempty"`
OpTosURI string `json:"op_tos_uri,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint,omitempty"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint,omitempty"`
RequirePushedAuthorizationRequests bool `json:"require_pushed_authorization_requests,omitempty"`
DPoPSigningAlgValuesSupported []string `json:"dpop_signing_alg_values_supported,omitempty"`
}
// ProtectedResourceMetadata represents OAuth 2.0 Protected Resource Metadata (RFC 9728)
type ProtectedResourceMetadata struct {
Resource string `json:"resource"`
AuthorizationServers []string `json:"authorization_servers"`
JwksURI string `json:"jwks_uri,omitempty"`
BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
ResourceDocumentation string `json:"resource_documentation,omitempty"`
}
// TokenIntrospectionResponse represents token introspection response
type TokenIntrospectionResponse struct {
Active bool `json:"active"`
Scope string `json:"scope,omitempty"`
ClientID string `json:"client_id,omitempty"`
Username string `json:"username,omitempty"`
TokenType string `json:"token_type,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
Audience []string `json:"aud,omitempty"`
Issuer string `json:"iss,omitempty"`
JwtID string `json:"jti,omitempty"`
}
// PushedAuthorizationRequest represents PAR request
type PushedAuthorizationRequest struct {
ClientID string `json:"client_id"`
ResponseType string `json:"response_type"`
RedirectURI string `json:"redirect_uri"`
Scope string `json:"scope,omitempty"`
State string `json:"state,omitempty"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
Resource string `json:"resource,omitempty"`
RequestURI string `json:"request_uri,omitempty"`
Request string `json:"request,omitempty"`
}
// PushedAuthorizationResponse represents PAR response
type PushedAuthorizationResponse struct {
RequestURI string `json:"request_uri"`
ExpiresIn int `json:"expires_in"`
}
// TokenExchangeResponse represents token exchange response
type TokenExchangeResponse struct {
AccessToken string `json:"access_token"`
IssuedTokenType string `json:"issued_token_type"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in,omitempty"`
Scope string `json:"scope,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
}
// DynamicClientRegistrationRequest represents dynamic client registration request
type DynamicClientRegistrationRequest struct {
RedirectURIs []string `json:"redirect_uris"`
ResponseTypes []string `json:"response_types,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
ApplicationType string `json:"application_type,omitempty"`
Contacts []string `json:"contacts,omitempty"`
ClientName string `json:"client_name,omitempty"`
LogoURI string `json:"logo_uri,omitempty"`
ClientURI string `json:"client_uri,omitempty"`
PolicyURI string `json:"policy_uri,omitempty"`
TosURI string `json:"tos_uri,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
Jwks string `json:"jwks,omitempty"`
Scope string `json:"scope,omitempty"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg,omitempty"`
DefaultMaxAge int `json:"default_max_age,omitempty"`
RequireAuthTime bool `json:"require_auth_time,omitempty"`
DefaultACRValues []string `json:"default_acr_values,omitempty"`
InitiateLoginURI string `json:"initiate_login_uri,omitempty"`
RequestURIs []string `json:"request_uris,omitempty"`
SoftwareID string `json:"software_id,omitempty"`
SoftwareVersion string `json:"software_version,omitempty"`
SoftwareStatement string `json:"software_statement,omitempty"`
}
// DynamicClientRegistrationResponse represents dynamic client registration response
type DynamicClientRegistrationResponse struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"`
ClientSecretExpiresAt int64 `json:"client_secret_expires_at,omitempty"`
RegistrationAccessToken string `json:"registration_access_token,omitempty"`
RegistrationClientURI string `json:"registration_client_uri,omitempty"`
ClientIDIssuedAt int64 `json:"client_id_issued_at,omitempty"`
*DynamicClientRegistrationRequest
}
// WWWAuthenticateChallenge represents WWW-Authenticate challenge
type WWWAuthenticateChallenge struct {
Scheme string `json:"scheme"`
Realm string `json:"realm,omitempty"`
Scope string `json:"scope,omitempty"`
Error string `json:"error,omitempty"`
ErrorDesc string `json:"error_description,omitempty"`
ErrorURI string `json:"error_uri,omitempty"`
Resource string `json:"resource,omitempty"`
Parameters map[string]string `json:"parameters,omitempty"`
}
// StateParameter represents OAuth state parameter
type StateParameter struct {
Value string `json:"value"`
ExpiresAt time.Time `json:"expires_at"`
ClientID string `json:"client_id"`
Nonce string `json:"nonce,omitempty"`
}
// TokenBinding represents token binding information
type TokenBinding struct {
TokenID string `json:"token_id"`
ClientID string `json:"client_id"`
BindingType string `json:"binding_type"` // "dpop", "mtls", "certificate"
BindingValue string `json:"binding_value"`
BindingData map[string]interface{} `json:"binding_data,omitempty"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
// ResourceParameter represents OAuth 2.0 resource parameter
type ResourceParameter struct {
Resource string `json:"resource"`
Canonical string `json:"canonical"`
Audiences []string `json:"audiences,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ValidatedAt time.Time `json:"validated_at"`
}
// ValidationResult represents validation result
type ValidationResult struct {
Valid bool `json:"valid"`
Errors []string `json:"errors,omitempty"`
Details map[string]string `json:"details,omitempty"`
}
// AuthorizationRequest represents authorization request
type AuthorizationRequest struct {
ClientID string `json:"client_id"`
ResponseType string `json:"response_type"`
RedirectURI string `json:"redirect_uri"`
Scope string `json:"scope,omitempty"`
State string `json:"state,omitempty"`
CodeChallenge string `json:"code_challenge,omitempty"`
CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
Resource string `json:"resource,omitempty"`
Nonce string `json:"nonce,omitempty"`
}
// AuthorizationResponse represents authorization response
type AuthorizationResponse struct {
Code string `json:"code,omitempty"`
State string `json:"state,omitempty"`
Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
}
// JWKSResponse represents JWKS response
type JWKSResponse struct {
Keys []JWK `json:"keys"`
}
// JWK represents JSON Web Key
type JWK struct {
Kty string `json:"kty"`
Use string `json:"use,omitempty"`
KeyOps []string `json:"key_ops,omitempty"`
Alg string `json:"alg,omitempty"`
Kid string `json:"kid,omitempty"`
X5U string `json:"x5u,omitempty"`
X5C []string `json:"x5c,omitempty"`
X5T string `json:"x5t,omitempty"`
X5TS256 string `json:"x5t#S256,omitempty"`
// RSA
N string `json:"n,omitempty"`
E string `json:"e,omitempty"`
D string `json:"d,omitempty"`
P string `json:"p,omitempty"`
Q string `json:"q,omitempty"`
DP string `json:"dp,omitempty"`
DQ string `json:"dq,omitempty"`
QI string `json:"qi,omitempty"`
// EC
Crv string `json:"crv,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
// Symmetric
K string `json:"k,omitempty"`
}
// OAuth Service Configuration Types
// SigningConfig represents signing configuration for OAuth service
type SigningConfig struct {
// Token signing certificate and key (for JWT tokens)
SigningCertPath string `json:"signing_cert_path"`
SigningKeyPath string `json:"signing_key_path"`
SigningKeyPassword string `json:"signing_key_password,omitempty"`
SigningAlgorithm string `json:"signing_algorithm"` // RS256, RS384, RS512, ES256, ES384, ES512
// Token verification certificates (for token validation)
VerificationCerts []string `json:"verification_certs,omitempty"`
// mTLS client certificate validation
MTLSClientCACertPath string `json:"mtls_client_ca_cert_path,omitempty"`
MTLSEnabled bool `json:"mtls_enabled"`
// Certificate rotation settings
CertRotationEnabled bool `json:"cert_rotation_enabled"`
CertRotationInterval time.Duration `json:"cert_rotation_interval"`
}
// TokenConfig represents token-related configuration
type TokenConfig struct {
// Access token settings
AccessTokenLifetime time.Duration `json:"access_token_lifetime"` // 1h
AccessTokenFormat string `json:"access_token_format"` // jwt, opaque
AccessTokenSigningAlg string `json:"access_token_signing_alg"` // RS256
// Refresh token settings
RefreshTokenLifetime time.Duration `json:"refresh_token_lifetime"` // 24h
RefreshTokenRotation bool `json:"refresh_token_rotation"` // true for OAuth 2.1
RefreshTokenFormat string `json:"refresh_token_format"` // opaque, jwt
// Authorization code settings
AuthorizationCodeLifetime time.Duration `json:"authorization_code_lifetime"` // 10m
AuthorizationCodeLength int `json:"authorization_code_length"` // 32
// Device code settings
DeviceCodeLifetime time.Duration `json:"device_code_lifetime"` // 15m
DeviceCodeLength int `json:"device_code_length"` // 8
UserCodeLength int `json:"user_code_length"` // 8
DeviceCodeInterval time.Duration `json:"device_code_interval"` // 5s
// Token binding settings
TokenBindingEnabled bool `json:"token_binding_enabled"`
SupportedBindingTypes []string `json:"supported_binding_types"` // dpop, mtls
// Token audience settings
DefaultAudience []string `json:"default_audience"`
AudienceValidationMode string `json:"audience_validation_mode"` // strict, relaxed
}
// SecurityConfig represents security-related configuration
type SecurityConfig struct {
// PKCE settings (mandatory for OAuth 2.1)
PKCERequired bool `json:"pkce_required"` // true for OAuth 2.1
PKCECodeChallengeMethod []string `json:"pkce_code_challenge_method"` // S256
PKCECodeVerifierLength int `json:"pkce_code_verifier_length"` // 128
// State parameter settings
StateParameterRequired bool `json:"state_parameter_required"`
StateParameterLifetime time.Duration `json:"state_parameter_lifetime"` // 10m
StateParameterLength int `json:"state_parameter_length"` // 32
// Rate limiting
RateLimitEnabled bool `json:"rate_limit_enabled"`
RateLimitRequests int `json:"rate_limit_requests"` // requests per window
RateLimitWindow time.Duration `json:"rate_limit_window"` // 1m
RateLimitByClientID bool `json:"rate_limit_by_client_id"`
// Brute force protection
BruteForceProtectionEnabled bool `json:"brute_force_protection_enabled"`
MaxFailedAttempts int `json:"max_failed_attempts"` // 5
LockoutDuration time.Duration `json:"lockout_duration"` // 15m
// Encryption settings
EncryptionKey string `json:"encryption_key"` // for encrypting sensitive data
EncryptionAlgorithm string `json:"encryption_algorithm"` // AES-256-GCM
// Additional security features
IPWhitelist []string `json:"ip_whitelist,omitempty"`
IPBlacklist []string `json:"ip_blacklist,omitempty"`
RequireHTTPS bool `json:"require_https"`
DisableUnsecureEndpoints bool `json:"disable_unsecure_endpoints"`
}
// ClientConfig represents default client configuration
type ClientConfig struct {
// Default client settings
DefaultClientType string `json:"default_client_type"` // confidential, public
DefaultTokenEndpointAuthMethod string `json:"default_token_endpoint_auth_method"` // client_secret_basic, client_secret_post, private_key_jwt
DefaultGrantTypes []string `json:"default_grant_types"` // authorization_code, refresh_token
DefaultResponseTypes []string `json:"default_response_types"` // code
DefaultScopes []string `json:"default_scopes"` // openid, profile, email
// Client validation settings
ClientIDLength int `json:"client_id_length"` // 32
ClientSecretLength int `json:"client_secret_length"` // 64
ClientSecretLifetime time.Duration `json:"client_secret_lifetime"` // 0 (never expires)
// Dynamic client registration
DynamicRegistrationEnabled bool `json:"dynamic_registration_enabled"`
AllowedRedirectURISchemes []string `json:"allowed_redirect_uri_schemes"` // https, http (for dev)
AllowedRedirectURIHosts []string `json:"allowed_redirect_uri_hosts"` // localhost (for dev)
// Client certificate settings
ClientCertificateRequired bool `json:"client_certificate_required"`
ClientCertificateValidation string `json:"client_certificate_validation"` // none, optional, required
}

50
openapi/oauth/user.go Normal file
View file

@ -0,0 +1,50 @@
package oauth
import (
"context"
)
// DefaultUserProvider provides a default implementation of UserProvider
type DefaultUserProvider struct {
getUserByAccessTokenFunc func(ctx context.Context, accessToken string) (interface{}, error)
getUserBySubjectFunc func(ctx context.Context, subject string) (interface{}, error)
validateUserScopeFunc func(ctx context.Context, userID string, scopes []string) (bool, error)
}
// NewDefaultUserProvider creates a new DefaultUserProvider with the given functions
func NewDefaultUserProvider(
getUserByAccessTokenFunc func(ctx context.Context, accessToken string) (interface{}, error),
getUserBySubjectFunc func(ctx context.Context, subject string) (interface{}, error),
validateUserScopeFunc func(ctx context.Context, userID string, scopes []string) (bool, error),
) *DefaultUserProvider {
return &DefaultUserProvider{
getUserByAccessTokenFunc: getUserByAccessTokenFunc,
getUserBySubjectFunc: getUserBySubjectFunc,
validateUserScopeFunc: validateUserScopeFunc,
}
}
// GetUserByAccessToken retrieves user information using an access token
func (p *DefaultUserProvider) GetUserByAccessToken(ctx context.Context, accessToken string) (interface{}, error) {
if p.getUserByAccessTokenFunc == nil {
return nil, &ErrorResponse{Code: "not_implemented", ErrorDescription: "GetUserByAccessToken is not implemented"}
}
return p.getUserByAccessTokenFunc(ctx, accessToken)
}
// GetUserBySubject retrieves user information using a subject identifier
func (p *DefaultUserProvider) GetUserBySubject(ctx context.Context, subject string) (interface{}, error) {
if p.getUserBySubjectFunc == nil {
return nil, &ErrorResponse{Code: "not_implemented", ErrorDescription: "GetUserBySubject is not implemented"}
}
return p.getUserBySubjectFunc(ctx, subject)
}
// ValidateUserScope validates if a user has access to requested scopes
func (p *DefaultUserProvider) ValidateUserScope(ctx context.Context, userID string, scopes []string) (bool, error) {
if p.validateUserScopeFunc == nil {
// Default implementation: allow all scopes
return true, nil
}
return p.validateUserScopeFunc(ctx, userID, scopes)
}