oauth

package
v0.4.0-rc.21 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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")
)
View Source
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.

View Source
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 EnsureConnectionToken added in v0.4.0

func EnsureConnectionToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, connectionID pgtype.UUID, refreshIfBefore time.Time) (string, error)

EnsureConnectionToken returns a valid plaintext access token for the connection, refreshing it under a row lock when it expires at/before refreshIfBefore. Pass time.Now() (plus a skew) for on-demand use at request time, or now()+buffer for pre-emptive background refresh. Multi-replica safe: it locks the connection row and re-checks expiry inside the txn, so concurrent callers serialize and see the freshly written token instead of double-refreshing. Returns ErrNeedsReauth when re-authorization is required; a wrapped error for transient refresh failures.

func EnsureMCPServerToken added in v0.4.0

func EnsureMCPServerToken(ctx context.Context, database *db.DB, enc secrets.Store, client *Client, logger *zap.Logger, serverID pgtype.UUID, refreshIfBefore time.Time) (string, error)

EnsureMCPServerToken is the MCP-server analogue of EnsureConnectionToken, operating on the agent_mcp_servers table and the "mcp" secret namespace.

func GeneratePKCE

func GeneratePKCE() (verifier, challenge string, err error)

GeneratePKCE generates a PKCE code verifier and S256 challenge.

func GenerateState

func GenerateState() (string, error)

GenerateState generates a random state token for CSRF protection.

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 NewClient

func NewClient() *Client

NewClient creates a new OAuth client.

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:

  1. Fetch Protected Resource Metadata (RFC 9728)
  2. Fetch Authorization Server Metadata (RFC 8414)
  3. Validate PKCE support

type OAuthError

type OAuthError struct {
	Code        string // e.g. "invalid_grant"
	Description string
}

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 added in v0.2.8

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 added in v0.2.8

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 added in v0.2.8

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.

type TokenResponse

type TokenResponse struct {
	AccessToken  string
	RefreshToken string
	TokenType    string
	ExpiresIn    int64 // seconds
}

TokenResponse represents the response from an OAuth token endpoint.

Jump to

Keyboard shortcuts

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