aauth

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 18 Imported by: 0

README

auth-go

Go Reference Go Report Card

A Go implementation of the AAuth protocoldraft-hardt-oauth-aauth-protocol (tracking -09) — giving AI agents their own cryptographic identity and a clean authorization model across trust domains: no shared secrets, no per-server pre-registration. Every agent holds its own Ed25519 key and a self-describing token that binds it; any party can verify the token and every request it signs.

To our knowledge this is the first Go implementation of the protocol (the draft's §17 Implementation Status lists TypeScript, .NET, Python, and Java).

Companion specs implemented against: signature-key-04 · aauth-bootstrap-01. For an interactive tour of the protocol, see explorer.aauth.dev.

Contents

Install

go get github.com/aauth-dev/auth-go

Requires Go 1.24+. Full API reference: pkg.go.dev/github.com/aauth-dev/auth-go.

import aauth "github.com/aauth-dev/auth-go"

Quick start

Agent — ask a Person Server before acting:

id, _ := aauth.ParseAgentIdentifier("aauth:claude-code@devbox.local")
agent, _ := aauth.NewAgent(id, aauth.WithPersonServer("http://127.0.0.1:7421"))

ps := aauth.NewPSClient("http://127.0.0.1:7421", agent)
res, err := ps.RequestPermission(ctx, aauth.PermissionRequest{
    Action:      "WriteFile",
    Description: "write the deploy config",
    Parameters:  map[string]any{"path": "/tmp/deploy.yaml"},
})
// res.Granted() reports the decision; res.Reason explains a denial.
// A 202 deferred response (a human deciding) is followed automatically.

Agent, transparently — wrap an http.Client and AAuth disappears; the transport signs each request and turns 401 challenges into token exchanges:

hc := &http.Client{Transport: aauth.NewTransport(agent, ps)}
resp, err := hc.Get("https://files.example/files") // signed, challenged, retried

Server — authenticate an agent behind any endpoint:

claims, err := aauth.VerifyAndExtractAgent(ctx, req, aauth.VerifyAgentTokenOptions{
    Resolver: aauth.SelfSignedResolver{}, // or JWKSResolver / StaticResolver
})
// claims.Subject, claims.IsSubAgent(), claims.Cnf.JWK — identity established;
// your policy layer decides what it may do.

More runnable examples render on pkg.go.dev.

API overview

The root package is the stable protocol vocabulary. Grouped by role:

Area Key symbols
Identity Agent, NewAgent, Agent.MintToken, Agent.MintSubAgentToken, ParseAgentIdentifier
Signing SignRequest, AttachSignatureKey, VerifyRequest
Verification / trust VerifyAndExtractAgent, VerifyAgentToken, KeyResolver · JWKSResolver · StaticResolver · SelfSignedResolver
Agent client PSClient (RequestPermission, ExchangeToken, Audit), Transport
Resource side IssueResourceToken, ChallengeAuthToken, VerifyAndExtractAuth
Delegation RouteDownstream, ActClaim, NextAct
Deferred / interaction DoDeferred, Requirement, WriteClarification, interactioncode

Protocol coverage

AAuth involves four participants — an Agent making signed requests, a Resource (the protected API), a Person Server (PS) representing the user, and an Access Server (AS) enforcing access policy — and stacks three layers: proving who the agent is, deciding what it may access, and optionally governing what it is doing and why. These tables track how much of each is implemented.

Legend: ✅ implemented & tested · 🟡 partial · ⬜ planned · ⛔ out of scope for now

Roles
Role Status What exists
Agent identity, token minting, permission client, and a protocol-aware http.RoundTripper (Transport): auto-signing, challenge handling, token exchange with deferred waits, per-resource token cache, AAuth-Access lifecycle
Resource agent + auth-token authentication, resource-token issuing, 401 challenges, AAuth-Access two-party flow
Person Server 🟡 permission, token exchange, audit, clarification, deferred responses; mission lifecycle pending
Access Server four-party federation not yet implemented
Layer 1 — Identity
Capability Status
Agent identifiers (aauth:name@domain; sub-agents name+worker@domain, single-level rule)
Agent tokens — sig=jwt (-09 claim set: iss dwk sub jti cnf iat exp ps parent_agent, kid header)
Self-hosted agents (agent as its own AP, bootstrap §4.3)
Verification (§5.2.4) — pluggable trust: JWKS discovery / pinned keys / self-signed
HTTP Message Signatures profile (@method @authority @path signature-key + content-digest)
Signature-Key scheme jwt
Error model (Signature-Error + RFC 9457 problem bodies)
Signature-Key schemes hwk / jkt-jwt / jwks_uri; two-key AP minting
Signature-Key scheme x509
Layer 2 — Resource access
Access mode Status
Identity-Based (requirement=agent-token)
Resource-Managed (two-party; AAuth-Access, signature-bound, rolling refresh)
PS-Asserted (three-party; challenge → PS token exchange → auth token)
Resource tokens (aa-resource+jwt): issue + verify (§6.7.2) + agent-side challenge verify (§6.7.3)
Auth tokens (aa-auth+jwt): -09 claim set, verification incl. cnf request binding (§9.4)
AAuth-Requirement header codec
Federated (four-party; Access Server)
Rich Resource Requests (R3)
Layer 3 — Governance
Capability Status
Permission endpoint (§7.4) — with or without a mission
Deferred responses (202 / Location / Retry-After / Prefer: wait, 429 backoff)
Audit endpoint (§7.5) + mission-status errors (§8.6)
Clarification chat (§7.3): question → answer / updated-request / cancel
Call chaining (§10.1) + act delegation chain (§10.3)
Interaction chaining (§10.1.2)
Interaction codes (Crockford base32)
Mission lifecycle: proposal, approval, scoped access, completion

Design notes

  • Pluggable trust. KeyResolver lets the same verification code serve public JWKS discovery, pinned keys (offline / air-gapped), or local self-signed agents. Strict RequireProviderClaims enforces §5.2.4 (iss HTTPS URL, dwk, jti) for cross-domain interop.
  • The RFC 9421 + Signature-Key layer is isolated in httpsig.go — the reference implementations externalize it too, so a signature-key draft bump stays contained.
  • Sub-agent authorization is a policy hook (IsSubAgent, ErrSubAgentDirect), not hard-coded — the PS decides how to enforce "the parent requests on behalf of the sub-agent."
  • Planned package split mirrors the reference TS monorepo: agent/, server/, keys/ (two-key minting, hardware backends), with the root package staying the stable protocol vocabulary.

Testing

go test ./...

White-box unit tests cover token round-trips (including wrong-typ and missing-claim rejection), tampered-@path and swapped-Signature-Key rejection, the stolen-AAuth-Access replay guard, sub-agent rules, JWKS discovery, and full end-to-end flows (three-party exchange, call chaining, clarification dialog) against live httptest servers. Runnable package aauth_test examples double as public-API documentation. Golden wire vectors as a cross-implementation conformance suite are planned.

License

MIT — matching the reference AAuth implementations.

Documentation

Overview

Package aauth implements the AAuth protocol (draft-hardt-oauth-aauth-protocol-09): agent identity and authorization across trust domains, without shared secrets or per-server pre-registration. Every agent gets its own Ed25519 keypair and a self-describing token that binds that key; any party can verify it.

Layers

AAuth stacks three concerns, each usable independently:

Deployment shapes

The self-hosted / local-agent shape is a first-class target: the agent is its own agent provider (draft-hardt-aauth-bootstrap-01 §4.3), which is what autonomous coding agents on developer machines are. Verification trust is pluggable via KeyResolver: JWKSResolver for public discovery, StaticResolver for pinned keys (offline / air-gapped), and SelfSignedResolver for local agents.

This is, to our knowledge, the first Go implementation of the protocol.

Example

Example shows the two ends of the cooperative permission flow: an agent asks a Person Server before acting, and the server authenticates the agent.

package main

import (
	"context"
	"fmt"
	"net/http"

	aauth "github.com/aauth-dev/auth-go"
)

func main() {
	// Agent side: mint an identity and ask before acting.
	id, _ := aauth.ParseAgentIdentifier("aauth:claude-code@devbox.local")
	agent, _ := aauth.NewAgent(id, aauth.WithPersonServer("http://127.0.0.1:7421"))

	ps := aauth.NewPSClient("http://127.0.0.1:7421", agent)
	_, err := ps.RequestPermission(context.Background(), aauth.PermissionRequest{
		Action:      "WriteFile",
		Description: "write the deploy config",
		Parameters:  map[string]any{"path": "/tmp/deploy.yaml"},
	})
	_ = err // res.Granted() reports the decision; a 202 is followed automatically.

	// Server side: authenticate the agent behind an endpoint.
	_ = func(w http.ResponseWriter, r *http.Request) {
		claims, err := aauth.VerifyAndExtractAgent(r.Context(), r, aauth.VerifyAgentTokenOptions{
			Resolver: aauth.SelfSignedResolver{},
		})
		if err != nil {
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}
		fmt.Fprintf(w, "authenticated %s", claims.Subject)
	}
}

Index

Examples

Constants

View Source
const (
	TypAgent    = "aa-agent+jwt"
	TypResource = "aa-resource+jwt"
	TypAuth     = "aa-auth+jwt"
)

JWT typ header values (draft -09 §5.2.2, §6, §7).

View Source
const (
	WellKnownAgent    = "aauth-agent.json"
	WellKnownPerson   = "aauth-person.json"
	WellKnownResource = "aauth-resource.json"
	WellKnownAccess   = "aauth-access.json"
)

Well-known metadata document names (draft -09 §4; used as the dwk claim and as /.well-known/{name} paths).

View Source
const (
	HeaderSignatureKey   = "Signature-Key"
	HeaderSignatureInput = "Signature-Input"
	HeaderSignature      = "Signature"
	HeaderSignatureError = "Signature-Error"
	HeaderPrefer         = "Prefer"
	HeaderRetryAfter     = "Retry-After"
	HeaderLocation       = "Location"
)

HTTP header names.

View Source
const (
	ActionClarificationResponse = "clarification_response"
	ActionUpdatedRequest        = "updated_request"
)

Clarification response actions (§7.3.2).

View Source
const (
	PermissionGranted = "granted"
	PermissionDenied  = "denied"
)

Permission values in a PermissionResponse.

View Source
const (
	RequirementAgentToken    = "agent-token"
	RequirementAuthToken     = "auth-token"
	RequirementInteraction   = "interaction"
	RequirementClarification = "clarification"
	RequirementClaims        = "claims"
)

Requirement values used in the AAuth-Requirement header (draft -09 §12.3).

View Source
const (
	SigErrUnsupportedAlgorithm = "unsupported_algorithm"
	SigErrInvalidSignature     = "invalid_signature"
	SigErrInvalidInput         = "invalid_input"
	SigErrKeyNotFound          = "key_not_found"
	SigErrInvalidKey           = "invalid_key"
	SigErrExpiredSignature     = "expired_signature"
)

Signature-Error codes (signature-key draft §5.4).

View Source
const ContentDigestAlg = "sha-256"

ContentDigestAlg is the digest algorithm for the Content-Digest header.

View Source
const DefaultSignatureLabel = "sig"

DefaultSignatureLabel is the signature label used by this implementation. Draft examples use "sig"; RFC 9421 labels are correlated by equality across Signature-Input, Signature, and Signature-Key (signature-key §3).

View Source
const HeaderRequirement = "AAuth-Requirement"

HeaderRequirement is the AAuth-Requirement header name.

View Source
const (
	MaxAgentTokenTTLSeconds = 24 * 60 * 60
)

Recommended lifetimes (draft -09 §5.2.2: agent tokens SHOULD NOT exceed 24h).

Variables

View Source
var (
	// ErrInvalidToken means a JWT was malformed or failed verification.
	ErrInvalidToken = errors.New("aauth: invalid token")
	// ErrWrongTokenType means the JWT typ header was not the expected value.
	ErrWrongTokenType = errors.New("aauth: wrong token type")
	// ErrSignatureInvalid means an HTTP message signature failed to verify.
	ErrSignatureInvalid = errors.New("aauth: signature invalid")
	// ErrExpired means a token's exp claim is in the past.
	ErrExpired = errors.New("aauth: token expired")
	// ErrMissingSigKey means the request had no Signature-Key header.
	ErrMissingSigKey = errors.New("aauth: missing Signature-Key header")
	// ErrBadSigKey means the Signature-Key header was malformed.
	ErrBadSigKey = errors.New("aauth: malformed Signature-Key header")
	// ErrMissingClaim means a required JWT claim was absent.
	ErrMissingClaim = errors.New("aauth: required claim missing")
	// ErrSubAgentDirect means a sub-agent tried to request authorization
	// itself; its parent must request on its behalf (draft -09 §10.2).
	ErrSubAgentDirect = errors.New("aauth: sub-agent must not request authorization directly")
	// ErrUnknownAction means a clarification POST had a missing or
	// unrecognized action member (draft -09 §7.3.2).
	ErrUnknownAction = errors.New("aauth: missing or unrecognized action")
)

Sentinel errors returned across the package; match with errors.Is.

Functions

func AttachSignatureKey

func AttachSignatureKey(req *http.Request, token string)

AttachSignatureKey sets the Signature-Key header carrying the agent (or auth) token via scheme=jwt (signature-key draft §3.6). The dictionary key is the signature label (§3: labels correlate across Signature-Input, Signature, and Signature-Key).

func ChainInteraction

func ChainInteraction(w http.ResponseWriter, pendingURL, interactionURL, code string)

Interaction chaining (draft -09 §10.1.2): when a resource acting as an agent receives a downstream requirement=interaction it cannot satisfy itself, it propagates the interaction to its own caller by returning its own 202 — its own Location (for the caller to poll) and its own interaction code. When the user completes the interaction and the resource obtains the downstream token, it finishes the original request at its pending URL.

ChainInteraction writes that propagating 202. pendingURL and code are the resource's own (not the downstream's); status defaults to "pending".

