Help us shape the future of Go! Take the 2026 Go Developer Survey

link

package
v0.24.1 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: Apache-2.0 Imports: 38 Imported by: 0

Documentation

Overview

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.

Index

Constants

View Source
const DelegatedProviderKeyLength = len(DelegatedProviderKeyPrefix) + sha256.Size*2

DelegatedProviderKeyLength is the fixed length of a delegated storage key: len("mcp:v1:") + 64 hex characters of an untruncated SHA-256 digest.

View Source
const DelegatedProviderKeyPrefix = "mcp:v1:"

DelegatedProviderKeyPrefix marks provider column values holding delegated (per-MCP-provider) tokens rather than workspace provider names.

Variables

View Source
var ErrOAuthStateInvalid = errors.New("oauth link state is invalid")

Functions

func Bearer

func Bearer(ctx context.Context) string

func CanonicalUserID

func CanonicalUserID(ctx context.Context) string

CanonicalUserID reads only the host-established canonical identity.

func CanonicalWorkspaceProvider

func CanonicalWorkspaceProvider(cfg *Config, provider string) string

CanonicalWorkspaceProvider normalizes trusted workspace provider aliases ("", "jwt", "oauth", "default") to the configured workspace provider name. Delegated storage keys and unknown provider names pass through unchanged.

func DelegatedProviderStorageKey

func DelegatedProviderStorageKey(workspaceNamespace, providerRef string) string

DelegatedProviderStorageKey derives the fixed-length, globally unambiguous user_oauth_token.provider value for a delegated provider reference:

mcp:v1:<hex sha256(workspaceNamespace + NUL + providerRef)>

The digest is never truncated. workspaceNamespace is an immutable configured identifier; changing it requires an explicit token-key migration.

func EffectiveUserID

func EffectiveUserID(ctx context.Context) string

EffectiveUserID reads the host-established presentation identity for token partitioning.

func IDToken

func IDToken(ctx context.Context) string

IDToken returns only the original ID token stored by trusted ingress. It never falls back to an access token; user-info ACL facts require the former.

func InjectTokens

func InjectTokens(ctx context.Context, tokens *scyauth.Token) context.Context

InjectTokens stores OAuth tokens in context so that MCPAuthToken and downstream MCP clients can forward the logged-in user's token. External auth middleware (outside this module) should call this after authenticating the user from a session or JWT.

func InjectUser

func InjectUser(ctx context.Context, subject string) context.Context

InjectUser carries host-supplied user identity for the delegated credential resolver. External middleware (outside this module) can call this to bridge their own auth context into the context key that EffectiveUserID reads.

func IsDelegatedProviderKey

func IsDelegatedProviderKey(provider string) bool

IsDelegatedProviderKey reports whether a provider column value is a delegated storage key. Delegated rows require exact lookup and per-provider broker routing; they must never be served by workspace fallback or sent to the workspace refresh broker.

func IsWorkspaceProviderAlias

func IsWorkspaceProviderAlias(cfg *Config, provider string) bool

IsWorkspaceProviderAlias reports whether provider names the workspace identity provider (directly or through a trusted legacy alias).

func MCPAuthToken

func MCPAuthToken(ctx context.Context, useIDToken bool) string

MCPAuthToken selects a single token string suitable for outbound MCP calls. Delegates to the internal auth package.

func OAuthScopesForHeadless

func OAuthScopesForHeadless(client *OAuthClient) []string

OAuthScopesForHeadless returns the grant a non-interactive user-owned runtime should request. A dedicated CLI scope wins; otherwise legacy browser-created schedules inherit the web scope, with mobile as the final compatibility fallback.

func Provider

func Provider(ctx context.Context) string

func TokensFromContext

func TokensFromContext(ctx context.Context) *scyauth.Token

func ValidateOAuthTokenScopes

func ValidateOAuthTokenScopes(expected []string, token *scyauth.Token) error

ValidateOAuthTokenScopes validates a newly obtained OAuth token against the exact scopes requested for that authorization flow.

func VerifiedProvider

func VerifiedProvider(ctx context.Context) string

VerifiedProvider reads the provider identifier established by host ingress.

