Documentation
¶
Overview ¶
Package oauth signs the CLI in to the AudD account service: discovery (RFC 9728 protected resource metadata, RFC 8414 server metadata), the pre-registered public client audd-cli, two ways to approve a sign-in (authorization code with PKCE (RFC 7636) through a loopback redirect or a pasted redirect URL, and the device authorization grant (RFC 8628)), refresh under a cross-process file lock, step-up consent for extra scopes, and revocation. Dynamic client registration (RFC 7591) is only a fallback for other authorization servers.
Sessions are stored per profile in the secrets store under "oauth"; an unfinished login is kept under "oauth_pending" so another invocation can complete it with `audd auth login --complete`.
Index ¶
- Constants
- Variables
- func SignedInLine(st output.Styles, account, profile string) string
- func Union(a, b []string) []string
- type Client
- func (c *Client) CompletePending(ctx context.Context, redirectURL string) (*Tokens, error)
- func (c *Client) EnsureScopes(ctx context.Context, needed ...string) (*Tokens, error)
- func (c *Client) FlushNotes()
- func (c *Client) LoggedIn() bool
- func (c *Client) Login(ctx context.Context, o LoginOptions) (*Tokens, error)
- func (c *Client) Logout(ctx context.Context) error
- func (c *Client) Refresh(ctx context.Context) (*Tokens, error)
- func (c *Client) ResourceURL() string
- func (c *Client) Stored() (*Tokens, error)
- func (c *Client) Token(ctx context.Context) (*Tokens, error)
- type Environment
- type LoginOptions
- type Method
- type Option
- func WithBrowserOpener(f func(url string) error) Option
- func WithEnvironment(e Environment) Option
- func WithHTTPClient(h *http.Client) Option
- func WithLockDir(dir string) Option
- func WithLoginTimeout(d time.Duration) Option
- func WithMethod(m Method) Option
- func WithNow(f func() time.Time) Option
- func WithResourceURL(u string) Option
- func WithSaveConfig(f func() error) Option
- type Tokens
Constants ¶
const AudDIssuer = "https://dashboard-api.audd.io"
AudDIssuer is AudD's authorization server. Against it the CLI always uses FirstPartyClientID and never registers a client.
const DefaultResourceURL = "https://mcp.audd.io"
DefaultResourceURL is the AudD MCP server, the resource the CLI signs in to.
const FirstPartyClientID = "audd-cli"
FirstPartyClientID is the AudD CLI's pre-registered public client.
const LoginTimeout = 10 * time.Minute
LoginTimeout is how long Login waits for the browser.
const NeverRequested = "api:request"
NeverRequested is left out of every sign-in: the CLI calls the API with the API token, and the AudD sign-in service refuses the whole request when it is asked for.
const RedirectPath = "/callback"
RedirectPath is the loopback callback path registered with the server.
Variables ¶
var DefaultScopes = []string{"openid", "profile:read", "account:read", "usage:read", "billing:read", "billing:pay", "token:read", "token:write"}
DefaultScopes are requested by `audd login`: everything the CLI uses. The user can untick any of them; commands that need an unticked one ask for it again (step-up).
var DetectEnvironment = func(stdinTTY, stdoutTTY bool) Environment { return Environment{ GOOS: runtime.GOOS, Getenv: os.Getenv, InContainer: inContainer(), StdinTTY: stdinTTY, StdoutTTY: stdoutTTY, } }
DetectEnvironment reads the environment of this process. Tests replace it.
var OpenBrowser = func(u string) error { browser.Stdout, browser.Stderr = io.Discard, io.Discard return browser.OpenURL(u) }
OpenBrowser opens a URL in the user's browser. Tests replace it.
var PollUnit = time.Second
PollUnit is the length of one second of a device sign-in's polling interval and lifetime. Tests shorten it.
var RegisteredScopes = []string{"openid", "profile:read", "account:read", "usage:read", "billing:read", "billing:pay", "token:read", "token:write"}
RegisteredScopes are the scopes the CLI registers its client for (on servers other than AudD's): every scope it may ever ask for.
var ScopeDescriptions = map[string]string{
"openid": "confirm who you are",
"email": "read your email address",
"profile:read": "read your account email and sign-in methods",
"account:read": "read your account details",
"usage:read": "read your usage",
"billing:read": "read your plan and billing history",
"billing:pay": "create payment links",
"token:read": "read your API token",
"token:write": "rotate your API token",
}
ScopeDescriptions explain scopes in prompts.
Functions ¶
func SignedInLine ¶
SignedInLine is the one-line message that replaces the waiting block when a sign-in succeeds: "✓ Signed in as user@example.com (profile default).", in the success style.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client signs in and keeps the session for one profile.
func (*Client) CompletePending ¶
CompletePending finishes a login started elsewhere (usually a login that printed login_pending) from the redirect URL the browser was sent to.
func (*Client) EnsureScopes ¶
EnsureScopes returns a session that has every scope in needed. When some are missing it signs in again (step-up), asking for the scopes granted now plus the missing ones, with the method chosen for this client.
func (*Client) FlushNotes ¶
func (c *Client) FlushNotes()
FlushNotes prints the warnings held back during a sign-in. Call it after the waiting block was replaced by the success line (or left on screen).
func (*Client) Login ¶
Login signs in. The browser method always prints the URL, opens the browser unless NoBrowser is set, listens on a loopback port, and, when stdin is a terminal, also accepts a pasted redirect URL, query string, or code. The device method prints a page and a code to approve on any device and polls until the user approves or denies, or the code expires. Without a terminal (agents), either method prints a login_pending record.
Against AudD's server the CLI signs in as FirstPartyClientID. When the saved session belongs to another client (a client an earlier version registered), it is revoked after the new sign-in succeeds.
func (*Client) Logout ¶
Logout revokes the refresh token and deletes the session, the pending login, and the API token fetched at login. Tokens the user set (config, environment, flag) are not touched. A failed revocation is reported as a warning; the local session is deleted regardless.
func (*Client) ResourceURL ¶
ResourceURL is the protected resource this client signs in to.
type Environment ¶
type Environment struct {
GOOS string
Getenv func(string) string
InContainer bool
StdinTTY bool
StdoutTTY bool
}
Environment is what method selection looks at.
func (Environment) BrowserAvailable ¶
func (e Environment) BrowserAvailable() bool
BrowserAvailable reports whether a browser can be opened on this machine: a macOS or Windows desktop session, or a Linux or BSD session with a display. Never over SSH, and never in a container without a display.
func (Environment) Method ¶
func (e Environment) Method() Method
Method picks the sign-in method: the browser when a person is at a terminal on a machine that can open one, otherwise the device flow. Without a terminal on stdin or stdout (scripts and agents) it is always the device flow, which needs nothing from this machine.
type LoginOptions ¶
type LoginOptions struct {
// Scopes to ask for (DefaultScopes when empty). api:request is left out.
Scopes []string
// KeepGranted also asks for the scopes of the saved sign-in when it was
// made with the same client, and for DefaultScopes otherwise.
KeepGranted bool
// Method forces browser or device; MethodAuto picks from the environment.
Method Method
// NoBrowser stops the CLI from opening a browser (the URL is printed
// either way).
NoBrowser bool
// In is where a pasted redirect URL is read from (browser method, with
// a terminal on stdin).
In io.Reader
}
LoginOptions configure a sign-in.
type Method ¶
type Method string
Method is how a sign-in is approved.
const ( // MethodAuto picks browser or device from the environment. MethodAuto Method = "" // MethodBrowser is the authorization code flow with PKCE: a browser on // this machine is sent back to a loopback port, or the user pastes the // address it was sent to. MethodBrowser Method = "browser" // MethodDevice is the device authorization grant (RFC 8628): the user // opens a page on any device and approves a short code. MethodDevice Method = "device" )
Sign-in methods.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithBrowserOpener ¶
WithBrowserOpener replaces OpenBrowser for this client.
func WithEnvironment ¶
func WithEnvironment(e Environment) Option
WithEnvironment sets what method selection looks at (default: this process).
func WithHTTPClient ¶
WithHTTPClient sets the HTTP client.
func WithLockDir ¶
WithLockDir sets where the refresh lock file lives (default the data dir).
func WithLoginTimeout ¶
WithLoginTimeout changes how long Login waits (default 10 minutes).
func WithMethod ¶
WithMethod sets the method for sign-ins this client starts on its own (step-up). MethodAuto picks one from the environment.
func WithResourceURL ¶
WithResourceURL sets the protected resource (default https://mcp.audd.io).
func WithSaveConfig ¶
WithSaveConfig is called after the profile changes (client ID, scopes, account email) so the caller can persist the config file.
type Tokens ¶
type Tokens struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
TokenType string `json:"token_type,omitempty"`
Expiry time.Time `json:"expiry"`
Scopes []string `json:"scopes,omitempty"`
// Requested are the scopes the sign-in asked for. The user may untick
// some, so Scopes can be narrower.
Requested []string `json:"requested_scopes,omitempty"`
Issuer string `json:"issuer,omitempty"`
ClientID string `json:"client_id,omitempty"`
}
Tokens is a stored OAuth session.