func ChallengeAuthToken

func ChallengeAuthToken(w http.ResponseWriter, resourceToken string)

ChallengeAuthToken builds the 401 response headers for requirement=auth-token.

func DoDeferred

func DoDeferred(ctx context.Context, hc *http.Client, req *http.Request, opts DeferredOptions) (*http.Response, error)

DoDeferred executes req and follows the §12.4 state machine until a terminal (non-202) response. The caller owns closing the returned body.

func FetchMetadata

func FetchMetadata(ctx context.Context, hc *http.Client, base, doc string, dst any) error

FetchMetadata GETs {base}/.well-known/{doc} and decodes into dst.

func FollowDeferred

func FollowDeferred(ctx context.Context, hc *http.Client, reqURL *url.URL, res *http.Response, opts DeferredOptions) (*http.Response, error)

FollowDeferred continues the §12.4 state machine from an already-received response: if res is not a 202 it is returned unchanged; otherwise the pending URL is polled until a terminal response arrives. reqURL is the URL the original request was sent to (for same-origin Location resolution).

func IssueResourceToken

func IssueResourceToken(resourceURL, audience string, agent *AgentClaims, scope string, priv ed25519.PrivateKey, kid string) (string, error)

IssueResourceToken is the resource-side helper for the 401 challenge (§6.6): mint an aa-resource+jwt for the agent that just called, addressed to its PS (three-party) or an AS (four-party).