func WithBearer

func WithBearer(ctx context.Context, value string) context.Context

func WithCanonicalUserID

func WithCanonicalUserID(ctx context.Context, value string) context.Context

func WithIDToken

func WithIDToken(ctx context.Context, value string) context.Context

func WithProvider

func WithProvider(ctx context.Context, value string) context.Context

func WithTokens

func WithTokens(ctx context.Context, tokens *scyauth.Token) context.Context

WithTokens stores only the token bundle. It preserves independent ingress bearer/ID-token values; InjectTokens is the explicit ingress convenience API.

func WithUserInfo

func WithUserInfo(ctx context.Context, info *UserInfo) context.Context

func WithoutTokens

func WithoutTokens(ctx context.Context) context.Context

Types

type Config

type Config struct {
	OnCredentialChange         func(MCPAuthChangeEvent) `json:"-" yaml:"-"`
	WorkspaceNamespace         string                   `json:"workspaceNamespace,omitempty" yaml:"workspaceNamespace,omitempty"`
	TokenEncryptionKey         string                   `json:"tokenEncryptionKey,omitempty" yaml:"tokenEncryptionKey,omitempty"`
	StateEncryptionKey         string                   `json:"stateEncryptionKey,omitempty" yaml:"stateEncryptionKey,omitempty"`
	StateEncryptionKeyPrevious string                   `json:"stateEncryptionKeyPrevious,omitempty" yaml:"stateEncryptionKeyPrevious,omitempty"`
	MCPLinkStateTTLMinutes     int                      `json:"mcpLinkStateTTLMinutes,omitempty" yaml:"mcpLinkStateTTLMinutes,omitempty"`
	// OAuth preserves legacy configURL-derived key selection during migration.
	OAuth *OAuth `json:"oauth,omitempty" yaml:"oauth,omitempty"`
}

Config configures the MCP authorization-code and delegated credential lifecycle. It does not configure application ingress, users, policies or login sessions.

func (*Config) DelegatedTokenEncryptionSalt

func (c *Config) DelegatedTokenEncryptionSalt() string

type DelegatedCredentialResolver

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

DelegatedCredentialResolver implements the viant/mcp CredentialResolver for Agently: canonical-user-keyed encrypted storage, validated workspace-token reuse, provider-exact refresh with distributed leases and CAS, and safe invalidation. Resolution returns outbound credentials only — it never installs values into the Agently authentication context.

func NewDelegatedCredentialResolver

func NewDelegatedCredentialResolver(cfg *Config, store DelegatedTokenStore, registry *providerregistry.Registry, namespace string) *DelegatedCredentialResolver

NewDelegatedCredentialResolver builds the resolver. namespace is the immutable workspace namespace used in storage-key derivation.

func (*DelegatedCredentialResolver) Invalidate

func (r *DelegatedCredentialResolver) Invalidate(ctx context.Context, requirement authcfg.Requirement) error

Invalidate implements config.CredentialResolver. It deletes only the exact canonical-user/provider row. Unknown providers or a missing canonical owner must never delete credentials.

func (*DelegatedCredentialResolver) MaxRefreshLead

func (r *DelegatedCredentialResolver) MaxRefreshLead(ctx context.Context) time.Duration

MaxRefreshLead implements DelegatedTokenRefresher.

func (*DelegatedCredentialResolver) Refresh

Refresh implements config.CredentialResolver. Per contract it bypasses any cached access token and mints a fresh credential while retaining the stored refresh credential.

func (*DelegatedCredentialResolver) RefreshStoredDelegatedToken

func (r *DelegatedCredentialResolver) RefreshStoredDelegatedToken(ctx context.Context, stored *OAuthToken) error

RefreshStoredDelegatedToken implements DelegatedTokenRefresher for the background watcher. Unknown providers, malformed metadata and disabled providers are skipped without modifying stored credentials; provider failures are isolated to the row.

func (*DelegatedCredentialResolver) Resolve

Resolve implements config.CredentialResolver.

type DelegatedMCPAuth

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

type DelegatedTokenLister

type DelegatedTokenLister interface {
	ListDelegated(ctx context.Context, userID string) ([]*OAuthToken, error)
}

