Documentation
¶
Overview ¶
Package appauth implements GitHub App authentication for go-github-kit: signing installation JWTs, minting short-lived installation tokens, and wiring them into HTTP transports and kit kernels.
The auth flow ¶
GitHub Apps authenticate in two steps:
- The App signs a RS256 JWT with its private key (issued by GitHub's App settings page). The JWT lives at most 10 minutes; we backdate iat by 60s for clock skew and let each JWT expire after 10 minutes.
- The JWT authorizes calls to the REST Apps API only. The only call that matters here is "create an installation access token" (POST /app/installations/{id}/access_tokens), which returns a Bearer token valid for ~1 hour and scoped to one installation.
AppAuth produces step-1 JWTs. AppAuthTransport attaches them to requests against the Apps API (use NewAppsClient for a ready-made *github.Client whose AppsService speaks JWT auth). InstallationTokenSource performs step 2 and caches the minted token until one minute before expiry, so concurrent consumers share a token and refresh happens safely inside the validity window. InstallationTokenTransport attaches the minted token to every request against the installation-scoped API.
Wiring a kernel per installation ¶
A kit kernel needs one installation token per customer installation. Until kit grows a first-class option, compose it through the existing surface:
apps, _ := appauth.NewAppsClient(app, "") // JWT auth, api.github.com
src, _ := appauth.NewInstallationTokenSource(instID, apps.Apps)
tr, _ := appauth.NewInstallationTokenTransport(src, nil)
kernel, _ := githubkit.New(
githubkit.WithBaseURL(base),
githubkit.WithHTTPClient(&http.Client{Transport: tr}),
)
WithHTTPClient makes the installation transport the innermost kernel layer, so every kernel request carries the minted Bearer token and kernel error/etag/rate-limit layers sit on top as designed.
Operational constraints (verified against GitHub's docs) ¶
- JWTs: RS256 only, iss = numeric App ID, exp at most 10 minutes out.
- Installation tokens expire after about 1 hour; we treat the refresh lead as 1 minute and refuse tokens with suspicious lifetimes (> 24h) because they indicate a mis-parsed response.
- Minting a token counts against the Apps API rate limit (low, per-App), not the installation's core limit — hence the caching.
- Secondary rate limits still apply to the installation token's requests; pair this package with the kit rate-limit layer.
Known gaps ¶
- Token minting is not single-flight across goroutines (the cache is mutex-guarded, but N concurrent callers at expiry may mint N tokens). The last token wins; correctness is unaffected, mint budget is slightly wasted. A single-flight wrapper is a planned improvement.
- NewAppsClient hardcodes the standard API root when baseURL is empty; GitHub Enterprise Server users must pass their root explicitly (with trailing slash).
Package appauth authenticates go-github-kit kernels as a GitHub App (plan V3 B-22): App JWTs for the Apps API, minted installation tokens for repository access, and transports that keep both fresh.
The flow mirrors GitHub's own guidance:
app := appauth.NewAppAuth(appID, pemKey)
apps := appauth.NewAppsClient(app) // JWT-authenticated
src := appauth.NewInstallationTokenSource(app, installationID, apps.AppsService)
kernel, err := githubkit.New(
githubkit.WithBaseURL(...),
githubkit.WithHTTPClient(&http.Client{
Transport: appauth.NewInstallationTokenTransport(src, nil),
}),
)
Nothing here contacts GitHub until a token is actually needed; creating the App, installing it, and rotating its private key are operational steps outside this package (founder gate, plan A1).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewAppsClient ¶
NewAppsClient builds a go-github client whose requests carry the app JWT: enough to list installations and mint installation tokens.
func NewInstallationTokenTransport ¶
func NewInstallationTokenTransport(src *InstallationTokenSource, base http.RoundTripper) (http.RoundTripper, error)
NewInstallationTokenTransport wraps base (nil means http.DefaultTransport).
Types ¶
type AppAuth ¶
type AppAuth struct {
// contains filtered or unexported fields
}
AppAuth signs GitHub App JWTs with the app's RSA private key.
func NewAppAuth ¶
NewAppAuth parses a GitHub App private key (PKCS#1 or PKCS#8 PEM, exactly what the App settings page downloads).
type AppAuthTransport ¶
type AppAuthTransport struct {
// contains filtered or unexported fields
}
AppAuthTransport authenticates requests to the Apps API with the app's JWT. It signs at most once per JWT lifetime and reuses the token for every request until shortly before expiry.
func NewAppAuthTransport ¶
func NewAppAuthTransport(app *AppAuth, base http.RoundTripper) (*AppAuthTransport, error)
NewAppAuthTransport wraps base (nil means http.DefaultTransport).
func (*AppAuthTransport) SetClock ¶
func (t *AppAuthTransport) SetClock(now func() time.Time)
SetClock overrides the wall clock (tests).
type InstallationTokenSource ¶
type InstallationTokenSource struct {
// contains filtered or unexported fields
}
InstallationTokenSource mints and caches one installation token. Tokens refresh tokenRefreshLead before expiry; minting is the only GitHub contact (the Apps API, authenticated by the app JWT).
func NewInstallationTokenSource ¶
func NewInstallationTokenSource(installationID int64, apps *github.AppsService) (*InstallationTokenSource, error)
NewInstallationTokenSource wires a token source for one installation.
func (*InstallationTokenSource) SetClock ¶
func (s *InstallationTokenSource) SetClock(now func() time.Time)
SetClock overrides the wall clock (tests).
func (*InstallationTokenSource) Token ¶
func (s *InstallationTokenSource) Token() (string, error)
Token returns a valid installation token, minting a fresh one when the cached token is missing or within the refresh window of its expiry.
type InstallationTokenTransport ¶
type InstallationTokenTransport struct {
// contains filtered or unexported fields
}
InstallationTokenTransport authenticates repository requests with the installation token from src, minting lazily on first use.