app

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package app holds Conduit's usecases. The token lifecycle is the core: it seals provider secrets into the vault, vends them (refreshing on read when near expiry), and refreshes ahead of expiry from a cron, flipping a connection to NEEDS_REAUTH and notifying when its refresh token dies.

Index

Constants

This section is empty.

Variables

View Source
var ErrNeedsReauth = errors.New("conduit: connection needs re-authorization")

ErrNeedsReauth means a connection's refresh token is dead; the owner must re-authorize the connector. The connection is flipped to NEEDS_REAUTH.

View Source
var ErrRevoked = errors.New("conduit: connection revoked")

ErrRevoked means the connection was revoked and can no longer vend secrets.

Functions

func DefaultConnectors

func DefaultConnectors() []domain.Connector

DefaultConnectors is the built-in provider catalog. Rate profiles default to each provider's published per-account API ceiling so all orgs' calls stay under it (enforced via kit/ratelimit).

func NewHTTPExchanger

func NewHTTPExchanger(client *httpclient.Client, limiter ratelimit.Limiter, secrets ClientSecrets, opts ...ExchangerOption) *httpExchanger

func NewLogNotifier

func NewLogNotifier() *logNotifier

Types

type ClientSecrets

type ClientSecrets interface {
	ClientCredentials(slug string) (id, secret string, ok bool)
}

ClientSecrets supplies a connector's OAuth app credentials (client id and secret) — deployment secrets, not part of the in-code connector catalog.

func NewEnvClientSecrets

func NewEnvClientSecrets() ClientSecrets

type ConnectionRepository

ConnectionRepository persists connections via kit generics.

type ConnectionStore

type ConnectionStore interface {
	Get(ctx context.Context, opts ...search.Option) (domain.Connection, error)
	SetStatus(ctx context.Context, id string, status domain.ConnectionStatus) error
}

ConnectionStore reads connections and flips their lifecycle status.

type ConnectionUsecase

ConnectionUsecase is the management surface for connections. Reads and deletes flow through the kit generic handlers (List filtered by the caller's owner); Create validates the connector exists and requires an owner.

func NewConnectionUsecase

func NewConnectionUsecase(conns ConnectionRepository, registry ConnectorRegistry) ConnectionUsecase

type ConnectorCatalog

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

ConnectorCatalog adapts the registry to a kit Lister so /api/connectors can reuse the generic JSON:API list handler.

func NewConnectorCatalog

func NewConnectorCatalog(registry ConnectorRegistry) *ConnectorCatalog

func (*ConnectorCatalog) List

List returns the full catalog; the static set ignores search options.

type ConnectorRegistry

type ConnectorRegistry interface {
	Lookup(slug string) (domain.Connector, bool)
	List() []domain.Connector
}

ConnectorRegistry resolves connector definitions; Lookup serves the token lifecycle, List serves the read-only /api/connectors catalog.

func NewConnectorRegistry

func NewConnectorRegistry(connectors ...domain.Connector) ConnectorRegistry

NewConnectorRegistry builds a frozen registry from the given connectors.

func NewDefaultConnectorRegistry

func NewDefaultConnectorRegistry() ConnectorRegistry

NewDefaultConnectorRegistry builds the registry from the built-in catalog.

type CredentialStore

type CredentialStore interface {
	Get(ctx context.Context, opts ...search.Option) (domain.Credential, error)
	Create(ctx context.Context, c domain.Credential) (domain.Credential, error)
	Replace(ctx context.Context, id string, ciphertext, wrappedKey []byte, keyID string, expiresAt *time.Time) error
	ListDueForRefresh(ctx context.Context, before time.Time, limit int) ([]domain.Credential, error)
}

CredentialStore persists sealed credentials and finds those due for refresh.

type ExchangerOption

type ExchangerOption func(*httpExchanger)

func WithExchangerClock

func WithExchangerClock(now func() time.Time) ExchangerOption

WithExchangerClock overrides the time source (tests).

type ReauthNotifier

type ReauthNotifier interface {
	NeedsReauth(ctx context.Context, conn domain.Connection) error
}

ReauthNotifier is told when a connection needs re-authorization (→ Herald).

type Sealer

type Sealer interface {
	Seal(ctx context.Context, plaintext []byte) (ciphertext, wrappedKey []byte, err error)
	Open(ctx context.Context, ciphertext, wrappedKey []byte) ([]byte, error)
	KeyID() string
}

Sealer is the envelope vault; implemented by *vault.Vault.

type Secret

type Secret struct {
	AccessToken  string     `json:"accessToken,omitempty"`
	RefreshToken string     `json:"refreshToken,omitempty"`
	APIKey       string     `json:"apiKey,omitempty"`
	APISecret    string     `json:"apiSecret,omitempty"`
	ExpiresAt    *time.Time `json:"expiresAt,omitempty"`
}

Secret is the plaintext credential material sealed in the vault. Only the fields relevant to a connector's AuthType are populated.

type TokenExchanger

type TokenExchanger interface {
	Refresh(ctx context.Context, connector domain.Connector, refreshToken string) (Secret, error)
}

TokenExchanger performs the provider-side OAuth refresh. It returns ErrNeedsReauth when the refresh token itself is rejected.

type TokenOption

type TokenOption func(*TokenUsecase)

func WithClock

func WithClock(now func() time.Time) TokenOption

WithClock overrides the time source (tests).

func WithRefreshLead

func WithRefreshLead(d time.Duration) TokenOption

WithRefreshLead sets how far before expiry a token is eagerly refreshed.

type TokenUsecase

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

TokenUsecase orchestrates the token lifecycle.

func NewTokenUsecase

func NewTokenUsecase(
	conns ConnectionStore,
	creds CredentialStore,
	vault Sealer,
	registry ConnectorRegistry,
	exchanger TokenExchanger,
	notifier ReauthNotifier,
	opts ...TokenOption,
) *TokenUsecase

func (*TokenUsecase) RefreshDue

func (u *TokenUsecase) RefreshDue(ctx context.Context, limit int) (int, error)

RefreshDue refreshes up to limit credentials that fall within the refresh lead, skipping non-ACTIVE connections. Intended to be driven by a cron so the vend hot path almost always finds a warm token. Returns the count refreshed.

func (*TokenUsecase) StoreCredential

func (u *TokenUsecase) StoreCredential(ctx context.Context, connectionID string, kind domain.CredentialKind, secret Secret) (domain.Credential, error)

StoreCredential seals a secret and persists it as the connection's credential.

func (*TokenUsecase) Vend

func (u *TokenUsecase) Vend(ctx context.Context, owner, connectionID string) (Secret, error)

Vend returns the connection's current secret, refreshing on read if it is within the refresh lead of expiry. This is the hot path; a warm credential returns after a single decrypt with no provider round-trip. The owner must match the connection's owner — a foreign owner gets a not-found, never a secret.

Jump to

Keyboard shortcuts

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