DelegatedTokenLister is the optional listing surface used by the user-lifecycle cleanup hook to enumerate a canonical user's delegated rows.

type DelegatedTokenRefresher

type DelegatedTokenRefresher interface {
	// RefreshStoredDelegatedToken applies the row's provider-specific refresh
	// policy and, when due, refreshes through that provider's exact
	// client/token endpoint using the distributed lease and CAS write.
	RefreshStoredDelegatedToken(ctx context.Context, stored *OAuthToken) error
	// MaxRefreshLead returns the largest configured provider refresh lead used
	// as the watcher's broad scan horizon.
	MaxRefreshLead(ctx context.Context) time.Duration
}

DelegatedTokenRefresher lets the background watcher refresh delegated rows through their exact provider broker. Until installed, delegated rows are skipped without mutation.

type DelegatedTokenStore

type DelegatedTokenStore interface {
	GetExact(ctx context.Context, userID, provider string) (*OAuthToken, error)
	Put(ctx context.Context, token *OAuthToken) error
	Delete(ctx context.Context, userID, provider string) error
	TryAcquireRefreshLease(ctx context.Context, userID, provider, owner string, ttl time.Duration) (version int64, acquired bool, err error)
	ReleaseRefreshLease(ctx context.Context, userID, provider, owner string) error
	CASPut(ctx context.Context, token *OAuthToken, expectedVersion int64, owner string) (swapped bool, err error)
}

DelegatedTokenStore is the exact, canonical-user-keyed persistence surface used for delegated MCP credentials. It deliberately excludes the legacy fallback Get: delegated storage keys require exact matches and delegated owner resolution uses CanonicalUserID directly, never resolveOAuthTokenOwnerID.

type ExpiringTokenScanner

type ExpiringTokenScanner interface {
	// ScanExpiring returns all stored tokens whose expiry is before horizon
	// and that carry a refresh token. Only tokens that can actually be
	// refreshed need to be returned.
	ScanExpiring(ctx context.Context, horizon time.Time) ([]*OAuthToken, error)
}

ExpiringTokenScanner is an optional extension of TokenStore for implementations that can efficiently query the store for tokens expiring before a given horizon. It is used by the background refresh watcher to proactively refresh tokens for users who are idle (no active in-memory session) before they expire. TokenStore implementations that do not embed a queryable DB may omit this.

type MCPAuthChangeEvent

type MCPAuthChangeEvent struct {
	// Kind is one of "linked", "disconnected".
	Kind string
	// CanonicalUserID keys credential storage and resolver cooldowns.
	CanonicalUserID string
	// EffectiveUserID keys the MCP manager client pool.
	EffectiveUserID string
	ServerName      string
	ProviderRef     string
	StorageKey      string
}

MCPAuthChangeEvent describes a delegated MCP credential change (link, disconnect, invalid_grant). Subscribers evict caches only — the event never changes EffectiveUserID, sessions or the workspace provider.

type MCPAuthConfigProvider

type MCPAuthConfigProvider interface {
	Options(ctx context.Context, serverName string) (*mcpcfg.MCPClient, error)
}

MCPAuthConfigProvider yields MCP client configurations for the hosted link endpoints. The MCP manager's repository provider satisfies it; the runtime falls back to a workspace repository loader when none is installed.

type MCPAuthState

type MCPAuthState struct {
	CanonicalUserID string    `json:"canonicalUserId"`
	SessionIDHash   string    `json:"sessionIdHash"`
	ServerName      string    `json:"serverName"`
	ProviderRef     string    `json:"providerRef"`
	ClientRef       string    `json:"clientRef,omitempty"`
	Resource        string    `json:"resource,omitempty"`
	Scopes          []string  `json:"scopes,omitempty"`
	CodeVerifier    string    `json:"codeVerifier"`
	ReturnURL       string    `json:"returnURL,omitempty"`
	Nonce           string    `json:"nonce"`
	ExpiresAt       time.Time `json:"expiresAt"`
	// ConfigFingerprint records the non-secret provider-registry fingerprint
	// at initiation; the callback verifies the configuration has not changed
	// underneath the flow.
	ConfigFingerprint string `json:"configFingerprint,omitempty"`
	// RedirectURI pins the exact redirect used on the authorization request so
	// the token exchange sends the identical value.
	RedirectURI string `json:"redirectURI,omitempty"`
}