func MintAgentToken

func MintAgentToken(claims AgentClaims, priv ed25519.PrivateKey, kid string) (string, error)

MintAgentToken signs an aa-agent+jwt. The kid header identifies the signing key within the provider's JWKS (draft -09 §5.2.2: header MUST carry kid).

func MintAuthToken

func MintAuthToken(claims AuthClaims, priv ed25519.PrivateKey, kid string) (string, error)

MintAuthToken signs an aa-auth+jwt (Person Server side).

func MintResourceToken

func MintResourceToken(claims ResourceClaims, priv ed25519.PrivateKey, kid string) (string, error)

MintResourceToken signs an aa-resource+jwt (resource side).

func ParseSignatureKey

func ParseSignatureKey(req *http.Request) (string, error)

ParseSignatureKey extracts the scheme=jwt token for the default label.

func SignRequest

func SignRequest(req *http.Request, priv ed25519.PrivateKey, keyid string) error

SignRequest signs req per the AAuth profile: sets Content-Digest when a body is present, then Signature-Input and Signature under the default label. The Signature-Key header MUST already be attached (it is a covered component). keyid is set to the signing key's RFC 7638 thumbprint.

func VerifyRequest

func VerifyRequest(req *http.Request, pub ed25519.PublicKey) error

VerifyRequest verifies the HTTP message signature against pub, requiring the mandated component coverage.

func WriteClarification

func WriteClarification(w http.ResponseWriter, pendingURL, question string, timeout int, options []string)

WriteClarification writes a requirement=clarification 202 (§7.3.1) asking the recipient a question. options may be nil; timeout 0 omits the field.

func WriteMissionStatusError

func WriteMissionStatusError(w http.ResponseWriter, missionStatus string)

WriteMissionStatusError writes the §8.6 error response (server side).

func WriteSignatureError

func WriteSignatureError(w http.ResponseWriter, status int, e SignatureError, detail string)

WriteSignatureError writes the Signature-Error header plus an RFC 9457 problem+json body. status is typically 400; use 401 for recoverable errors (unsupported_algorithm, invalid_input) the client can retry corrected.

Policy denials after successful verification are NOT signature errors — return a plain 403 without this header (§5.3).

Types

type ActClaim

type ActClaim struct {
	Agent string    `json:"agent"`         // the immediate upstream agent's identifier
	Act   *ActClaim `json:"act,omitempty"` // the next node up the chain, if any
}

ActClaim is a node in the delegation chain (§10.3). Agent is the aauth: identifier of the immediate upstream agent — the intermediary resource in call chaining, or the parent in sub-agent authorization. If that agent was itself delegated to, its upstream is the nested Act. The + delimiter in an AAuth identifier distinguishes sub-agent from call-chain relationships, so no separate type field is needed. The presenter's own identity is in the top-level agent claim and is not repeated inside act.

func NextAct

func NextAct(upstreamAgent string, upstreamAct *ActClaim) *ActClaim

NextAct builds the delegation chain (§10.3) for a downstream auth token the recipient (PS/AS) is about to issue to the intermediary. upstreamAgent is the immediate upstream agent (the caller that presented the upstream token); upstreamAct is that upstream token's own act claim, if any, which becomes the nested tail.

func (*ActClaim) Delegators

func (a *ActClaim) Delegators() []string

Delegators returns the chain of upstream agent identifiers, nearest first.

type Agent

type Agent struct {
	// ID is the agent identifier (the token's sub), stable across rotations.
	ID AgentIdentifier
	// Issuer is the agent provider URL. For a self-hosted agent this is a
	// domain the operator controls, publishing /.well-known/aauth-agent.json.
	// Empty for purely local deployments verified via SelfSignedResolver.
	Issuer string
	// PS is the agent's Person Server URL (optional ps claim).
	PS string
	// TokenTTL bounds minted tokens; capped at 24h per draft -09 §5.2.2.
	TokenTTL time.Duration

	// Priv is the agent's Ed25519 private key. It signs both self-issued
	// agent tokens and HTTP messages; its public half appears in cnf.jwk.
	Priv ed25519.PrivateKey
	// Pub is the corresponding Ed25519 public key.
	Pub ed25519.PublicKey
}

Agent is a self-hosted agent: it acts as its own agent provider and self-issues agent tokens signed by its published key (draft-hardt-aauth-bootstrap-01 §4.3). One key serves both as the AP signing key and as the cnf.jwk HTTP-signing key.

