Documentation
¶
Overview ¶
Package authgate implements the auth-phase gate for ATCR's registry JWT issuance. The gate runs once per token request (push or pull): it always reconciles crew membership so first-time CLI users on a private hold can pass the hold-side read check, and additionally enforces hold membership (captain or crew with blob:write) plus storage quota for non-wildcard push scopes. Once the JWT is signed, the registry hot path trusts it for its short lifetime — no per-/v2/-request re-authorization.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Authorizer ¶
type Authorizer struct {
// contains filtered or unexported fields
}
Authorizer enforces hold membership and quota at /auth/token.
Captain/crew checks are served from local Jetstream-fed tables (hold_captain_records, hold_crew_members) so they're sub-millisecond. Quota is a live GET to hold's public io.atcr.hold.getQuota endpoint, which fails open on network errors so a brief hold outage doesn't lock users out of pushing entirely.
func New ¶
func New(db *sql.DB, holdAuthorizer auth.HoldAuthorizer, refresher *oauth.Refresher, defaultHoldDID string, opts ...Option) *Authorizer
New constructs an Authorizer. defaultHoldDID is the AppView's fallback hold (used when a user's sailor profile hasn't recorded one yet). The refresher is required for OAuth-flow service-token minting during EnsureCrewMembership reconciliation; pass nil if OAuth isn't configured.
func (*Authorizer) Authorize ¶
func (a *Authorizer) Authorize(ctx context.Context, did, authMethod string, access []auth.AccessEntry) error
Authorize satisfies token.Authorizer.
authMethod is one of token.AuthMethodOAuth or token.AuthMethodAppPassword and selects which service-token fetcher EnsureCrewMembership uses for reconciliation. We can't infer it from `did` alone because either flow can produce a valid session for the same identity.
Crew reconciliation runs for both push and pull token requests so a first-time CLI user (especially OAuth/credential-helper) doesn't get 403'd on their first pull from a private hold. The remaining gates (membership requirement, quota) only apply to non-wildcard push.
type Option ¶
type Option func(*Authorizer)
Option configures an Authorizer. Use New(..., WithX(...)) to set them.
func WithHTTPClient ¶
WithHTTPClient overrides the HTTP client used for the live quota call. Production wiring leaves this at http.DefaultClient; tests pass an httptest.Server-backed client to drive checkQuota deterministically.
type ServiceAuthFetcher ¶
type ServiceAuthFetcher struct {
// contains filtered or unexported fields
}
ServiceAuthFetcher pre-mints the AppView↔hold service-auth at /auth/token time so the registry JWT can be bound to its lifetime.
Flow: token handler calls Fetch with the requester's DID and chosen auth method, we resolve their hold, call the appropriate service-token fetcher (which caches the result with the PDS-granted exp), and read the cached expiry back. The token handler stamps the JWT's exp from that value, so the JWT and service-auth expire concurrently.