MCPAuthState is the encrypted callback state payload for delegated MCP OAuth. It round-trips through the browser only as AEAD ciphertext; the database stores only its hash. The PKCE verifier lives exclusively here.

type MCPLinkBackend

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

MCPLinkBackend exposes the same provider registry, PKCE/state validation, grant verification and refresh lifecycle to hosts with their own ingress and storage. The stores may be local files; no native DB is required.

func NewMCPLinkBackend

func NewMCPLinkBackend(cfg *Config, registry *providerregistry.Registry, tokens DelegatedTokenStore, states OAuthStateStore, configs MCPAuthConfigProvider, users UserService) (*MCPLinkBackend, error)

func (*MCPLinkBackend) Complete

func (b *MCPLinkBackend) Complete(ctx context.Context, identity MCPLinkIdentity, code, state string) (*MCPLinkCompletion, error)

func (*MCPLinkBackend) Initiate

func (b *MCPLinkBackend) Initiate(ctx context.Context, identity MCPLinkIdentity, server, returnURL, callbackURL string) (*MCPLinkInitiation, error)

func (*MCPLinkBackend) Invalidate

func (b *MCPLinkBackend) Invalidate(ctx context.Context, identity MCPLinkIdentity, requirement authcfg.Requirement) error

func (*MCPLinkBackend) Refresh

func (b *MCPLinkBackend) Refresh(ctx context.Context, identity MCPLinkIdentity, requirement authcfg.Requirement) (*authcfg.Credential, error)

func (*MCPLinkBackend) Resolve

func (b *MCPLinkBackend) Resolve(ctx context.Context, identity MCPLinkIdentity, requirement authcfg.Requirement) (*authcfg.Credential, error)

func (*MCPLinkBackend) Status

func (b *MCPLinkBackend) Status(ctx context.Context, identity MCPLinkIdentity, server string) (*MCPLinkStatus, error)

type MCPLinkCompletion

type MCPLinkCompletion = mcpCallbackResult

type MCPLinkIdentity

type MCPLinkIdentity struct{ CanonicalUserID, EffectiveUserID, SessionID string }

MCPLinkIdentity is a trusted host's already-authenticated identity and session binding. It must never be decoded directly from an HTTP/MCP body. Hosts resolve CanonicalUserID through their authoritative user service.

type MCPLinkInitiation

type MCPLinkInitiation = mcpInitiateResult

type MCPLinkStatus

type MCPLinkStatus = mcpStatusResult

type OAuth

type OAuth struct {
	Name   string       `json:"name,omitempty" yaml:"name,omitempty"`
	Client *OAuthClient `json:"client,omitempty" yaml:"client,omitempty"`
}

type OAuthClient

type OAuthClient struct {
	Scopes         []string `json:"scopes,omitempty" yaml:"scopes,omitempty"`
	WebUIScopes    []string `json:"webUIScopes,omitempty" yaml:"webUIScopes,omitempty"`
	MobileUIScopes []string `json:"mobileUIScopes,omitempty" yaml:"mobileUIScopes,omitempty"`
	CLIScopes      []string `json:"cliScopes,omitempty" yaml:"cliScopes,omitempty"`

	ConfigURL string `json:"configURL,omitempty" yaml:"configURL,omitempty"`
}

type OAuthStateRecord

type OAuthStateRecord struct {
	StateHash       string
	FlowHash        string
	CanonicalUserID string
	SessionHash     string
	Provider        string
	ExpiresAt       time.Time
	ConsumedAt      *time.Time
	CreatedAt       time.Time
}

func (*OAuthStateRecord) Pending

func (r *OAuthStateRecord) Pending(now time.Time) bool

type OAuthStateStore

type OAuthStateStore interface {
	CreateOrGetPending(context.Context, *OAuthStateRecord) (*OAuthStateRecord, bool, error)
	Consume(context.Context, string, string, string) error
	DeleteExpired(context.Context, time.Time) (int64, time.Time, error)
}

