Documentation
¶
Overview ¶
Package auth provides OAuth 2.1 authentication for Basecamp.
Index ¶
- type ClientCredentials
- type Credentials
- type LoginOptions
- type LoginResult
- type Manager
- func (m *Manager) AccessToken(ctx context.Context) (string, error)
- func (m *Manager) AccountID() string
- func (m *Manager) AuthorizationEndpoint(ctx context.Context) (string, error)
- func (m *Manager) CredentialKey() string
- func (m *Manager) GetOAuthType() string
- func (m *Manager) GetStore() *Store
- func (m *Manager) GetUserEmail() string
- func (m *Manager) IsAuthenticated() bool
- func (m *Manager) Login(ctx context.Context, opts LoginOptions) (*LoginResult, error)
- func (m *Manager) Logout() error
- func (m *Manager) Refresh(ctx context.Context) error
- func (m *Manager) SetStore(s *Store)
- func (m *Manager) SetUserEmail(email string) error
- func (m *Manager) SetUserIdentity(userID, email string) error
- func (m *Manager) StoredAccessToken(ctx context.Context) (string, error)
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClientCredentials ¶
type ClientCredentials struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"`
}
ClientCredentials holds OAuth client ID and secret.
type Credentials ¶
type Credentials struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt int64 `json:"expires_at"`
Scope string `json:"scope"`
OAuthType string `json:"oauth_type"` // "bc5", "launchpad", or legacy "bc3"
TokenEndpoint string `json:"token_endpoint"`
UserID string `json:"user_id,omitempty"`
UserEmail string `json:"user_email,omitempty"`
// Resource is the RFC 8707 resource indicator the tokens are bound to
// (BC5: urn:bc:account:<id>). BC5 device logins as the trusted
// basecamp-cli client mint MULTI-ACCOUNT refresh tokens, and the refresh
// grant rejects them without this echo — refresh sends it when set and
// preserves it when a refresh response omits it.
Resource string `json:"resource,omitempty"`
}
Credentials holds OAuth tokens and metadata.
type LoginOptions ¶
type LoginOptions struct {
Scope string
NoBrowser bool // If true, don't auto-open browser, just print URL
// Remote forces remote/headless mode: skip the loopback listener and
// prompt the user to paste the callback URL. Auto-detected when SSH
// env vars are present (unless Local is set).
Remote bool
// Local forces local mode, overriding SSH auto-detection.
// Mutually exclusive with Remote.
Local bool
// InputReader is the source for pasted callback URLs in remote mode.
// If nil, os.Stdin is used.
InputReader io.Reader
// RedirectURI overrides the OAuth redirect URI.
// Takes precedence over BASECAMP_OAUTH_REDIRECT_URI and CallbackAddr.
RedirectURI string
// CallbackAddr is the address for the local OAuth callback server.
// Default: "127.0.0.1:8976"
CallbackAddr string
// BrowserLauncher opens the authorization URL in a browser.
// If nil, uses the default system browser launcher.
BrowserLauncher func(url string) error
// Logger receives status messages during the login flow.
// If nil, messages are suppressed for headless/SDK use.
Logger func(msg string)
// contains filtered or unexported fields
}
LoginOptions configures the login flow.
type LoginResult ¶ added in v0.4.0
type LoginResult struct {
OAuthType string // "bc5" or "launchpad" (stored credentials may also carry legacy "bc3")
Scope string // effective scope: "read"/"full" for BC5, "" for Launchpad
}
LoginResult holds the outcome of a successful Login(). Callers use this to determine the effective scope instead of their input.
type Manager ¶
type Manager struct {
// Warnf receives transport-policy warnings (a proxy ignored for OAuth
// traffic, a malformed opt-out value). Test seam; nil means stderr.
Warnf func(format string, args ...any)
// contains filtered or unexported fields
}
Manager handles OAuth authentication.
func NewManager ¶
NewManager creates a new auth manager.
A nil httpClient is the production configuration: OAuth requests ride per-provenance clients that enforce the SDK's address policy at dial time. A non-nil httpClient is caller-owned (test-only in this codebase) and carries every OAuth request as-is — no address policy is applied on top.
func (*Manager) AccessToken ¶
AccessToken returns a valid access token, refreshing if needed. If BASECAMP_TOKEN env var is set, it's used directly without OAuth.
func (*Manager) AccountID ¶ added in v0.9.0
AccountID returns the account a BC5 token is bound to, derived from its stored RFC 8707 resource indicator, or "" when the credentials carry no account binding (Launchpad tokens, or a resource naming the service origin rather than one account).
The binding is authoritative: the token grants access to exactly this account, so there is nothing to discover over the network and no picker to show. It also works where account discovery cannot — /authorization.json is served only on the API host, which beta deployments don't route.
BASECAMP_TOKEN wins — match AccessToken() precedence. Requests carry the environment token, which is bound to whatever the operator issued it for; answering with a stored token's account would silently address the wrong one. Fall through to discovery, which asks using the token in play.
func (*Manager) AuthorizationEndpoint ¶ added in v0.7.0
AuthorizationEndpoint returns the authorization info endpoint URL for the current authentication context. BASECAMP_TOKEN takes precedence over stored credentials (mirroring AccessToken), with the token prefix used to determine the issuer. When no env token is set, stored OAuth type drives selection.
func (*Manager) CredentialKey ¶
CredentialKey returns the current credential storage key. This is exported for use in commands that need to display or lookup credentials.
func (*Manager) GetOAuthType ¶
GetOAuthType returns the OAuth type for the current credential key ("bc5", "launchpad", or legacy "bc3").
func (*Manager) GetUserEmail ¶ added in v0.3.0
GetUserEmail returns the stored user email for the current credential key.
func (*Manager) IsAuthenticated ¶
IsAuthenticated checks if there are valid credentials. Returns true if BASECAMP_TOKEN env var is set or if OAuth credentials exist.
func (*Manager) Login ¶
func (m *Manager) Login(ctx context.Context, opts LoginOptions) (*LoginResult, error)
Login initiates the OAuth login flow. Discovery selects the provider: a BC5 issuer runs the RFC 8628 device flow; the Launchpad fallback runs the authorization-code flow with a loopback (or pasted) callback.
func (*Manager) SetStore ¶ added in v0.3.0
SetStore replaces the credential store. Used in tests to inject a file-backed store rooted in a temp directory.
func (*Manager) SetUserEmail ¶ added in v0.3.0
SetUserEmail stores the user email for the current credential key without modifying the stored user ID.
BASECAMP_TOKEN wins — match AccessToken() precedence. The email was fetched with the environment token, so it names that token's user, not whoever the stored credentials belong to; writing it there would mislabel them. Skipping the store also keeps a token session off the keyring probe and the fallback warning it can raise.
func (*Manager) SetUserIdentity ¶ added in v0.3.0
SetUserIdentity stores the user ID and email for the current credential key.
func (*Manager) StoredAccessToken ¶
StoredAccessToken returns a valid access token from the credential store, refreshing if needed. Unlike AccessToken, this ignores the BASECAMP_TOKEN environment variable and always uses stored OAuth credentials.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store wraps credstore.Store with typed Credentials marshaling.
Construction of the underlying credstore.Store is deferred to first use: credstore.NewStore probes the OS keyring availability with a write, and on a locked keychain with no TTY or GUI (headless macOS) that probe blocks forever in an uncancellable `security` child process. Credential-free commands must never pay it.
func NewStore ¶
NewStore creates a credential store. The OS keyring is not touched until the first credential operation.
func (*Store) Load ¶
func (s *Store) Load(origin string) (*Credentials, error)
Load retrieves credentials for the given origin.
func (*Store) MigrateToKeyring ¶
MigrateToKeyring migrates credentials from file to keyring.
func (*Store) Save ¶
func (s *Store) Save(origin string, creds *Credentials) error
Save stores credentials for the given origin.
func (*Store) UsingKeyring ¶
UsingKeyring returns true if the store is using the system keyring.