Documentation
¶
Overview ¶
Package oauth provides OAuth 2.0 PKCE support for Airlock credential management. Ported from gateway/internal/tokenbroker — simplified to flat params, no discovery.
Index ¶
- Variables
- func CanonicalScopeSet(scopes []string) string
- func CanonicalScopes(raw string) string
- func CoversScopes(required, granted string) bool
- func EnsureConnectionToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, ...) (string, error)
- func EnsureMCPServerToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, ...) (string, error)
- func GeneratePKCE() (verifier, challenge string, err error)
- func GenerateState() (string, error)
- func MissingScopes(required, granted string) []string
- func RefreshConnectionTokenIfEligible(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, ...) (bool, error)
- func RefreshMCPServerTokenIfEligible(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, ...) (bool, error)
- func ScopeSet(scopes ...string) []string
- func Scopes(raw string) []string
- func UnionScopes(declarations ...string) string
- func ValidateAuthParams(params map[string]string) error
- func ValidatePKCESupport(meta *AuthServerMeta) error
- type AuthServerMeta
- type Client
- func (c *Client) BuildAuthURL(authURL, clientID, redirectURI, state, codeChallenge, scopes string, ...) (string, error)
- func (c *Client) ExchangeCode(ctx context.Context, ...) (*TokenResponse, error)
- func (c *Client) RefreshToken(ctx context.Context, tokenURL, refreshToken, clientID, clientSecret string) (*TokenResponse, error)
- type DiscoveryResult
- type OAuthError
- type ProtectedResourceMeta
- type RefreshJob
- type RegisterClientRequest
- type RegisterClientResponse
- type TokenResponse
Constants ¶
This section is empty.
Variables ¶
var ( // ErrDiscoveryFailed indicates that metadata discovery did not succeed. ErrDiscoveryFailed = errors.New("metadata discovery failed") // ErrPKCENotSupported indicates the authorization server does not support S256 PKCE. ErrPKCENotSupported = errors.New("authorization server does not support S256 PKCE") )
var ErrDCRUnsupported = errors.New("server does not advertise an RFC 7591 registration endpoint")
ErrDCRUnsupported is returned when the registration endpoint is empty (the server didn't advertise one in its discovery metadata). The operator's only path forward is to switch auth_mode to `oauth` and paste credentials manually.
var ErrNeedsReauth = errors.New("oauth: re-authorization required")
ErrNeedsReauth means the caller must direct the user to re-authorize: the connection has no access token, no refresh token to recover with, or the provider revoked the grant (in which case the stored credentials are cleared so the UI reflects the disconnected state). Distinct from a transient refresh failure, which is returned as a wrapped error.
Functions ¶
func CanonicalScopeSet ¶ added in v0.4.0
CanonicalScopeSet returns the canonical form of a scope slice.
func CanonicalScopes ¶ added in v0.4.0
CanonicalScopes returns the persisted and authorization-URL form of scopes.
func CoversScopes ¶ added in v0.4.0
CoversScopes reports whether granted contains every required scope.
func EnsureConnectionToken ¶ added in v0.4.0
func EnsureConnectionToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, agentID pgtype.UUID, needSlug string, connectionID pgtype.UUID, refreshIfBefore time.Time) (string, error)
EnsureConnectionToken resolves a token for an already-resolved runtime binding. It locks and rereads the target need before the resource row, so a concurrent sync scope expansion is observed before a valid token is returned.
func EnsureMCPServerToken ¶ added in v0.4.0
func EnsureMCPServerToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, agentID pgtype.UUID, needSlug string, serverID pgtype.UUID, refreshIfBefore time.Time) (string, error)
EnsureMCPServerToken resolves a token for an already-resolved runtime binding with the same locked need recheck as EnsureConnectionToken.
func GeneratePKCE ¶
GeneratePKCE generates a PKCE code verifier and S256 challenge.
func GenerateState ¶
GenerateState generates a random state token for CSRF protection.
func MissingScopes ¶ added in v0.4.0
MissingScopes returns required scopes absent from granted.
func RefreshConnectionTokenIfEligible ¶ added in v0.4.0
func RefreshConnectionTokenIfEligible(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, connectionID pgtype.UUID, refreshIfBefore time.Time) (bool, error)
RefreshConnectionTokenIfEligible refreshes only while at least one active, scope-ready binding remains locked in the same transaction.
func RefreshMCPServerTokenIfEligible ¶ added in v0.4.0
func RefreshMCPServerTokenIfEligible(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, serverID pgtype.UUID, refreshIfBefore time.Time) (bool, error)
RefreshMCPServerTokenIfEligible is the background-refresh MCP equivalent.
func Scopes ¶ added in v0.4.0
Scopes parses an OAuth scope declaration as a set. Agent declarations and persisted rows may use either delimited text or JSON arrays.
func UnionScopes ¶ added in v0.4.0
UnionScopes returns the canonical union of every supplied declaration.
func ValidateAuthParams ¶ added in v0.4.0
ValidateAuthParams rejects parameters that could replace OAuth's identity, callback, CSRF, response type, or PKCE binding.
func ValidatePKCESupport ¶
func ValidatePKCESupport(meta *AuthServerMeta) error
ValidatePKCESupport checks that the authorization server supports S256 PKCE.
Types ¶
type AuthServerMeta ¶
type AuthServerMeta struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}
AuthServerMeta represents RFC 8414 Authorization Server Metadata.
func FetchAuthServerMetadata ¶
func FetchAuthServerMetadata(ctx context.Context, httpClient *http.Client, authServerURL string) (*AuthServerMeta, error)
FetchAuthServerMetadata fetches RFC 8414 Authorization Server Metadata. Tries /.well-known/oauth-authorization-server first, then falls back to /.well-known/openid-configuration.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client handles OAuth 2.0 operations.
func (*Client) BuildAuthURL ¶
func (c *Client) BuildAuthURL(authURL, clientID, redirectURI, state, codeChallenge, scopes string, extraParams map[string]string) (string, error)
BuildAuthURL constructs an OAuth 2.0 authorization URL with PKCE. extraParams are provider-specific query parameters merged in last — e.g. access_type=offline to request a refresh token.
func (*Client) ExchangeCode ¶
func (c *Client) ExchangeCode(ctx context.Context, tokenURL, code, codeVerifier, redirectURI, clientID, clientSecret string) (*TokenResponse, error)
ExchangeCode exchanges an authorization code for tokens.
func (*Client) RefreshToken ¶
func (c *Client) RefreshToken(ctx context.Context, tokenURL, refreshToken, clientID, clientSecret string) (*TokenResponse, error)
RefreshToken uses a refresh token to obtain a new access token.
type DiscoveryResult ¶
type DiscoveryResult struct {
ResourceURI string
AuthorizationURL string
TokenURL string
RegistrationEndpoint string // RFC 7591 dynamic client registration endpoint
ScopesSupported []string
}
DiscoveryResult contains the result of upstream OAuth discovery.
func DiscoverUpstream ¶
func DiscoverUpstream(ctx context.Context, httpClient *http.Client, serverURL string) (*DiscoveryResult, error)
DiscoverUpstream orchestrates the full upstream discovery flow:
type OAuthError ¶
OAuthError represents an error returned by the OAuth provider.
func (*OAuthError) Error ¶
func (e *OAuthError) Error() string
type ProtectedResourceMeta ¶
type ProtectedResourceMeta struct {
Resource string `json:"resource"`
AuthorizationServers []string `json:"authorization_servers"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
}
ProtectedResourceMeta represents RFC 9728 Protected Resource Metadata.
func DiscoverProtectedResource ¶
func DiscoverProtectedResource(ctx context.Context, httpClient *http.Client, serverURL string) (*ProtectedResourceMeta, error)
DiscoverProtectedResource fetches RFC 9728 Protected Resource Metadata. Tries /.well-known/oauth-protected-resource/{path} first, then falls back to root.
type RefreshJob ¶
type RefreshJob struct {
// contains filtered or unexported fields
}
RefreshJob refreshes OAuth tokens before they expire.
func NewRefreshJob ¶
func NewRefreshJob(database *db.DB, encryptor secrets.Store, client *Client, logger *zap.Logger) *RefreshJob
NewRefreshJob creates a RefreshJob with default interval (5 min) and buffer (10 min).
func (*RefreshJob) Run ¶
func (j *RefreshJob) Run(ctx context.Context)
Run starts the background refresh loop. Blocks until ctx is cancelled. Runs an immediate refresh on startup so tokens that expired while the process was down are caught without waiting for the first tick.
type RegisterClientRequest ¶
type RegisterClientRequest struct {
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,omitempty"`
Scope string `json:"scope,omitempty"`
}
RegisterClientRequest is the body posted to the registration endpoint per RFC 7591 §2. We send the minimal set: a name (informational), a single redirect URI, the grant types we'll actually use, and the requested scopes when known. Anything else is server-default.
type RegisterClientResponse ¶
type RegisterClientResponse struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"` // empty for public clients
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
}
RegisterClientResponse covers the RFC 7591 §3.2.1 response fields we actually consume. Any others (client_id_issued_at, expires_at, registration_access_token, …) are deliberately ignored — we don't support runtime registration management in v1, so we treat the returned client_id as opaque-and-permanent until the operator hits "Reconfigure" to register a new one.
func RegisterClient ¶
func RegisterClient(ctx context.Context, httpClient *http.Client, registrationEndpoint, clientName, redirectURI, scope string) (*RegisterClientResponse, error)
RegisterClient performs RFC 7591 dynamic client registration against the given registration endpoint. Returns the issued client_id (and client_secret if the server treats us as a confidential client).
Errors:
- ErrDCRUnsupported when registrationEndpoint is empty.
- Wrapped HTTP error (status + body) when the server returns >= 400 so the operator sees the server's actual rejection reason.