type OAuthToken

type OAuthToken struct {
	Username     string    `json:"username"`
	Provider     string    `json:"provider"`
	AccessToken  string    `json:"accessToken"`
	IDToken      string    `json:"idToken,omitempty"`
	RefreshToken string    `json:"refreshToken,omitempty"`
	ExpiresAt    time.Time `json:"expiresAt,omitempty"`

	// Issuer is the normalized OAuth issuer the token was granted by.
	Issuer string `json:"issuer,omitempty"`
	// Resource is the protected resource (audience) the token targets.
	Resource string `json:"resource,omitempty"`
	// Scopes are the granted scopes (authoritative after refresh).
	Scopes []string `json:"scopes,omitempty"`
	// TokenType records accessToken versus idToken usage intent.
	TokenType string `json:"tokenType,omitempty"`
	// Subject is the provider subject extracted from the verified token; it is
	// credential metadata only and never becomes the effective user.
	Subject string `json:"subject,omitempty"`
	// ProviderRef/ClientRef record the workspace provider registry references
	// for delegated tokens (the row provider column stores a fixed-length
	// hashed storage key).
	ProviderRef string `json:"providerRef,omitempty"`
	ClientRef   string `json:"clientRef,omitempty"`
	// IDTokenExpiresAt is the verified ID-token exp. ExpiresAt remains the
	// access-token expiry for compatibility; tokenType=idToken consumers must
	// derive validity and refresh thresholds from this field instead.
	IDTokenExpiresAt time.Time `json:"idTokenExpiresAt,omitempty"`
	// IssuedAt records when the token set was obtained (login, exchange or
	// refresh); the refresh policy uses it to derive the original selected
	// token lifetime for the 20% clamp.
	IssuedAt time.Time `json:"issuedAt,omitempty"`
}

OAuthToken represents a stored OAuth token set for a user/provider pair. The metadata fields (Issuer, Resource, Scopes, TokenType, Subject, ProviderRef, ClientRef) are optional for backward compatibility with legacy workspace rows; delegated (per-provider MCP) tokens always populate them and every conversion/refresh path must preserve them.

func (*OAuthToken) HasDelegatedMetadata

func (t *OAuthToken) HasDelegatedMetadata() bool

HasDelegatedMetadata reports whether the token carries the delegated credential metadata that refresh/CAS paths must not drop.

func (*OAuthToken) MergeMetadataFrom

func (t *OAuthToken) MergeMetadataFrom(prior *OAuthToken)

MergeMetadataFrom copies missing metadata fields from prior. Populated fields on the receiver win (e.g. an authoritative refreshed scope set).

type PreferencesPatch

type PreferencesPatch struct {
	DisplayName        *string                           `json:"displayName,omitempty"`
	Timezone           *string                           `json:"timezone,omitempty"`
	DefaultAgentRef    *string                           `json:"defaultAgentRef,omitempty"`
	DefaultModelRef    *string                           `json:"defaultModelRef,omitempty"`
	DefaultEmbedderRef *string                           `json:"defaultEmbedderRef,omitempty"`
	AgentPrefs         map[string]map[string]interface{} `json:"agentPrefs,omitempty"`
}

PreferencesPatch describes a partial update to user preferences.

type Session

type Session struct {
	ID       string         `json:"id"`
	UserID   string         `json:"userId,omitempty"`
	Username string         `json:"username"`
	Email    string         `json:"email,omitempty"`
	Subject  string         `json:"subject,omitempty"`
	Provider string         `json:"provider,omitempty"`
	Scopes   []string       `json:"scopes,omitempty"`
	Tokens   *scyauth.Token `json:"-"`
	// TransientRefreshRetryAt suppresses repeated refresh attempts/log spam
	// after a temporary token-endpoint failure. Runtime auth code persists the
	// cooldown through session metadata when a durable store is configured.
	TransientRefreshRetryAt time.Time `json:"-"`
	CreatedAt               time.Time `json:"createdAt"`
	ExpiresAt               time.Time `json:"expiresAt"`
}

Session represents an authenticated user session.