func NewAgent

func NewAgent(id AgentIdentifier, opts ...AgentOption) (*Agent, error)

NewAgent generates a fresh Ed25519 keypair for the given identifier.

func (*Agent) JWK

func (a *Agent) JWK() JWK

JWK returns the agent's public key with Kid = RFC 7638 thumbprint.

func (*Agent) JWKS

func (a *Agent) JWKS() JWKS

JWKS returns the one-key set a self-hosted agent publishes at its jwks_uri.

func (*Agent) MintSubAgentToken

func (a *Agent) MintSubAgentToken(discriminator string) (string, error)

MintSubAgentToken issues a token for a short-lived worker under this agent (draft -09 §10.2): sub = aauth:name+discriminator@domain, parent_agent set. The sub-agent shares consent obtained by the parent but stays individually identifiable for audit and revocation.

func (*Agent) MintToken

func (a *Agent) MintToken() (string, error)

MintToken self-issues an aa-agent+jwt with the draft -09 claim set: iss, dwk, sub, jti, cnf.jwk, iat, exp (+ ps when configured).

func (*Agent) Thumbprint

func (a *Agent) Thumbprint() string

Thumbprint is the agent key's RFC 7638 thumbprint.

type AgentClaims

type AgentClaims struct {
	DWK         string `json:"dwk"`                    // well-known doc name for key discovery
	PS          string `json:"ps,omitempty"`           // the agent's Person Server URL (optional)
	ParentAgent string `json:"parent_agent,omitempty"` // parent id; set marks a sub-agent
	Cnf         Cnf    `json:"cnf"`                    // confirmation claim carrying the agent's public key
	// RegisteredClaims carries iss, sub, jti, iat, exp.
	jwt.RegisteredClaims
}

AgentClaims is the payload of an aa-agent+jwt (draft -09 §5.2.2).

Required: iss (agent provider HTTPS URL), dwk ("aauth-agent.json"), sub (agent identifier), jti, cnf.jwk, iat, exp. Optional: ps (Person Server URL), parent_agent (sub-agent marker §10.2).

func VerifyAgentToken

func VerifyAgentToken(ctx context.Context, token string, opts VerifyAgentTokenOptions) (*AgentClaims, error)

VerifyAgentToken verifies an aa-agent+jwt per draft -09 §5.2.4 and returns its claims. The caller still MUST verify the HTTP message signature against claims.Cnf.JWK (step 5) — see VerifyRequest.

func VerifyAndExtractAgent

func VerifyAndExtractAgent(ctx context.Context, req *http.Request, opts VerifyAgentTokenOptions) (*AgentClaims, error)

VerifyAndExtractAgent is the server-side entry point: parse Signature-Key, verify the agent token (via opts.Resolver), then verify the HTTP message signature against the token's cnf.jwk (draft -09 §5.2.4 steps 1–5).

func (*AgentClaims) IsSubAgent

func (c *AgentClaims) IsSubAgent() bool

IsSubAgent reports whether the token marks a sub-agent (draft -09 §10.2). Sub-agents MUST NOT request authorization directly.

type AgentIdentifier

type AgentIdentifier struct {
	Name          string // the agent name
	Discriminator string // sub-agent discriminator; non-empty marks a sub-agent
	Domain        string // the agent's domain
}

AgentIdentifier is the parsed form of an AAuth agent identifier:

aauth:<name>@<domain>                    — a top-level agent
aauth:<name>+<discriminator>@<domain>    — a sub-agent (draft -09 §10.2)

