auth

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Adapter

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

Adapter handles authentication operations using bcrypt and JWT

func NewAdapter

func NewAdapter(jwtSecret string) *Adapter

NewAdapter creates a new auth adapter with the given JWT secret

func NewAdapterWithCost

func NewAdapterWithCost(jwtSecret string, bcryptCost int) *Adapter

NewAdapterWithCost creates a new auth adapter with custom bcrypt cost

func (*Adapter) GenerateToken

func (a *Adapter) GenerateToken(claims *domain.TokenClaims) (string, error)

GenerateToken creates a signed JWT from domain claims

func (*Adapter) HashPassword

func (a *Adapter) HashPassword(password string) (string, error)

HashPassword generates a bcrypt hash from a plaintext password

func (*Adapter) ParseToken

func (a *Adapter) ParseToken(tokenString string) (*domain.TokenClaims, error)

ParseToken validates a JWT and extracts domain claims

func (*Adapter) VerifyPassword

func (a *Adapter) VerifyPassword(password, hash string) bool

VerifyPassword checks if a password matches a bcrypt hash

type OAuthTokenProvider

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

OAuthTokenProvider implements TokenProvider for OAuth2 credentials. It is intended to be shared across all consumers of the same connectionID within the process — TokenProviderFactory.Create ensures exactly one instance exists per connection. The embedded mutex serialises token refresh: while one caller is executing the HTTP round-trip to the token endpoint (typically ~200ms), other callers for the same connection block on the lock and then see the fresh token without issuing a second refresh. This prevents the token-endpoint race that occurs when N concurrent goroutines each hold a stale token and all decide to refresh simultaneously, causing Microsoft to rotate the refresh token and invalidate N-1 of the requests.

The mutex is held for the entire duration of the refresh HTTP call. This is an intentional design choice: the alternative (generation counters or refresh-in-flight channels) adds significantly more code for the same outcome. Document this explicitly so future readers do not reach for a singleflight rewrite without considering the trade-off.

Token state divergence: if anything mutates the connection's secrets in Postgres without going through refreshLocked (e.g. a direct DB write), the in-memory provider will go stale until the process restarts or RemoveProvider is called and Create re-loads the connection.

func NewOAuthTokenProvider

func NewOAuthTokenProvider(
	connectionID string,
	accessToken string,
	refreshToken string,
	expiry *time.Time,
	refresher TokenRefresherFunc,
	connectionStore driven.ConnectionStore,
) *OAuthTokenProvider

NewOAuthTokenProvider creates a token provider for OAuth credentials.

func (*OAuthTokenProvider) AuthMethod

func (p *OAuthTokenProvider) AuthMethod() domain.AuthMethod

AuthMethod returns OAuth2.

func (*OAuthTokenProvider) GetAccessToken

func (p *OAuthTokenProvider) GetAccessToken(ctx context.Context) (string, error)

GetAccessToken returns a valid access token, refreshing if needed. It holds mu for the duration of a refresh so that concurrent callers serialise: the second caller blocks until the first refresh completes, then sees needsRefresh() == false and skips a redundant refresh.

func (*OAuthTokenProvider) GetCredentials

func (p *OAuthTokenProvider) GetCredentials(ctx context.Context) (*domain.Credentials, error)

GetCredentials returns credentials for OAuth. It holds mu for the same reason as GetAccessToken.

func (*OAuthTokenProvider) IsValid

func (p *OAuthTokenProvider) IsValid(ctx context.Context) bool

IsValid checks if credentials are valid (not expired or can be refreshed).

This method is lock-free and intentionally so: its contract is best-effort ("has a valid token or can be refreshed"). A concurrent refresh may flip isExpired() from true to false while IsValid runs; the race only makes the result more conservative (returns true when the token was just refreshed), never incorrectly false. Callers that need a guaranteed-valid token must call GetAccessToken instead.

type StaticTokenProvider

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

StaticTokenProvider implements TokenProvider for non-OAuth credentials. Used for API keys, PATs, and service accounts.

func NewStaticTokenProvider

func NewStaticTokenProvider(token string, authMethod domain.AuthMethod) *StaticTokenProvider

NewStaticTokenProvider creates a token provider for static credentials.

func (*StaticTokenProvider) AuthMethod

func (p *StaticTokenProvider) AuthMethod() domain.AuthMethod

AuthMethod returns the authentication method.

func (*StaticTokenProvider) GetAccessToken

func (p *StaticTokenProvider) GetAccessToken(ctx context.Context) (string, error)

GetAccessToken returns the static token.

func (*StaticTokenProvider) GetCredentials

func (p *StaticTokenProvider) GetCredentials(ctx context.Context) (*domain.Credentials, error)

GetCredentials returns nil for static tokens - use GetAccessToken instead.

func (*StaticTokenProvider) IsValid

func (p *StaticTokenProvider) IsValid(ctx context.Context) bool

IsValid returns true - static credentials don't expire.

type TokenProviderFactory

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

TokenProviderFactory creates TokenProviders from connection credentials. It maintains a per-connection cache (providers) so that all callers sharing the same connectionID get the exact same *OAuthTokenProvider instance. This ensures that OAuth token refresh is serialised per connection: only one goroutine refreshes at a time, and subsequent callers see the new token immediately instead of racing the same refresh token.

Cold-start race: if two goroutines call Create for the same connectionID simultaneously before either has populated the cache, both may call connectionStore.Get and construct a provider. Only one wins the LoadOrStore; the loser's instance is GC'd. This costs at most one extra Postgres read per connection per process lifetime and requires no synchronisation beyond the sync.Map itself.

func NewTokenProviderFactory

func NewTokenProviderFactory(
	connectionStore driven.ConnectionStore,
) *TokenProviderFactory

NewTokenProviderFactory creates a new TokenProviderFactory.

func (*TokenProviderFactory) Create

func (f *TokenProviderFactory) Create(ctx context.Context, connectionID string) (driven.TokenProvider, error)

Create returns a TokenProvider for the given connectionID.

If a provider for connectionID already exists in the process-level cache, it is returned immediately (O(1), no I/O). Otherwise the connection is loaded from the store, a new provider is constructed, and the result is stored via LoadOrStore so concurrent callers converge on a single instance.

func (*TokenProviderFactory) CreateFromConnection

func (f *TokenProviderFactory) CreateFromConnection(ctx context.Context, conn *domain.Connection) (driven.TokenProvider, error)

CreateFromConnection creates a TokenProvider from a connection directly. Use this when you already have the connection loaded. The returned provider is NOT inserted into the per-connection cache — it is a fresh instance scoped to the caller's use (e.g. OAuth callback flows that have the connection in hand before it is registered in the cache).

func (*TokenProviderFactory) RegisterRefresher

func (f *TokenProviderFactory) RegisterRefresher(
	platform domain.PlatformType,
	refresher TokenRefresherFunc,
)

RegisterRefresher registers a token refresh function for a platform type.

func (*TokenProviderFactory) RemoveProvider added in v0.3.0

func (f *TokenProviderFactory) RemoveProvider(connectionID string)

RemoveProvider evicts the cached provider for a connection. It should be called by ConnectionService.Delete after the underlying connection row is removed, to prevent stale token state from persisting in memory.

RemoveProvider is a concrete-only method and does not appear on the driven.TokenProviderFactory port interface — callers that only hold the interface are unaffected.

type TokenRefresherFunc

type TokenRefresherFunc func(ctx context.Context, refreshToken string) (*driven.OAuthToken, error)

TokenRefresherFunc is a function type for token refresh operations.

Jump to

Keyboard shortcuts

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