Identity model:

  • UserID = canonical agently users.id when available
  • Subject = raw oauth/jwt subject
  • Username = jwt.preferred_username or jwt.name — display name only
  • Email = jwt.email — display / contact only

Identity split:

  • sess.UserID is the canonical users.id UUID for internal persistence, token storage, token refresh, and session bookkeeping.
  • request EffectiveUserID is the provider subject/email/username used by ownership and visibility filters, including created_by_user_id.

Do not feed sess.EffectiveUserID() into request context unless created_by_user_id has been migrated to canonical users.id. Token persistence can use the canonical UserID, but request-scoped ownership must remain subject-compatible.

func (*Session) EffectiveUserID

func (s *Session) EffectiveUserID() string

EffectiveUserID returns the canonical session identity when available. This is intended for session/token persistence. For request-scoped ownership or visibility checks, use Subject/Email/Username directly; those filters still compare against subject-based created_by_user_id values.

func (*Session) IsExpired

func (s *Session) IsExpired() bool

IsExpired returns true when the session has passed its expiry time.

type TokenStore

type TokenStore interface {
	Get(ctx context.Context, username, provider string) (*OAuthToken, error)
	Put(ctx context.Context, token *OAuthToken) error
	Delete(ctx context.Context, username, provider string) error

	// TryAcquireRefreshLease atomically attempts to acquire a distributed lease
	// for refreshing the token identified by (username, provider).
	TryAcquireRefreshLease(ctx context.Context, username, provider, owner string, ttl time.Duration) (version int64, acquired bool, err error)

	// ReleaseRefreshLease releases a previously acquired lease.
	ReleaseRefreshLease(ctx context.Context, username, provider, owner string) error

	// CASPut atomically updates the token only if the current version matches
	// expectedVersion and the lease is held by owner.
	CASPut(ctx context.Context, token *OAuthToken, expectedVersion int64, owner string) (swapped bool, err error)
}

TokenStore abstracts encrypted OAuth token persistence. Implementations may use scy-backed secrets, database storage, etc.

type User

type User struct {
	ID          string                 `json:"id,omitempty"`
	Username    string                 `json:"username"`
	Email       string                 `json:"email,omitempty"`
	DisplayName string                 `json:"displayName,omitempty"`
	Provider    string                 `json:"provider,omitempty"`
	Subject     string                 `json:"subject,omitempty"`
	Preferences map[string]interface{} `json:"preferences,omitempty"`
	// Disabled marks a deactivated canonical user: disabled users cannot use
	// stored delegated credentials and fail canonical resolution.
	Disabled bool `json:"disabled,omitempty"`
}

User represents a registered user.

type UserByIDLookup

type UserByIDLookup interface {
	GetByID(ctx context.Context, id string) (*User, error)
}

UserByIDLookup is the optional by-ID active-status lookup used by the delegated credential resolver, the background refresh watcher and the link endpoints: a disabled or deleted canonical user cannot use delegated tokens. It is a separate interface so existing UserService implementations remain compatible.

type UserInfo

type UserInfo = iauth.UserInfo

UserInfo carries host-provided profile identifiers. These values are token partitioning metadata and never substitute for an authorization provider.

func UserFromContext

func UserFromContext(ctx context.Context) *UserInfo

type UserService

type UserService interface {
	GetByUsername(ctx context.Context, username string) (*User, error)
	GetBySubjectAndProvider(ctx context.Context, subject, provider string) (*User, error)
	Upsert(ctx context.Context, user *User) error
	UpsertWithProvider(ctx context.Context, username, displayName, email, provider, subject string) (string, error)
	UpdateHashIPByID(ctx context.Context, id, hash string) error
	UpdatePreferences(ctx context.Context, username string, patch *PreferencesPatch) error
}

UserService abstracts user CRUD. Implementations may use Datly, SQL, or an in-memory store.

Directories

Path Synopsis
internal
SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.
SPDX-License-Identifier: Apache-2.0 Extracted from Viant Agently Core.
Package providerregistry implements the workspace OAuth provider registry.
Package providerregistry implements the workspace OAuth provider registry.

Jump to

Keyboard shortcuts

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