authgate

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 17, 2026 License: MIT Imports: 15 Imported by: 0

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

func WithHTTPClient(c *http.Client) Option

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.

func