auth

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: Apache-2.0 Imports: 40 Imported by: 0

Documentation

Index

Constants

View Source
const (
	TokenExchangeGrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
	AccessTokenType        = "urn:ietf:params:oauth:token-type:access_token"
	AgentTokenType         = "urn:legant:params:oauth:token-type:agent-token"
)

RFC 8693 + Legant token-type identifiers.

Variables

This section is empty.

Functions

func AuthServerMetadataHandler

func AuthServerMetadataHandler(issuerURL string) http.HandlerFunc

AuthServerMetadataHandler serves RFC 8414 OAuth 2.0 Authorization Server Metadata.

func DiscoveryHandler

func DiscoveryHandler(issuerURL string) http.HandlerFunc

DiscoveryHandler serves the OpenID Connect discovery document.

func IsCIMD

func IsCIMD(clientID string) bool

IsCIMD reports whether a client_id is a CIMD URL.

func JWKSHandler

func JWKSHandler(keysFn func() map[string]*rsa.PublicKey) http.HandlerFunc

JWKSHandler serves the JSON Web Key Set. It is backed by a function returning the currently-trusted public keys indexed by kid, so the set reflects key rotation without a restart and publishes every key a verifier may need during an overlap window.

func MintRegistrationToken

func MintRegistrationToken(ctx context.Context, pool *pgxpool.Pool, maxUses int, ttl time.Duration) (string, error)

MintRegistrationToken creates an initial access token, returning the plaintext (shown once). Used by the `legant dcr issue-token` command.

func NewOAuth2Provider

func NewOAuth2Provider(storage *Storage, issuerURL string, activeKey func() *rsa.PrivateKey, systemSecret []byte) fosite.OAuth2Provider

NewOAuth2Provider builds the Fosite OAuth2/OIDC provider. activeKey returns the current signing private key on each call, so key rotation performed in-process is picked up without rebuilding the provider.

func NewSession

func NewSession(subject string) *openid.DefaultSession

NewSession creates a new OpenID Connect session for the given subject, stamping the active signing key id into the token headers.

func SameOrigin

func SameOrigin(r *http.Request) bool

SameOrigin reports whether a state-changing request's Origin/Referer matches its own host. It is a CSRF defense-in-depth on top of SameSite=Lax cookies: a cross-site form POST that does carry an Origin is rejected, closing the same-site/subdomain gap that Lax alone leaves open. A request with no Origin/Referer is allowed (and still protected by the Lax session cookie).

func SetSigningKID

func SetSigningKID(kid string)

SetSigningKID records the active signing key id for stamping on issued tokens.

Types

type AccountHandler

type AccountHandler struct {
	// contains filtered or unexported fields
}

AccountHandler serves the self-service delegation-management UI: a user views the delegations they have granted to agents and can revoke any of them (and the whole sub-agent chain it seeded).

func NewAccountHandler

func NewAccountHandler(ch *chains.Service, sessions *SessionManager, tmpl *template.Template) *AccountHandler

func (*AccountHandler) Delegations

func (h *AccountHandler) Delegations(w http.ResponseWriter, r *http.Request)

Delegations renders the list of the logged-in user's granted delegations.

func (*AccountHandler) Revoke

func (h *AccountHandler) Revoke(w http.ResponseWriter, r *http.Request)

Revoke revokes one delegation (and its sub-agent chain) owned by the user.

type ActorResolver

type ActorResolver func(ctx context.Context, token string) (agentID string, ok bool)

ActorResolver authenticates the acting agent from its presented token.

type AdminHandler

type AdminHandler struct {
	// contains filtered or unexported fields
}

AdminHandler serves superadmin dashboard pages — currently the tamper-evident audit/provenance viewer, the user-facing surface of Legant's most differentiated asset (who-acted-for-whom, provably).

func NewAdminHandler

func NewAdminHandler(pool *pgxpool.Pool, sessions *SessionManager, tmpl *template.Template) *AdminHandler

func (*AdminHandler) Audit

func (h *AdminHandler) Audit(w http.ResponseWriter, r *http.Request)

Audit renders the audit trail with provenance and a chain-verified badge.

type CIMDResolver

type CIMDResolver struct {
	// contains filtered or unexported fields
}

CIMDResolver fetches and validates Client ID Metadata Documents, where the client_id is itself an https URL pointing at the document. CIMD clients are always PUBLIC and rely on S256 PKCE — there is no client secret to leak.