The identifier is stable across key rotations (it is the token's sub); keys are conveyed separately via cnf.jwk.

func ParseAgentIdentifier

func ParseAgentIdentifier(s string) (AgentIdentifier, error)

ParseAgentIdentifier parses an "aauth:" identifier string.

Example

ExampleParseAgentIdentifier parses an agent identifier into its parts.

package main

import (
	"fmt"

	aauth "github.com/aauth-dev/auth-go"
)

func main() {
	id, _ := aauth.ParseAgentIdentifier("aauth:claude-code@devbox.local")
	fmt.Println(id.Name, id.Domain, id.IsSubAgent())
}
Output:
claude-code devbox.local false

func (AgentIdentifier) IsSubAgent

func (a AgentIdentifier) IsSubAgent() bool

IsSubAgent reports whether the identifier carries a sub-agent discriminator.

func (AgentIdentifier) Parent

func (a AgentIdentifier) Parent() (AgentIdentifier, error)

Parent returns the parent identifier of a sub-agent.

func (AgentIdentifier) String

func (a AgentIdentifier) String() string

String renders the canonical identifier form.

func (AgentIdentifier) SubAgent

func (a AgentIdentifier) SubAgent(discriminator string) (AgentIdentifier, error)

SubAgent derives a sub-agent identifier under this agent (draft -09 §10.2: single level only — deriving from a sub-agent is an error).

Example

ExampleAgentIdentifier_SubAgent derives a short-lived worker under an orchestrating agent (draft -09 §10.2).

package main

import (
	"fmt"

	aauth "github.com/aauth-dev/auth-go"
)

func main() {
	parent, _ := aauth.ParseAgentIdentifier("aauth:orchestrator@example.com")
	worker, _ := parent.SubAgent("search-1")
	fmt.Println(worker)
	fmt.Println(worker.IsSubAgent())
}
Output:
aauth:orchestrator+search-1@example.com
true

type AgentOption

type AgentOption func(*Agent)

AgentOption configures NewAgent.

func WithIssuer

func WithIssuer(iss string) AgentOption

WithIssuer sets the agent-provider URL (self-hosted domain).

func WithKey

func WithKey(priv ed25519.PrivateKey) AgentOption

WithKey uses an existing Ed25519 private key instead of generating one.

func WithPersonServer

func WithPersonServer(ps string) AgentOption

WithPersonServer sets the ps claim.

func WithTokenTTL

func WithTokenTTL(d time.Duration) AgentOption

WithTokenTTL overrides the minted-token lifetime (capped at 24h).

type AgentProviderMetadata

type AgentProviderMetadata struct {
	Issuer  string `json:"issuer,omitempty"` // the provider's issuer URL
	JWKSURI string `json:"jwks_uri"`         // URL of the token-signing JWKS
}

AgentProviderMetadata is /.well-known/aauth-agent.json — published by an agent provider (or by a self-hosted agent acting as its own provider) so verifiers can discover the token-signing JWKS.

type AuditRequest

type AuditRequest struct {
	// Mission binds the record to the mission log (REQUIRED).
	Mission MissionRef `json:"mission"`
	// Action identifies what was performed (REQUIRED).
	Action string `json:"action"`
	// Description says what was done and the outcome (Markdown, optional).
	Description string `json:"description,omitempty"`
	// Parameters are the arguments that were used.
	Parameters map[string]any `json:"parameters,omitempty"`
	// Result carries the outcome of the action.
	Result map[string]any `json:"result,omitempty"`
}

AuditRequest is the body of POST {audit_endpoint} (§7.5.1).

type AuthClaims

type AuthClaims struct {
	DWK     string      `json:"dwk"`               // well-known doc name for key discovery
	Agent   string      `json:"agent"`             // the authorized agent's identifier
	Scope   string      `json:"scope,omitempty"`   // authorized scopes, space-separated
	Cnf     Cnf         `json:"cnf"`               // confirmation claim binding the agent's key
	Mission *MissionRef `json:"mission,omitempty"` // mission context, when issued under one
	Tenant  string      `json:"tenant,omitempty"`  // tenant identifier (enterprise deployments)
	// Act records the upstream delegation chain (§10.3, RFC 8693 §4.1).
	// Absent for a directly-obtained token; present after call chaining or
	// sub-agent authorization.
	Act *ActClaim `json:"act,omitempty"`
	jwt.RegisteredClaims
}

AuthClaims is the payload of an aa-auth+jwt (draft -09 §9.4.1) — issued by a PS (three-party, dwk=aauth-person.json) or AS (four-party, dwk=aauth-access.json), asserting identity and/or consent. Bound to the agent's key via cnf.jwk; aud is the resource. At least one of sub or scope MUST be present. Lifetime MUST NOT exceed 1 hour.

func VerifyAndExtractAuth

func VerifyAndExtractAuth(ctx context.Context, req *http.Request, resourceURL string, resolver KeyResolver) (*AuthClaims, error)

VerifyAndExtractAuth authenticates a resource request signed with an auth token in Signature-Key (§9.4.2): verify the token (issuer trust via resolver), then the HTTP message signature against its cnf.jwk.

func VerifyAuthToken

func VerifyAuthToken(ctx context.Context, token, resourceURL string, resolver KeyResolver) (*AuthClaims, error)

VerifyAuthToken verifies an aa-auth+jwt per §9.4.3 from the resource's perspective: issuer trust via resolver, aud = this resource, at least one of sub/scope, 1-hour lifetime cap. Request-context binding (cnf.jwk vs the HTTP signature) is completed by VerifyAndExtractAuth.

type ChainRouter

type ChainRouter struct {
	// Endpoint is the PS (or AS) base URL to route the downstream request to.
	Endpoint string
	// UpstreamToken is the raw upstream auth token to send as upstream_token.
	UpstreamToken string
	// Governed reports whether a PS with mission context is in the loop
	// (mission present, or the upstream issuer was a PS). When false, the
	// upstream was an AS with no governance context.
	Governed bool
}

ChainRouter tells an intermediary where to send a downstream token request and what to carry, derived from the upstream auth token per §10.1.1.

func RouteDownstream

func RouteDownstream(upstream *AuthClaims, rawUpstream string, isPS func(iss string) bool) (ChainRouter, error)

RouteDownstream computes the routing for a downstream call from the verified upstream auth token and its raw form (§10.1.1):

  • mission present → route to mission.approver (the governed path; the PS sees the full delegation chain);
  • no mission, upstream iss is a PS → route to that PS;
  • no mission, upstream iss is an AS → route to that AS (no governance).

The ps claim in the calling agent's token is NOT used — the upstream auth token is authoritative. isPS reports whether a given issuer URL is a PS (vs an AS); pass nil to treat every issuer as a PS (three-party default).

type Clarification

type Clarification struct {
	Question string   // the Markdown question to answer
	Timeout  int      // seconds until the server times out the request (0 if unset)
	Options  []string // discrete answer choices, when the question has them
}

Clarification is a question posed during a deferred flow (§7.3.1) that the agent must answer before the request can proceed.

type ClarificationPost

type ClarificationPost struct {
	Action                string `json:"action"`                           // clarification_response or updated_request
	ClarificationResponse string `json:"clarification_response,omitempty"` // the answer text
	ResourceToken         string `json:"resource_token,omitempty"`         // replacement resource token
	Justification         string `json:"justification,omitempty"`          // reason for an updated request
}

ClarificationPost is a parsed agent response to a clarification (§7.3.2), read from a POST to the pending URL. Action is one of ActionClarificationResponse or ActionUpdatedRequest.

func ParseClarificationPost

func ParseClarificationPost(r *http.Request) (*ClarificationPost, error)

ParseClarificationPost decodes a POST body to the pending URL and validates the action member (§7.3.2: a missing or unrecognized action is a 400).

type ClarificationReply

type ClarificationReply struct {
	Text          string // the answer, for a clarification_response
	ResourceToken string // a replacement resource token, for an updated_request
	Justification string // optional reason accompanying an updated_request
	Cancel        bool   // withdraw the request (DELETE the pending URL)
}

ClarificationReply is the agent's answer to a Clarification (§7.3.2):

  • Text set → clarification_response (answer the question).
  • ResourceToken set → updated_request (replace the request; the new resource token MUST share iss/agent/agent_jkt with the original).
  • Cancel true → DELETE the pending URL, withdrawing the request.

type Cnf

type Cnf struct {
	JWK *JWK   `json:"jwk,omitempty"` // the confirmation public key
	JKT string `json:"jkt,omitempty"` // RFC 7638 thumbprint (alternative to JWK)
}

Cnf is the JWT confirmation claim (RFC 7800). Agent tokens carry the full public key in JWK; other token types may bind by thumbprint via JKT.

type DeferredOptions

type DeferredOptions struct {
	// PreferWaitSeconds is sent as `Prefer: wait=N` on polling GETs to
	// request long-poll behavior. 0 omits the header.
	PreferWaitSeconds int
	// DefaultPollInterval applies when a 202 carries no Retry-After
	// (spec default: 5s).
	DefaultPollInterval time.Duration
	// MaxPolls bounds the polling loop as a safety valve. 0 = unbounded
	// (the context deadline is then the only limit).
	MaxPolls int
	// Sign re-signs each polling GET. Deferred polling of a signed endpoint
	// keeps presenting the agent's identity; leave nil for unsigned polls.
	Sign func(*http.Request) error
	// OnRequirement is invoked once per distinct AAuth-Requirement header
	// seen on a 202 (e.g. requirement=interaction; url=…; code=…) so the
	// caller can surface the interaction to the user while polling continues.
	OnRequirement func(Requirement)
	// OnClarification answers a requirement=clarification 202 (§7.3): given
	// the question, it returns the agent's reply. If nil, a clarification
	// requirement is treated as an ordinary pending state (polling continues
	// without answering — which will eventually time out server-side).
	OnClarification func(Clarification) (ClarificationReply, error)
}

DeferredOptions tunes DoDeferred.

type JWK

type JWK struct {
	Kty string `json:"kty"`           // key type; "OKP" for Ed25519
	Crv string `json:"crv,omitempty"` // curve; "Ed25519"
	X   string `json:"x,omitempty"`   // base64url-encoded public key
	Kid string `json:"kid,omitempty"` // key id; the RFC 7638 thumbprint
	Alg string `json:"alg,omitempty"` // algorithm; "EdDSA"
	Use string `json:"use,omitempty"` // intended use; "sig"
}

JWK is a minimal JSON Web Key. Ed25519 OKP keys are the AAuth baseline (draft -09 §12.7.1: EdDSA/Ed25519 MUST; P-256 SHOULD — P-256 can be added without breaking this shape).

func NewEd25519JWK

func NewEd25519JWK(pub ed25519.PublicKey) JWK

NewEd25519JWK builds a JWK from an Ed25519 public key with Kid set to the RFC 7638 thumbprint.

func (JWK) PublicKey

func (j JWK) PublicKey() (ed25519.PublicKey, error)

PublicKey decodes the JWK to an Ed25519 public key.

func (JWK) Thumbprint

func (j JWK) Thumbprint() string

Thumbprint computes the RFC 7638 thumbprint (RFC 8037 §2 member set for OKP: crv, kty, x — lexicographic, no whitespace), base64url-encoded.

type JWKS

type JWKS struct {
	Keys []JWK `json:"keys"` // the key set
}

JWKS is a JSON Web Key Set, served at the URL published as jwks_uri in a well-known metadata document.

type JWKSResolver

type JWKSResolver struct {
	HTTPClient *http.Client // client for discovery fetches; nil uses http.DefaultClient
}

JWKSResolver verifies token signatures via the signature-key draft §3.6 discovery chain: {iss}/.well-known/{dwk} → jwks_uri → key by kid.

func (JWKSResolver) ResolveKey

func (r JWKSResolver) ResolveKey(ctx context.Context, iss, dwk, kid string, _ *JWK) (ed25519.PublicKey, error)

ResolveKey implements KeyResolver.

type KeyResolver

type KeyResolver interface {
	ResolveKey(ctx context.Context, iss, dwk, kid string, cnf *JWK) (ed25519.PublicKey, error)
}

KeyResolver resolves the token-signature verification key for an issuer.

Deployments choose the trust model (signature-key draft §3.6 step 5):

  • JWKSResolver: fetch {iss}/.well-known/{dwk} → jwks_uri → key by kid.
  • StaticResolver: pre-configured issuer keys (air-gapped / pinned).
  • SelfSignedResolver: verify against the token's own cnf.jwk — the self-hosted/local shape where possession of the cnf key IS the identity and the verifier applies its own policy per agent.

type MissionRef

type MissionRef struct {
	Approver string `json:"approver"` // HTTPS URL of the entity that approved the mission
	S256     string `json:"s256"`     // base64url SHA-256 of the approved mission JSON
}

MissionRef binds a request to a mission (draft -09 §7.4.1): approver is the PS URL that approved the mission; s256 is the mission digest.

type MissionStatusError

type MissionStatusError struct {
	Code          string `json:"error"`          // e.g. "mission_terminated"
	MissionStatus string `json:"mission_status"` // e.g. "terminated"
}

MissionStatusError is the §8.6 error body a PS returns when a request references a mission that is no longer active. The agent MUST stop acting on the mission.

func (*MissionStatusError) Error

func (e *MissionStatusError) Error() string

Error implements the error interface.

type PSClient

type PSClient struct {
	// BaseURL of the PS, e.g. "http://127.0.0.1:7421". Endpoint URLs are
	// discovered from metadata when available; PermissionEndpoint overrides.
	BaseURL string
	// PermissionEndpoint overrides discovery (defaults to BaseURL+"/permission").
	PermissionEndpoint string
	// TokenEndpoint overrides discovery (defaults to BaseURL+"/token").
	TokenEndpoint string
	// AuditEndpoint overrides discovery (defaults to BaseURL+"/audit").
	AuditEndpoint string
	// Agent is the identity this client acts as. Required.
	Agent *Agent
	// HTTPClient makes requests; nil uses http.DefaultClient.
	HTTPClient *http.Client
	// PreferWaitSeconds sets `Prefer: wait=N` on requests that may defer.
	PreferWaitSeconds int
	// OnRequirement is invoked when a deferred (202) response carries an
	// AAuth-Requirement — e.g. requirement=interaction with the URL and code
	// the user must visit. The agent surfaces it; polling continues.
	OnRequirement func(Requirement)
	// OnClarification answers a requirement=clarification 202 (§7.3) that
	// arrives during a permission or token request. Nil leaves clarifications
	// unanswered (the request eventually times out server-side).
	OnClarification func(Clarification) (ClarificationReply, error)
}

PSClient calls a Person Server on behalf of a (self-hosted) agent.

func NewPSClient

func NewPSClient(baseURL string, agent *Agent) *PSClient

NewPSClient returns a client with sane defaults.

func (*PSClient) Audit

func (c *PSClient) Audit(ctx context.Context, a AuditRequest) error

Audit posts an action record to the PS audit endpoint (§7.5). Returns nil on 201 Created; a *MissionStatusError when the mission is no longer active (the caller MUST stop acting on it).

The endpoint is PersonServerMetadata.AuditEndpoint when discovered, else BaseURL+"/audit".

func (*PSClient) ExchangeToken

func (c *PSClient) ExchangeToken(ctx context.Context, treq TokenRequest) (*TokenResponse, error)

ExchangeToken presents a resource token at the PS token endpoint (§7.1.3) and returns the granted auth token, following deferred (202) responses — including operator/user interaction waits — until resolution.

The endpoint is PersonServerMetadata.TokenEndpoint when discovered, else BaseURL+"/token".

func (*PSClient) RequestPermission

func (c *PSClient) RequestPermission(ctx context.Context, p PermissionRequest) (*PermissionResponse, error)

RequestPermission performs the full ceremony (draft -09 §7.4 + §12.4): mint token → attach Signature-Key → sign → POST → follow any deferred (202) responses until a terminal PermissionResponse arrives.

Sub-agents MUST NOT call this directly (§10.2); the parent requests on their behalf — enforced here by refusing parent_agent-marked identities.

type PendingStatus

type PendingStatus struct {
	// Status is "pending", or "interacting" once the user has arrived at an
	// interaction endpoint. Unrecognized values MUST be treated as pending.
	Status string `json:"status"`
	// Clarification, when present with requirement=clarification (§7.3.1),
	// is a Markdown question the recipient must answer before proceeding.
	Clarification string `json:"clarification,omitempty"`
	// Timeout is the optional deadline (seconds) to answer a clarification.
	Timeout int `json:"timeout,omitempty"`
	// Options are discrete answer choices, when the question has them.
	Options []string `json:"options,omitempty"`
}

PendingStatus is the body of a 202 pending response.

type PermissionRequest

type PermissionRequest struct {
	// Action identifies what the agent wants to do (e.g. a tool name).
	Action string `json:"action"`
	// Description is an optional Markdown string: what and why.
	Description string `json:"description,omitempty"`
	// Parameters carries the arguments the agent intends to pass.
	Parameters map[string]any `json:"parameters,omitempty"`
	// Mission binds the request to an active mission.
	Mission *MissionRef `json:"mission,omitempty"`
}

PermissionRequest is the body of POST {permission_endpoint} (draft -09 §7.4.1) — governance for actions not fronted by an AAuth resource: tool calls, file writes, messages.

type PermissionResponse

type PermissionResponse struct {
	Permission string `json:"permission"` // "granted" or "denied"
	// Reason optionally explains a denial (Markdown).
	Reason string `json:"reason,omitempty"`
}

PermissionResponse is the 200 body of the permission endpoint (draft -09 §7.4.2). Denial is a 200 with permission="denied" — not an HTTP error.

func (*PermissionResponse) Granted

func (r *PermissionResponse) Granted() bool

Granted reports whether the agent may proceed.

type PersonServerMetadata

type PersonServerMetadata struct {
	Issuer              string `json:"issuer,omitempty"`               // the PS issuer URL
	JWKSURI             string `json:"jwks_uri,omitempty"`             // URL of the PS signing JWKS
	TokenEndpoint       string `json:"token_endpoint,omitempty"`       // where agents exchange resource tokens
	PermissionEndpoint  string `json:"permission_endpoint,omitempty"`  // where agents request permission
	AuditEndpoint       string `json:"audit_endpoint,omitempty"`       // where agents log actions
	MissionEndpoint     string `json:"mission_endpoint,omitempty"`     // where agents propose missions
	InteractionEndpoint string `json:"interaction_endpoint,omitempty"` // where the user completes interactions
}

PersonServerMetadata is /.well-known/aauth-person.json.

type Requirement

type Requirement struct {
	// Requirement is the requirement value: agent-token, auth-token,
	// interaction, clarification, or claims.
	Requirement string
	// ResourceToken accompanies requirement=auth-token.
	ResourceToken string
	// URL accompanies requirement=interaction (where the user goes).
	URL string
	// Code accompanies requirement=interaction (shown to the user).
	Code string
}

Requirement is a parsed AAuth-Requirement header value, e.g.:

requirement=auth-token; resource-token="eyJ..."
requirement=interaction; url="https://ps.example/interaction"; code="A1B2-C3D4"
requirement=agent-token

func ParseRequirement

func ParseRequirement(v string) (Requirement, error)

ParseRequirement parses an AAuth-Requirement header value. Parameters may be quoted or bare; unknown parameters are ignored (forward compatibility).

func (Requirement) String

func (r Requirement) String() string

String renders the header value.

type ResourceClaims

type ResourceClaims struct {
	DWK         string               `json:"dwk"`                   // well-known doc name for key discovery
	Agent       string               `json:"agent"`                 // the requesting agent's identifier
	AgentJKT    string               `json:"agent_jkt"`             // thumbprint of the agent's signing key
	Scope       string               `json:"scope,omitempty"`       // requested scopes, space-separated
	Mission     *MissionRef          `json:"mission,omitempty"`     // mission context, when present
	Interaction *ResourceInteraction `json:"interaction,omitempty"` // resource's own interaction requirement
	// RegisteredClaims carries iss (resource URL), aud (PS or AS), jti, iat, exp.
	jwt.RegisteredClaims
}

ResourceClaims is the payload of an aa-resource+jwt (draft -09 §6.7.1) — issued by a resource in the three/four-party flows. aud selects the PS or AS; agent + agent_jkt bind it to the requesting agent's identity and key. Lifetime SHOULD NOT exceed 5 minutes.

func VerifyResourceChallenge

func VerifyResourceChallenge(token, resourceURL string, agent *Agent) (*ResourceClaims, error)

VerifyResourceChallenge is the agent-side check (§6.7.3) before sending a resource token to the PS: the token really came from the resource we called, names us, and binds our current key.

func VerifyResourceToken

func VerifyResourceToken(ctx context.Context, token, audience string, agent *AgentClaims, resolver KeyResolver) (*ResourceClaims, error)

VerifyResourceToken verifies an aa-resource+jwt per §6.7.2 from the recipient's (PS or AS) perspective. audience is the recipient's own URL; agent binds the token to the requesting agent's verified claims.

type ResourceInteraction

type ResourceInteraction struct {
	URL  string `json:"url"`  // the resource's interaction endpoint
	Code string `json:"code"` // interaction code to present there
}

ResourceInteraction is the optional interaction claim in a resource token (§6.7.1): the resource requires its own user-facing flow before the PS can issue an auth token.

type ResourceMetadata

type ResourceMetadata struct {
	Issuer                        string   `json:"issuer,omitempty"`                          // the resource issuer URL
	JWKSURI                       string   `json:"jwks_uri,omitempty"`                        // URL of the resource signing JWKS
	AuthorizationEndpoint         string   `json:"authorization_endpoint,omitempty"`          // where agents proactively request access
	AccessMode                    string   `json:"access_mode,omitempty"`                     // declared access mode
	AdditionalSignatureComponents []string `json:"additional_signature_components,omitempty"` // extra components the resource requires signed
}

ResourceMetadata is /.well-known/aauth-resource.json. AccessMode declares how the resource authorizes agents (identity, resource, ps, federated).

type SelfSignedResolver

type SelfSignedResolver struct{}

SelfSignedResolver trusts the embedded cnf.jwk to verify the token's own signature (proof of possession is then established by the HTTP message signature, which must use the same key). Suitable for local/self-hosted agents where the verifier's policy layer decides what the identity may do.

func (SelfSignedResolver) ResolveKey

func (SelfSignedResolver) ResolveKey(_ context.Context, _, _, _ string, cnf *JWK) (ed25519.PublicKey, error)

ResolveKey implements KeyResolver, returning the key from cnf.jwk.

type SignatureError

type SignatureError struct {
	// Code is the required error member.
	Code string
	// Params carries code-specific members (e.g. supported_algorithms).
	// Unknown members are preserved; recipients ignore what they don't know.
	Params map[string]string
}

SignatureError is a parsed Signature-Error header.

func ParseSignatureError

func ParseSignatureError(v string) (*SignatureError, error)

ParseSignatureError parses a Signature-Error header value. Returns nil if the value is empty (no signature error present).

func SignatureErrorFromResponse

func SignatureErrorFromResponse(res *http.Response) (*SignatureError, error)

SignatureErrorFromResponse extracts the Signature-Error from a response, or nil when absent.

func (*SignatureError) Error

func (e *SignatureError) Error() string

Error implements error.

func (SignatureError) String

func (e SignatureError) String() string

String renders the header value: `error=<code>[, k=v ...]`.

type StaticResolver

type StaticResolver map[string]JWKS

StaticResolver resolves issuers from a fixed map of iss → JWKS.

func (StaticResolver) ResolveKey

func (r StaticResolver) ResolveKey(_ context.Context, iss, _, kid string, _ *JWK) (ed25519.PublicKey, error)

ResolveKey implements KeyResolver, returning the pinned key for iss by kid.

type TokenRequest

type TokenRequest struct {
	ResourceToken string   `json:"resource_token"`           // the resource token to exchange (required)
	UpstreamToken string   `json:"upstream_token,omitempty"` // upstream auth token, for call chaining (§10.1.1)
	SubagentToken string   `json:"subagent_token,omitempty"` // a sub-agent's token, when a parent requests for it
	Justification string   `json:"justification,omitempty"`  // Markdown reason shown to the user at consent
	LoginHint     string   `json:"login_hint,omitempty"`     // hint about who to authorize
	Tenant        string   `json:"tenant,omitempty"`         // tenant identifier
	DomainHint    string   `json:"domain_hint,omitempty"`    // domain hint
	Prompt        string   `json:"prompt,omitempty"`         // consent prompt behavior (none/login/consent/select_account)
	Platform      string   `json:"platform,omitempty"`       // runtime platform identifier (agent-attested)
	Device        string   `json:"device,omitempty"`         // human-readable device label for the user's dashboard
	Capabilities  []string `json:"capabilities,omitempty"`   // capability values the agent can handle for this request
}

TokenRequest is the body of POST {token_endpoint} (draft -09 §7.1.3).

type TokenResponse

type TokenResponse struct {
	AuthToken string `json:"auth_token"` // the issued aa-auth+jwt
	ExpiresIn int64  `json:"expires_in"` // token lifetime in seconds
}

TokenResponse is the direct-grant 200 body (draft -09 §7.1.4).

type Transport

type Transport struct {
	// Agent is the identity every request is signed as. Required.
	Agent *Agent
	// PS performs token exchanges. Optional: without it, auth-token
	// challenges fail with the challenge error (identity-only deployments).
	PS *PSClient
	// Base is the underlying RoundTripper (default http.DefaultTransport).
	Base http.RoundTripper
	// OnRequirement surfaces interaction requirements (URL + code) that
	// arrive while a request is deferred.
	OnRequirement func(Requirement)
	// MaxBodyBytes bounds request-body buffering (default 4 MiB).
	MaxBodyBytes int64
	// ExpiryLeeway is subtracted from auth-token lifetimes when caching
	// (default 60s), so tokens are refreshed before servers reject them.
	ExpiryLeeway time.Duration
	// contains filtered or unexported fields
}

Transport is a protocol-aware http.RoundTripper for agents. Wrap any HTTP client with it and AAuth disappears from application code:

hc := &http.Client{Transport: aauth.NewTransport(agent, ps)}
res, err := hc.Get("https://files.example/files")   // just works

Per request it:

  • signs with the agent's key, presenting the freshest credential for the target origin (auth token if one is cached, else the agent token) and any cached AAuth-Access opaque token (§6.4) bound into the signature;
  • on 401 requirement=agent-token (§6.3), retries with the agent token;
  • on 401 requirement=auth-token (§6.6), verifies the resource-token challenge (§6.7.3), exchanges it at the PS token endpoint — following deferred (202) waits — caches the auth token, and retries. Step-up re-challenges (§6.6) trigger a fresh exchange;
  • follows resource-managed 202 interaction waits (§6.5), surfacing requirement=interaction via OnRequirement;
  • honors AAuth-Access rolling refresh: a new header value on any response replaces the cached token (§6.4).

Request bodies are buffered in memory so retries can re-sign and resend; bound with MaxBodyBytes.

Example

ExampleTransport wraps an http.Client so AAuth is transparent: signing, 401 challenges, token exchange, and caching all happen automatically.

package main

import (
	"net/http"

	aauth "github.com/aauth-dev/auth-go"
)

func main() {
	id, _ := aauth.ParseAgentIdentifier("aauth:assistant@agent.example")
	agent, _ := aauth.NewAgent(id)
	ps := aauth.NewPSClient("https://ps.example", agent)

	hc := &http.Client{Transport: aauth.NewTransport(agent, ps)}
	// This call signs itself, and if the resource answers 401 with an
	// auth-token challenge, the transport exchanges a token and retries.
	_, _ = hc.Get("https://files.example/files")
}

func NewTransport

func NewTransport(agent *Agent, ps *PSClient) *Transport

NewTransport builds a Transport for agent, exchanging tokens at ps (ps may be nil for identity-only use).

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper.

type VerifyAgentTokenOptions

type VerifyAgentTokenOptions struct {
	// Resolver locates the token-signature key. Required.
	Resolver KeyResolver
	// RequireProviderClaims enforces draft -09 §5.2.4 strictly: iss must be
	// an HTTPS URL, dwk must equal WellKnownAgent, jti must be present.
	// Self-hosted local deployments MAY relax this (signature-key §3.6 makes
	// iss/dwk SHOULD; the aauth -09 agent-token profile makes them MUST —
	// set true for cross-domain interop).
	RequireProviderClaims bool
}

VerifyAgentTokenOptions tunes VerifyAgentToken.

Directories

Path Synopsis
Package interactioncode generates and canonicalizes AAuth interaction codes (draft-hardt-oauth-aauth-protocol-09; format per the interaction-code requirements: unambiguous alphabet, minimum entropy).
Package interactioncode generates and canonicalizes AAuth interaction codes (draft-hardt-oauth-aauth-protocol-09; format per the interaction-code requirements: unambiguous alphabet, minimum entropy).

Jump to

Keyboard shortcuts

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