Documentation
¶
Overview ¶
Package github — GitHub OAuth Device Flow + tiny REST helpers scoped to clawtool's needs. Today: device-code authorisation + `PUT /user/starred/{owner}/{repo}` for the star feature. More will land as engagement / source-management features need them.
Why Device Flow over web-redirect OAuth: clawtool is a CLI; we have no http server to receive a callback. Device Flow is designed exactly for this — we POST a device-code request, show the user a `user_code` and a verification URL, the user authorises in their browser, we poll the token endpoint until they finish. No redirect URI, no localhost listener, no port collision.
Token storage: handled by the caller via internal/secrets, not here. This package is the wire-protocol shim and stays stateless so tests can drive it with httptest fixtures.
Index ¶
Constants ¶
const DefaultAPIBaseURL = "https://api.github.com"
DefaultAPIBaseURL is api.github.com's REST root. Same override shape as DefaultBaseURL.
const DefaultBaseURL = "https://github.com"
DefaultBaseURL is github.com's well-known endpoint. Overridable in tests (httptest fixture) by setting BaseURL on the Client.
Variables ¶
var ClientID = ""
ClientID is the GitHub OAuth App client_id used by clawtool's CLI surface. Public-by-design (Device Flow doesn't use a client secret; the user-code + browser confirmation IS the security boundary). Empty when the operator hasn't registered an OAuth app yet — the device flow then errors out cleanly via ErrNoClientID instead of crashing.
To wire this in: create a GitHub OAuth App at github.com/settings/developers, set Device flow enabled, copy the resulting client_id into the build via -ldflags '-X github.com/cogitave/clawtool/internal/github.ClientID=<id>' or hard-code below at release time.
var ErrAuthorizationDenied = errors.New("github: authorization denied by user")
ErrAuthorizationDenied is returned when the user explicitly declined the consent screen.
var ErrDeviceCodeExpired = errors.New("github: device code expired before authorisation")
ErrDeviceCodeExpired is returned by PollAccessToken when the device code's lifetime ran out before the user authorised. Callers typically restart the flow with a fresh code.
var ErrNoClientID = errors.New("github: clawtool's GitHub OAuth client_id is not configured")
ErrNoClientID surfaces the "we don't have an OAuth app registered yet" state cleanly so the caller can fall back to a browser-redirect-to-action-page flow.
Functions ¶
func StarPageURL ¶
StarPageURL returns the human-facing star page on github.com for the given owner/repo. Used as the OAuth-disabled fallback: open this in the user's browser and let them click Star themselves.
Types ¶
type Client ¶
type Client struct {
HTTP *http.Client
BaseURL string // for /login/device/code + /login/oauth/access_token
APIBaseURL string // for REST endpoints
UserAgent string // GitHub asks every API call to set a UA
ClientIDStr string // override for tests; falls back to package ClientID
}
Client wraps an *http.Client with the URLs and credentials the clawtool→GitHub flows need. Construct via NewClient() and override fields for tests.
func NewClient ¶
func NewClient() *Client
NewClient returns a Client with sane defaults. 30s overall timeout protects against a hung github.com from stranding the CLI; the per-call ctx the caller passes may impose a tighter budget for individual phases.
func (*Client) PollAccessToken ¶
PollAccessToken polls /login/oauth/access_token at the device-code's documented interval until either the user authorises (returns the access token), the code expires (returns ErrDeviceCodeExpired), or the user denies it (returns ErrAuthorizationDenied). ctx cancellation aborts the poll cleanly so a Ctrl-C in the CLI doesn't hang.
ADR-036 Phase 1: this delegates to the issuer-agnostic internal/auth/devicecode poller so the wizard's DeviceCodeStep and clawtool star share one RFC 8628 §3.5 state machine. Errors are translated back to this package's sentinels so existing callers (star.go) keep their `errors.Is` shape.
func (*Client) RequestDeviceCode ¶
RequestDeviceCode kicks off the device flow with the given space-separated scope list (e.g. "public_repo" for starring public repos). Returns the device code envelope or an error.
type DeviceCode ¶
type DeviceCode struct {
DeviceCodeStr string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
ExpiresIn int `json:"expires_in"` // seconds
Interval int `json:"interval"` // poll interval, seconds
Expires time.Time `json:"-"` // computed
PollEvery time.Duration `json:"-"` // computed
}
DeviceCode is the response from the device authorisation endpoint. The CLI shows VerificationURI + UserCode to the operator (and ideally OpenBrowser's the URI), then polls /login/oauth/access_token using DeviceCodeStr until the user authorises or the code expires.