func NewCIMDResolver

func NewCIMDResolver(client *http.Client) *CIMDResolver

func (*CIMDResolver) Resolve

func (r *CIMDResolver) Resolve(ctx context.Context, clientID string) (fosite.Client, error)

Resolve fetches the document at clientID (over the SSRF-hardened client) and returns a public fosite.Client. The document must self-identify with the same URL it was fetched from, and carry valid redirect URIs.

type ConsentHandler

type ConsentHandler struct {
	// contains filtered or unexported fields
}

func NewConsentHandler

func NewConsentHandler(pool *pgxpool.Pool, sessionManager *SessionManager, templates *template.Template) *ConsentHandler

func (*ConsentHandler) LoginPage

func (ch *ConsentHandler) LoginPage(w http.ResponseWriter, r *http.Request)

LoginPage renders the login form.

func (*ConsentHandler) LoginSubmit

func (ch *ConsentHandler) LoginSubmit(w http.ResponseWriter, r *http.Request)

LoginSubmit handles the login form submission.

func (*ConsentHandler) LogoutHandler

func (ch *ConsentHandler) LogoutHandler(w http.ResponseWriter, r *http.Request)

LogoutHandler handles POST /logout.

func (*ConsentHandler) RegisterPage

func (ch *ConsentHandler) RegisterPage(w http.ResponseWriter, r *http.Request)

RegisterPage renders the registration form.

func (*ConsentHandler) RegisterSubmit

func (ch *ConsentHandler) RegisterSubmit(w http.ResponseWriter, r *http.Request)

RegisterSubmit handles the registration form submission.

type DiscoveryDocument

type DiscoveryDocument struct {
	Issuer                            string   `json:"issuer"`
	AuthorizationEndpoint             string   `json:"authorization_endpoint"`
	TokenEndpoint                     string   `json:"token_endpoint"`
	UserinfoEndpoint                  string   `json:"userinfo_endpoint"`
	JwksURI                           string   `json:"jwks_uri"`
	RevocationEndpoint                string   `json:"revocation_endpoint"`
	IntrospectionEndpoint             string   `json:"introspection_endpoint"`
	RegistrationEndpoint              string   `json:"registration_endpoint,omitempty"`
	ScopesSupported                   []string `json:"scopes_supported"`
	ResponseTypesSupported            []string `json:"response_types_supported"`
	ResponseModesSupported            []string `json:"response_modes_supported"`
	GrantTypesSupported               []string `json:"grant_types_supported"`
	SubjectTypesSupported             []string `json:"subject_types_supported"`
	IDTokenSigningAlgValuesSupported  []string `json:"id_token_signing_alg_values_supported"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
	CodeChallengeMethodsSupported     []string `json:"code_challenge_methods_supported"`
	ClaimsSupported                   []string `json:"claims_supported"`
}

func BuildMetadata

func BuildMetadata(issuerURL string) DiscoveryDocument

BuildMetadata constructs the authorization-server metadata shared by the OIDC discovery document and the RFC 8414 endpoint. It advertises only what the server actually implements — notably scopes and claims that are genuinely issued, so clients are not told to expect email/profile claims that the server does not populate.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

func NewHandler

func NewHandler(provider fosite.OAuth2Provider, sessionManager *SessionManager) *Handler

func (*Handler) AuthorizeHandler

func (h *Handler) AuthorizeHandler(w http.ResponseWriter, r *http.Request)

AuthorizeHandler handles GET /oauth2/authorize It checks for an existing session and either shows the login page or proceeds with consent.

func (*Handler) IntrospectHandler

func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request)

IntrospectHandler handles POST /oauth2/introspect

func (*Handler) RevokeHandler

func (h *Handler) RevokeHandler(w http.ResponseWriter, r *http.Request)

RevokeHandler handles POST /oauth2/revoke

func (*Handler) SetExchanger

func (h *Handler) SetExchanger(ex *TokenExchanger)

SetExchanger enables the RFC 8693 token-exchange grant on the token endpoint and delegation-token introspection.

func (*Handler) TokenHandler

func (h *Handler) TokenHandler(w http.ResponseWriter, r *http.Request)

TokenHandler handles POST /oauth2/token

func (*Handler) UserinfoHandler

func (h *Handler) UserinfoHandler(w http.ResponseWriter, r *http.Request)

UserinfoHandler handles GET /oauth2/userinfo

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Use string `json:"use"`
	Kid string `json:"kid"`
	Alg string `json:"alg"`
	N   string `json:"n"`
	E   string `json:"e"`
}

type JWKSResponse

type JWKSResponse struct {
	Keys []JWK `json:"keys"`
}

type LiveHandler

type LiveHandler struct {
	// contains filtered or unexported fields
}

LiveHandler serves the superadmin real-time console: a dashboard shell, a JSON snapshot of the current authority graph, and an SSE stream of live activity events (mints, revocations, gateway decisions) fanned out from the hub.

func NewLiveHandler

func NewLiveHandler(pool *pgxpool.Pool, sessions *SessionManager, tmpl *template.Template, hub *live.Hub, pub *live.Publisher, ingestToken string) *LiveHandler

func (*LiveHandler) Dashboard

func (h *LiveHandler) Dashboard(w http.ResponseWriter, r *http.Request)

Dashboard renders the live console shell.

func (*LiveHandler) Events

func (h *LiveHandler) Events(w http.ResponseWriter, r *http.Request)

Events streams live activity as Server-Sent Events.

func (*LiveHandler) Ingest

func (h *LiveHandler) Ingest(w http.ResponseWriter, r *http.Request)

Ingest accepts an allow/deny decision from a connected resource server (a `legant guard` hook) and publishes it to the live console. It is authenticated by a shared bearer token (not a user session), so an off-box guard with no Legant login can report. Disabled (404) unless a token is configured. The event goes through the cross-process publisher so every replica's console sees it.

func (*LiveHandler) Snapshot

func (h *LiveHandler) Snapshot(w http.ResponseWriter, r *http.Request)

Snapshot returns the current active authority graph as JSON. The console fetches it on load and refreshes it when a mint or revoke arrives.

type Registrar

type Registrar struct {
	// contains filtered or unexported fields
}

Registrar implements RFC 7591 dynamic client registration, gated by an initial access token so the endpoint cannot be used to create clients anonymously.

func NewRegistrar

func NewRegistrar(pool *pgxpool.Pool, clients *client.Service) *Registrar

func (*Registrar) Register

func (rg *Registrar) Register(w http.ResponseWriter, r *http.Request)

Register handles POST /oauth2/register.

type Session

type Session struct {
	ID           string
	UserID       string
	IP           string
	UserAgent    string
	CreatedAt    time.Time
	LastActiveAt time.Time
	ExpiresAt    time.Time
}

type SessionManager

type SessionManager struct {
	// contains filtered or unexported fields
}

func NewSessionManager

func NewSessionManager(pool *pgxpool.Pool, cookieSecret string, lifetime time.Duration, secure bool) *SessionManager

func (*SessionManager) CSRFToken

func (sm *SessionManager) CSRFToken(s *Session) string

CSRFToken returns the synchronizer token bound to a session: an HMAC of the session id under the cookie secret. It is stable for the session's lifetime and requires no extra storage.

func (*SessionManager) Create

func (sm *SessionManager) Create(ctx context.Context, userID string, r *http.Request) (*Session, error)

func (*SessionManager) Delete

func (*SessionManager) Get

func (sm *SessionManager) Get(ctx context.Context, r *http.Request) (*Session, error)

func (*SessionManager) SetCookie

func (sm *SessionManager) SetCookie(w http.ResponseWriter, session *Session)

func (*SessionManager) ValidateCSRF

func (sm *SessionManager) ValidateCSRF(r *http.Request, s *Session) bool

ValidateCSRF reports whether a state-changing request carries the correct CSRF token for its session, taken from the X-CSRF-Token header (JSON/fetch clients) or a csrf_token form field (HTML forms). It only reads the form body for form-encoded requests, so it never consumes a JSON body the handler will parse.

type Storage

type Storage struct {
	// contains filtered or unexported fields
}

Storage implements all fosite storage interfaces needed for our OAuth2/OIDC flows: - fosite.Storage (ClientManager) - oauth2.CoreStorage (AuthorizeCodeStorage, AccessTokenStorage, RefreshTokenStorage) - oauth2.TokenRevocationStorage - pkce.PKCERequestStorage - openid.OpenIDConnectRequestStorage

func NewStorage

func NewStorage(pool *pgxpool.Pool) *Storage

func (*Storage) ClientAssertionJWTValid

func (s *Storage) ClientAssertionJWTValid(ctx context.Context, jti string) error

func (*Storage) CreateAccessTokenSession

func (s *Storage) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error

func (*Storage) CreateAuthorizeCodeSession

func (s *Storage) CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) error

func (*Storage) CreateOpenIDConnectSession

func (s *Storage) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) error

func (*Storage) CreatePKCERequestSession

func (s *Storage) CreatePKCERequestSession(ctx context.Context, signature string, requester fosite.Requester) error

func (*Storage) CreateRefreshTokenSession

func (s *Storage) CreateRefreshTokenSession(ctx context.Context, signature string, accessSignature string, request fosite.Requester) error

func (*Storage) DeleteAccessTokenSession

func (s *Storage) DeleteAccessTokenSession(ctx context.Context, signature string) error

func (*Storage) DeleteOpenIDConnectSession

func (s *Storage) DeleteOpenIDConnectSession(ctx context.Context, authorizeCode string) error

func (*Storage) DeletePKCERequestSession

func (s *Storage) DeletePKCERequestSession(ctx context.Context, signature string) error

func (*Storage) DeleteRefreshTokenSession

func (s *Storage) DeleteRefreshTokenSession(ctx context.Context, signature string) error

func (*Storage) GetAccessTokenSession

func (s *Storage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)

func (*Storage) GetAuthorizeCodeSession

func (s *Storage) GetAuthorizeCodeSession(ctx context.Context, code string, session fosite.Session) (fosite.Requester, error)

func (*Storage) GetClient

func (s *Storage) GetClient(ctx context.Context, id string) (fosite.Client, error)

func (*Storage) GetOpenIDConnectSession

func (s *Storage) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) (fosite.Requester, error)

func (*Storage) GetPKCERequestSession

func (s *Storage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)

func (*Storage) GetRefreshTokenSession

func (s *Storage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)

func (*Storage) InvalidateAuthorizeCodeSession

func (s *Storage) InvalidateAuthorizeCodeSession(ctx context.Context, code string) error

func (*Storage) RevokeAccessToken

func (s *Storage) RevokeAccessToken(ctx context.Context, requestID string) error

func (*Storage) RevokeRefreshToken

func (s *Storage) RevokeRefreshToken(ctx context.Context, requestID string) error

func (*Storage) RotateRefreshToken

func (s *Storage) RotateRefreshToken(ctx context.Context, requestID string, refreshTokenSignature string) error

func (*Storage) SetCIMDResolver

func (s *Storage) SetCIMDResolver(r *CIMDResolver)

SetCIMDResolver enables resolving CIMD (https-URL) client ids by fetching the client metadata document instead of looking the client up in the database.

func (*Storage) SetClientAssertionJWT

func (s *Storage) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error

type SubjectResolver

type SubjectResolver func(ctx context.Context, token string) (userID string, scopes []string, ok bool)

SubjectResolver validates the subject (user) token and returns the user id and the scopes that token was granted (the scope ceiling).

type TokenExchanger

type TokenExchanger struct {
	// contains filtered or unexported fields
}

TokenExchanger implements the RFC 8693 token-exchange grant: it turns a user's subject token plus an agent's actor token into a short-lived composite delegation JWT (sub = user, act = agent), bounded by the delegation's scopes, constraints, and expiry, recorded for revocation, and audited.

func NewTokenExchanger

func NewTokenExchanger(issuer string, ttl time.Duration, keys *keystore.Keystore, ch *chains.Service, rev *revocation.Store, pool *pgxpool.Pool, pub *live.Publisher, actor ActorResolver, subject SubjectResolver) *TokenExchanger

func (*TokenExchanger) Handle

func (x *TokenExchanger) Handle(w http.ResponseWriter, r *http.Request)

Handle processes a token-exchange request. The caller (TokenHandler) has already parsed the form and set no-store headers.

func (*TokenExchanger) IntrospectDelegation

func (x *TokenExchanger) IntrospectDelegation(ctx context.Context, token string) (map[string]any, bool)

IntrospectDelegation answers RFC 7662 introspection for a composite delegation token: it verifies the signature (kid-aware) and consults the revocation store. Returns ok=false when the token is not one of ours (so the caller can fall back to Fosite introspection of normal access tokens).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL