agentauth

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

README

github.com/ubgo/auth/plugin/agentauth

Lets AI agents authenticate to the app with their own keys and act for a user under capabilities the user granted. It ports better-auth's agent-auth plugin, which implements the Agent Auth Protocol. It is opt-in, and it is its own module.

Install

go get github.com/ubgo/auth/plugin/agentauth

It depends on the core module and sqlb only; WebAuthn approval takes plugin/passkey through an interface, so the WebAuthn library reaches only apps that use it. Agent keys are parsed with the core module's plugin/jwt (KeysFromJWKS), so a key the jwt plugin would refuse (a point off the curve, a short key) is refused here too.

The model

  • A host is the runtime an agent lives in: an MCP client, a CLI, a desktop app. It holds a key pair and signs host+jwt tokens. A signed-in user creates it with CreateHost (its key, and a budget of capabilities its agents get without asking), or, under AllowDynamicHostRegistration, an unknown host registers itself with the key in its token.
  • An agent has its own Ed25519 (or P-256) key pair. The host registers it with Register, a host JWT whose agent_public_key claim is the agent's key. The agent then signs a short agent+jwt for every request, with sub set to its id.
  • A capability is one thing an agent may do, from the catalogue given to Capabilities. A grant gives one agent one capability, optionally scoped by constraints ({"amount": {"max": 1000}}).
  • An agent is delegated (acts for the user who approved it) or autonomous (acts on its own under its host's budget until a user claims the host).

Wire it

ag := agentauth.New(
	agentauth.Capabilities(
		agentauth.Capability{Name: "read_data", Description: "Read user data"},
		agentauth.Capability{Name: "transfer_money", Description: "Transfer funds", RequiredConstraints: []string{"amount"}},
	),
	agentauth.OnExecute(func(ctx context.Context, ex agentauth.Execution) (any, error) {
		return runCapability(ctx, ex.Capability.Name, ex.Arguments, ex.Session.User.ID)
	}),
)
a, err := auth.New(auth.WithDB(db, d), auth.WithBaseURL("https://app.example.com"), auth.WithPlugins(signIn, ag))

mux.Handle("/auth/", authhttp.Handler(a, authhttp.Prefix("/auth")))
mux.Handle("/", ag.Handler())

Handler serves the discovery document at /.well-known/agent-configuration and the protocol endpoints under the issuer (Issuer, by default the base URL): /agent/register, /agent/session, /agent/status, /agent/revoke, /agent/request-capability, /agent/introspect, /agent/revoke-capability, /agent/claim, /capability/list, /capability/describe, /capability/execute, /host/rotate-key and /host/revoke. Each is a self-served operation, so it runs the whole pipeline: rate limits, hooks, tracing. Errors are the protocol's {"error": code, "message": text}, and a 401 carries WWW-Authenticate: AgentAuth discovery="<origin>/.well-known/agent-configuration". The user's operations, approve_capability and create_host (both need a fresh sign-in), pending_approvals and update_agent, are ordinary operations every transport mounts under /agentauth.

The approval flow

  1. The host registers an agent. A capability inside the host's budget is granted at once; anything else, every capability of an agent on a host no user owns yet, and everything under ForceApproval, waits. The answer then carries an approval: a user_code, a verification_uri (DeviceAuthorizationPage, default /device/capabilities, which the app serves) and expires_in (5 minutes).
  2. The agent shows the user the code and the URL, and polls /agent/status with its host JWT.
  3. The signed-in user enters the code on the page, which calls approve_capability with agent_id, user_code and action (approve or deny). The user code is compared as a SHA-256 digest in constant time. Approval activates the agent for that user, and links an unowned host to them.
  4. The agent calls /capability/execute. The plugin checks the agent, finds an active, unexpired grant whose constraints the arguments satisfy (or grants it from the host's budget, unless the user revoked or denied that capability before), and calls OnExecute. Execution.ConsumeGrant spends a single-use grant.

An app route serving a capability with a Location verifies the agent with VerifyAgentRequest(r), checks AgentSession.HasGrant, and enforces the grant's constraints with Constraints.Validate.

CIBA

MethodCIBA (client-initiated backchannel authentication, poll mode) is the other approval method, on by default beside device authorization (ApprovalMethods). An agent asks for it with preferred_method: "ciba" on register or request-capability (ApprovalHints, which also carries login_hint and binding_message); ResolveApprovalMethod(fn) replaces that choice. CIBA needs a user to address, so an agent nobody owns yet always gets device authorization. A host can also address a user directly: POST /agent/ciba/authorize with a host JWT, login_hint (any verified sign-in identifier) and the host's own agent_id; an unknown hint gets the same answer and costs the same write, so it reveals no account.

The request reaches the user only through the app: the approval.created event carries Method, UserID, ApprovalID and BindingMessage, for a push or an email, and the pending_approvals operation (PendingApprovalsFor) is the user's inbox. The user answers with approve_capability and approval_id, no code; only the user the request names may. The agent polls /agent/status.

WebAuthn approval

ProofOfPresence(v) makes approvals that need it also need a passkey assertion with user verification, better-auth's proofOfPresence: a capability with ApprovalStrength: StrengthWebAuthn, the first agent of a pending host, and new capabilities for an active agent. v is plugin/passkey's plugin (PresenceEnrolled, BeginPresence, VerifyPresence). The first approve_capability answers status: "webauthn_required" with a challenge (webauthn.ceremony, webauthn.webauthn_options) and approves nothing; the page runs navigator.credentials.get and calls again with webauthn_response: {ceremony, credential}. Each ceremony is single-use and bound to the user and the agent, so an assertion made for one agent never approves another. A user with no passkey gets webauthn_not_enrolled; a refused assertion webauthn_verification_failed. Denying never needs it.

Managing hosts, agents and grants

  • RevokeHost (/host/revoke) revokes a host and every agent under it with their grants. The host calls it with its own JWT, or the signed-in owner with host_id. The revoked host keeps its key, so that key can neither register again dynamically nor be taken with CreateHost; better-auth clears it, which lets a revoked key come back as a new host. A host the user does not own answers host_not_found, including a dynamic host nobody owns (better-auth lets any user revoke one).
  • RevokeCapability (/agent/revoke-capability, {"agent_id", "capabilities"}) revokes every active or pending grant of the named capabilities without revoking the agent. The agent, its host or its owner may call it. Executing the capability then answers grant_revoked, and the host's budget does not grant it again. Denied and consumed grants are left as they are.
  • Claim (/agent/claim, a host JWT and {"agent_id"}) asks a user to take over an autonomous agent on a host nobody owns, for example one that built a site the user wants. Under AllowDynamicHostRegistration a host on its first contact registers itself here, as at /agent/register. The answer carries a device-authorization approval; the user who enters its code in approve_capability owns the agent, which becomes claimed and authenticates nothing more, and its host. The request is stored as a claim (auth_agent_approval.kind), so only its own code claims: better-auth claims any active autonomous agent whose id a user approves, request or not. A claim never uses CIBA, because resolving a login_hint would tell the host whether it names an account.
  • Reactivate (/agent/reactivate, a host JWT and {"agent_id"}) brings back an agent of that host that expired (its AgentSessionTTL or AgentMaxLifetime), better-auth's reactivate: every grant it held is revoked and the host's budget granted again, so a capability a user approved on top of the budget is lost; the agent is active with a fresh lifetime, and its answer is the shape of /agent/status. A live agent is answered as it is; a revoked, rejected, claimed or pending agent, or one past AbsoluteLifetime, is refused. better-auth sends the budget for approval when nobody owns the host; here no agent can be active in that state (approving a delegated agent links its host to the user), so the budget is granted at once, as at registration.
  • RotateAgentKey (/agent/rotate-key, a host JWT and {"agent_id", "public_key"}) replaces a live agent's key; the old key stops at once. Only the agent's own host may, and not to a key another agent of the host holds (agent_exists). An agent that fetched its keys from a JWKS URL has a stored key from then on.
  • UpdateHost (update_host, fresh sign-in) changes a host the user owns: name, public_key or jwks_url (one of the two; the old key stops at once), and default_capabilities. better-auth also lets any signed-in user edit a host nobody owns, and a member of the owner's organization edit any; here only the owner can, and any other host answers host_not_found.
  • Enrollment: create_host with neither public_key nor jwks_url creates a host in pending_enrollment and returns a one-time enrollment_token (32 random bytes, valid EnrollmentTokenTTL, one hour). The device posts {"token", "public_key", "name"} to /host/enroll (no other credential, 10 per minute per IP) and the host becomes active with the device's key, so the private key never leaves the device. Only the token's SHA-256 is stored, matched in SQL and cleared on use. A key another host already holds is refused (host_already_linked), where better-auth merges the two hosts and would hand the enrolling user the other host's agents. Before enrolling, the host authenticates nothing and update_host cannot set its key.
  • JWKS URLs (AllowJWKSURLs(), off by default): a host created or updated with jwks_url, or an agent registered with the agent_jwks_url claim instead of agent_public_key, signs with the key whose kid its JWT header names, fetched from that https URL and cached for 5 minutes; a kid the cached set lacks is fetched again at most every 30 seconds, so made-up kids cannot turn the server into a fetch loop. Risk, why it is off: the server then fetches URLs a user or a host chose, and whoever controls the document at a URL signs for that host or agent; Init logs a warning at every start. The default client fetches https only, refuses private, loopback, link-local and CGNAT addresses after DNS resolution (so a name that resolves to 169.254.169.254 is refused at connect), uses no proxy, follows no redirect and reads at most 1 MiB in 5 seconds; JWKSClient(c) replaces it, for a key server on a private network.
  • UpdateAgent (update_agent, signed in) renames an agent or replaces its metadata, a JSON object of strings, numbers, booleans and nulls up to 4 KiB, stored with sorted keys; {} clears it. The metadata reaches the app as AgentSession.Agent.Metadata. Only the owner of the agent or its host sees the agent.

OpenAPI

FromOpenAPISpec(spec, OpenAPIOptions) turns an OpenAPI 3.x JSON document into plugin options, better-auth's createFromOpenAPI: provider name and description from info, one capability per operationId (input schema from the parameters and JSON body, output schema from the 200 or 201 JSON answer), an OnExecute that proxies to BaseURL, and optionally the default host budget (HostCapabilities: AllOperations(), OperationsWithMethod("GET", "HEAD") or any OperationFilter), each capability's strength (StrengthForAll, StrengthByMethod, any StrengthFunc) and a Location. FromOpenAPI(spec) returns the capabilities only, OpenAPIHandler(spec, OpenAPIHandlerOptions) the proxy only.

The proxy escapes path parameters (a . or .. value is refused), sets query and header parameters, sends the other arguments as the JSON body (not for GET or HEAD), and returns a JSON answer verbatim and anything else as text. Headers (better-auth's resolveHeaders) adds the upstream credentials per call from the agent session; they win over header parameters, so an agent cannot replace them. A non-2xx answer is ErrUpstream, an internal error to the agent. Paths are taken in sorted order, not the document's, because Go's JSON maps have none.

The agent side

agentclient is the Go counterpart of better-auth's @auth/agent SDK: New(hostKey), Discover, Connect (a fresh agent key per agent), WaitForApproval, Execute (request-bound tokens: htm, htu, ath), RequestCapability, ListCapabilities, DescribeCapability, Disconnect, CIBAAuthorize and SignAgentJWT. It keeps no state: Connect returns the Agent for the caller to store. Client.Tools(store) returns the SDK's MCP tools (discover_provider, list_capabilities, describe_capability, connect_agent, agent_status, request_capability, execute_capability, disconnect_agent) as name, description, JSON Schema and handler, for any Go MCP server library; store (MemoryStore, or the app's own) holds the connected agents.

Security rules

Each is pinned by a test that was seen to fail with the check removed.

  • A JWT's typ must be agent+jwt or host+jwt as the endpoint expects, so one kind is never accepted as the other.
  • The signature is checked with the stored key, and the algorithm is the key's (EdDSA for Ed25519, ES256 for P-256), never the header's: alg: none and a mismatched alg are refused.
  • aud must name the issuer's origin, the issuer, the execute endpoint, or the capability's Location for a token narrowed to one capability. Unlike better-auth, the request's Host header never widens that set.
  • exp must be in the future and iat within JWTMaxAge (60 seconds).
  • Every token carries a jti, spent in the auth_agent_jti table, so it authenticates once on every server sharing the database. A token whose request failed is rolled back with the request's transaction and may be retried within its lifetime.
  • htm, htu and ath bind a token to a request's method, URL and body hash when the token carries them.
  • A revoked, pending, rejected or claimed agent authenticates nothing, nor one past its sliding AgentSessionTTL (1 hour), its AgentMaxLifetime from activation (24 hours) or its AbsoluteLifetime.
  • Revocation clears the agent's key and revokes every grant. Only the agent itself, its host, or the user who owns it or its host may revoke it or one of its capabilities. Revoking a host revokes every agent under it and refuses its key from then on.
  • A claim is approved only with the code of its own claim request; nothing else transfers an agent.
  • approve_capability needs the user code whenever a request is pending; without one only the agent's or host's owner may answer. An agent that already belongs to a user is answered by that user only.
  • A CIBA request is answered only by the user it names; a host may address one only for its own agents; an unknown login_hint gets the same answer as a known one.
  • A WebAuthn approval spends a ceremony bound to the user and the agent, and accepts only an assertion with user verification from one of the user's own passkeys.
  • OpenAPI proxy credentials from Headers override header arguments, and a . or .. path argument is refused.
  • A host or agent with a JWKS URL is verified only under AllowJWKSURLs, only with the key its header's kid names, and only from a public https address; turning the option off makes those hosts and agents authenticate nothing.
  • An enrollment token works once, before it expires, for a host still waiting for one; reactivation and agent key rotation need the agent's own host's JWT, checked before the agent's state so another host learns nothing.

Options

Option Default Effect
Issuer(url) the base URL Base of the endpoint URLs and the accepted audience
ProviderName, ProviderDescription agent-auth, Agent Auth enabled service Discovery document fields
Modes(...) delegated, autonomous Registration modes served
Capabilities(...) none The catalogue; with one, names outside it are refused
OnExecute(fn) none Runs capabilities; without it execute answers execute_not_configured
AllowedKeyAlgorithms(...) Ed25519 Ed25519 and P-256 are supported
JWTMaxAge(d) 60s Oldest a JWT may be
AgentSessionTTL(d) 1h Sliding idle lifetime; 0 disables
AgentMaxLifetime(d) 24h Lifetime from activation; 0 disables
AbsoluteLifetime(d) 0 Lifetime from creation; 0 disables
MaxAgentsPerUser(n) 25 Active agents per user; 0 disables
BlockedCapabilities(...) none Never granted; x.* blocks a family
AllowDynamicHostRegistration() off Unknown hosts may register with an inline key
DefaultHostCapabilities(...) none Budget of dynamic and newly claimed hosts, and of hosts created without one
DefaultHostCapabilitiesFunc(fn) none The budget per host, from its mode, owner and name
ApprovalMethods(...) ciba, device_authorization Approval methods discovery lists and requests may use
ResolveApprovalMethod(fn) the agent's supported preference, else device authorization Chooses the method per request
ProofOfPresence(v) off WebAuthn approval through plugin/passkey
TrustProxy() off htu is compared with the URL from X-Forwarded-Proto and X-Forwarded-Host; audiences never come from the request
DeviceAuthorizationPage(url) /device/capabilities The approval page the answer points to
RequireAuthForCapabilities() off Listing and describing need an agent or host JWT; a 401 carries the WWW-Authenticate: AgentAuth discovery="…" challenge
AutonomousUser(fn) none The user an autonomous agent on an unowned host acts as
AllowJWKSURLs() off Hosts and agents may sign with keys fetched from an https JWKS URL; warned about at every start
JWKSClient(c) the guarded client The HTTP client JWKS URLs are fetched with; the replacement is trusted as it is
EnrollmentTokenTTL(d) 1h How long a host's enrollment token works; must be positive

Events

OnEvent(fn) receives better-auth's audit events: host.created, agent.created, agent.revoked, host.key_rotated, capability.requested, approval.created (with its id, method, user and binding message, for CIBA notifications), capability.approved and approval.approved, capability.denied and approval.denied, agent.claimed (with approval.approved), host.revoked (the actor is the host or its owner), capability.revoked (only when a grant was revoked), agent.updated, agent.reactivated (with the capabilities granted back; not for a live agent answered as it is), agent.key_rotated, host.updated, host.enrolled, host.reactivated (a create_host that took over or reactivated a host with the same key, instead of host.created), and capability.executed with its status (success or error), arguments and duration. They are delivered after the commit, in the background (auth.WithBackground), so a rolled-back call reports nothing. A slow or panicking fn never reaches the request, as with better-auth's emit, which never throws.

Tables

auth_agent_host, auth_agent, auth_agent_grant, auth_agent_approval (device and CIBA requests: method, user code hash, login hint, binding message, and kind, claim for a claim, added by migration 0002_agent_approval_kind) and auth_agent_jti, with no foreign keys. Migration 0003_agent_lifecycle adds jwks_url to hosts and agents, and enrollment_token_hash (indexed) and enrollment_token_expires_at to hosts. UserTables names user_id of hosts, agents and approvals and granted_by of grants, so deleting a user removes them; a User BeforeDelete data hook also removes every agent of the user's hosts (a pending agent names only its host) with its grants and approval requests. Sweep deletes spent JWT ids and approval requests that have expired.

Errors

Every error's code is agentauth.<protocol code>; Handler answers the part after the dot. The full list is in docs/concepts/errors.md.

Not ported

  • CIBA ping and push delivery to a client notification endpoint: better-auth has the columns (clientNotificationEndpoint, clientNotificationToken, deliveryMode) and a deliverApprovalNotifications helper, but every route that creates an approval request (register, request-capability, claim, device code, /agent/ciba/authorize) writes null endpoints and poll, so no request can ever be delivered by ping or push there. Only poll mode exists in either, and the columns are not copied.
  • Async and streaming proxy answers: a 202 or an event stream from the OpenAPI upstream is returned as its body, not as a polling handle or a stream.
  • The agentclient covers the earlier endpoints only: no provider directory (search, list_providers), batch execute, reactivate, enroll, claim, revoke-capability or agent key rotation, and no CLI binary: a Go program embeds the package instead. Those endpoints are plain JSON POSTs with a host JWT (agentclient.SignAgentJWT's sibling for hosts is the app's own signer).
  • Transparent reactivation inside other calls: an expired agent is refused (agent_expired) until its host calls /agent/reactivate. Also not ported: claiming every autonomous agent of a host when a user takes the host with CreateHost (only Claim claims), batch execute, async and streaming results, capability search (query, resolveQuery, resolveCapabilities), the grant, list/get agent, list/get/update host and switch account endpoints, revoking a host through a shared organization, onHostClaimed and onAutonomousAgentClaimed, resolveGrantTTL and validateCapabilities. OnEvent reports the events of everything ported; the events of these (agent.cleanup, host.claimed, capability.granted) are missing with them.
  • Host key rotation keeps the host id, because agents and grants name it; better-auth renames the host to the new key's thumbprint.
  • A constraint violation does not list the violated fields: the library's errors carry a code, a status and a message only.

Documentation

Overview

Package agentauth lets AI agents authenticate to the app with their own keys and act for a user under capabilities the user granted. It is a Go port of better-auth's agent-auth plugin, which implements the Agent Auth Protocol (agentauthprotocol.com).

Two kinds of caller hold keys. A host is the runtime an agent lives in (an MCP client, a CLI, a desktop app); it signs "host+jwt" tokens and registers agents. An agent has its own Ed25519 (or P-256) key pair and signs a short "agent+jwt" for every request. A user approves an agent and its capabilities with a user code (the device_authorization method), and the agent then calls the execute endpoint, which checks its grants and the grants' constraints before the app's ExecuteFunc runs.

The protocol endpoints are served by Plugin.Handler in the protocol's JSON shape; each one is a self-served auth operation underneath (auth.OpInfo .SelfServed), so it runs the same pipeline as every other operation: rate limits, hooks, tracing. The user's operations (approving an agent, creating a host) are ordinary operations every transport mounts under /agentauth.

Security rules, each pinned by a test: a JWT's typ, signature, audience, expiry and age are checked, and the algorithm comes from the stored key, never from the token; every JWT carries a jti that authenticates once, recorded in the database so the rule holds across servers; a revoked, pending, rejected or expired agent authenticates nothing; a capability executes only under an active, unexpired grant whose constraints allow the arguments; a user code is compared as a SHA-256 digest in constant time.

Index

Constants

View Source
const (
	EndpointConfiguration      auth.EndpointID = "agentauth.configuration"
	EndpointRegister           auth.EndpointID = "agentauth.register"
	EndpointSession            auth.EndpointID = "agentauth.session"
	EndpointRevoke             auth.EndpointID = "agentauth.revoke"
	EndpointStatus             auth.EndpointID = "agentauth.status"
	EndpointIntrospect         auth.EndpointID = "agentauth.introspect"
	EndpointListCapabilities   auth.EndpointID = "agentauth.list_capabilities"
	EndpointDescribeCapability auth.EndpointID = "agentauth.describe_capability"
	EndpointRequestCapability  auth.EndpointID = "agentauth.request_capability"
	EndpointExecute            auth.EndpointID = "agentauth.execute"
	EndpointRotateHostKey      auth.EndpointID = "agentauth.rotate_host_key"
	EndpointApproveCapability  auth.EndpointID = "agentauth.approve_capability"
	EndpointCreateHost         auth.EndpointID = "agentauth.create_host"
	EndpointCIBAAuthorize      auth.EndpointID = "agentauth.ciba_authorize"
	EndpointPendingApprovals   auth.EndpointID = "agentauth.pending_approvals"
	EndpointRevokeHost         auth.EndpointID = "agentauth.revoke_host"
	EndpointRevokeCapability   auth.EndpointID = "agentauth.revoke_capability"
	EndpointClaim              auth.EndpointID = "agentauth.claim"
	EndpointUpdateAgent        auth.EndpointID = "agentauth.update_agent"
	EndpointReactivate         auth.EndpointID = "agentauth.reactivate"
	EndpointRotateAgentKey     auth.EndpointID = "agentauth.rotate_agent_key"
	EndpointUpdateHost         auth.EndpointID = "agentauth.update_host"
	EndpointEnrollHost         auth.EndpointID = "agentauth.enroll_host"
)

Operation ids. The self-served ones are served by Handler at the protocol paths; the others by every transport, under /agentauth.

View Source
const (
	PathConfiguration      = "/agent-configuration"
	PathRegister           = "/agent/register"
	PathSession            = "/agent/session"
	PathRevoke             = "/agent/revoke"
	PathStatus             = "/agent/status"
	PathIntrospect         = "/agent/introspect"
	PathListCapabilities   = "/capability/list"
	PathDescribeCapability = "/capability/describe"
	PathRequestCapability  = "/agent/request-capability"
	PathExecute            = "/capability/execute"
	PathRotateHostKey      = "/host/rotate-key"
	// PathCIBAAuthorize is the CIBA backchannel authentication endpoint
	// (protocol section 9.2), called with a host JWT.
	PathCIBAAuthorize = "/agent/ciba/authorize"
	// PathRevokeHost revokes a host and every agent under it (protocol
	// section 6.10), with a host JWT or a signed-in owner.
	PathRevokeHost = "/host/revoke"
	// PathRevokeCapability revokes capabilities of one agent without
	// revoking the agent.
	PathRevokeCapability = "/agent/revoke-capability"
	// PathClaim starts the claim of an autonomous agent (protocol section
	// 3.4), with a host JWT.
	PathClaim = "/agent/claim"
	// PathReactivate reactivates an expired agent (protocol section 6.6),
	// with a host JWT.
	PathReactivate = "/agent/reactivate"
	// PathRotateAgentKey replaces an agent's key (protocol section 6.8),
	// with a host JWT.
	PathRotateAgentKey = "/agent/rotate-key"
	// PathEnrollHost binds a key to a host created for enrollment
	// (protocol section 3.2), with the one-time enrollment token.
	PathEnrollHost = "/host/enroll"
	// WellKnownConfiguration is the discovery document's well-known path
	// (protocol section 5.1), served at the root of the issuer's host.
	WellKnownConfiguration = "/.well-known/agent-configuration"
)

Protocol paths, under the issuer's path. They are better-auth's.

View Source
const (
	// MethodDeviceAuthorization: the agent shows the user a code and a
	// URL, and the signed-in user enters the code on the app's approval
	// page.
	MethodDeviceAuthorization = "device_authorization"
	// MethodCIBA: client-initiated backchannel authentication, in poll
	// mode. The request is addressed to a known user, who sees it in the
	// pending_approvals list (the app may notify them from the
	// approval.created event) and answers it by its approval id; the agent
	// polls /agent/status.
	MethodCIBA = "ciba"
)

Approval methods (protocol section 9).

View Source
const (
	ActionApprove = "approve"
	ActionDeny    = "deny"
)

Approval actions.

View Source
const (
	AlgEd25519 = "Ed25519"
	AlgP256    = "P-256"
)

Key algorithms, by JWK curve name as the protocol names them.

View Source
const (
	// DefaultJWTMaxAge is the oldest an agent or host JWT may be, from iat.
	DefaultJWTMaxAge = 60 * time.Second
	// DefaultAgentSessionTTL is the sliding idle lifetime of an agent: each
	// authenticated request extends it.
	DefaultAgentSessionTTL = time.Hour
	// DefaultAgentMaxLifetime caps an agent's life from its activation.
	DefaultAgentMaxLifetime = 24 * time.Hour
	// DefaultMaxAgentsPerUser is how many active agents one user may have.
	DefaultMaxAgentsPerUser = 25
	// DefaultDeviceAuthorizationPage is where the approval answer sends the
	// user; the app serves the page.
	DefaultDeviceAuthorizationPage = "/device/capabilities"
	// DefaultApprovalTTL is how long a user code is valid.
	DefaultApprovalTTL = 5 * time.Minute
	// DefaultApprovalInterval is the polling interval the agent is told.
	DefaultApprovalInterval = 5 * time.Second
	// DefaultPresenceTTL is how long a WebAuthn approval challenge stays
	// valid, better-auth's challenge cache lifetime.
	DefaultPresenceTTL = 5 * time.Minute
)

Defaults. Each has an option; the values are better-auth's.

View Source
const (
	OpEq    = "eq"
	OpMin   = "min"
	OpMax   = "max"
	OpIn    = "in"
	OpNotIn = "not_in"
)

Constraint operators, protocol section 2.13.

View Source
const (
	EventAgentCreated        = "agent.created"
	EventAgentRevoked        = "agent.revoked"
	EventHostCreated         = "host.created"
	EventHostKeyRotated      = "host.key_rotated"
	EventCapabilityRequested = "capability.requested"
	EventCapabilityApproved  = "capability.approved"
	EventCapabilityDenied    = "capability.denied"
	EventApprovalCreated     = "approval.created"
	EventApprovalApproved    = "approval.approved"
	EventApprovalDenied      = "approval.denied"
	EventCapabilityExecuted  = "capability.executed"
	EventHostRevoked         = "host.revoked"
	EventCapabilityRevoked   = "capability.revoked"
	EventAgentClaimed        = "agent.claimed"
	EventAgentUpdated        = "agent.updated"
	EventAgentReactivated    = "agent.reactivated"
	EventAgentKeyRotated     = "agent.key_rotated"
	EventHostUpdated         = "host.updated"
	EventHostEnrolled        = "host.enrolled"
	EventHostReactivated     = "host.reactivated"
	// Execution statuses of a capability.executed event.
	ExecutionSuccess = "success"
	ExecutionError   = "error"
	// Actor types.
	ActorUser  = "user"
	ActorAgent = "agent"
	// ActorHost is a host acting with its own JWT, as when it revokes
	// itself.
	ActorHost = "host"
)

Event types, better-auth's AgentAuthEvent names for what this plugin does.

View Source
const (
	// DefaultJWKSCacheTTL is how long fetched keys are used before the URL
	// is fetched again.
	DefaultJWKSCacheTTL = 5 * time.Minute
	// DefaultJWKSRefetchInterval is the shortest time between two fetches
	// of one URL caused by a kid the cached keys do not have.
	DefaultJWKSRefetchInterval = 30 * time.Second
)

JWKS defaults. Each is better-auth's jwks-cache value except the refetch interval, which better-auth does not have: it refetches on every unknown kid, so a stream of tokens naming made-up kids makes the server fetch the URL once per token.

View Source
const (
	TypeHostJWT  = "host+jwt"
	TypeAgentJWT = "agent+jwt"
)

JWT types, protocol sections 4.2 and 4.5. A host token can never be presented as an agent token, nor the reverse.

View Source
const (
	// TableNameHost holds hosts: the runtimes (an MCP client, a CLI, a
	// desktop app) that hold a key and register agents.
	TableNameHost  = "auth_agent_host"
	TableAliasHost = "aah"
	// TableNameAgent holds agents, each with its own public key.
	TableNameAgent  = "auth_agent"
	TableAliasAgent = "aag"
	// TableNameGrant holds capability grants, one row per capability and
	// constraint scope.
	TableNameGrant  = "auth_agent_grant"
	TableAliasGrant = "aagr"
	// TableNameApproval holds pending device-authorization requests.
	TableNameApproval  = "auth_agent_approval"
	TableAliasApproval = "aaap"
	// TableNameJTI records spent JWT ids until they expire, so each agent or
	// host JWT authenticates once on every server sharing the database.
	TableNameJTI  = "auth_agent_jti"
	TableAliasJTI = "aajt"
)

The plugin's tables. No foreign keys (ADR-0002): UserTables names the user columns, so auth.DeleteUser removes a user's hosts, agents, grants and approval requests.

View Source
const (
	ColID          = "id"
	ColName        = "name"
	ColUserID      = "user_id"
	ColHostID      = "host_id"
	ColAgentID     = "agent_id"
	ColStatus      = "status"
	ColPublicKey   = "public_key"
	ColKid         = "kid"
	ColActivatedAt = "activated_at"
	ColExpiresAt   = "expires_at"
	ColLastUsedAt  = "last_used_at"
	ColCreatedAt   = "created_at"
	ColUpdatedAt   = "updated_at"
)

Column names shared by several tables.

View Source
const (
	// ColDefaultCapabilities is a host's capability budget, a JSON array:
	// capabilities its agents receive without asking a user.
	ColDefaultCapabilities = "default_capabilities"
	// ColMode is an agent's mode, ModeDelegated or ModeAutonomous.
	ColMode = "mode"
	// ColMetadata is an agent's JSON object of display metadata, or NULL.
	ColMetadata = "metadata"
	// ColCapability is the capability a grant covers.
	ColCapability = "capability"
	// ColConstraints is a grant's JSON constraint object, or NULL for an
	// unrestricted grant.
	ColConstraints = "constraints"
	ColGrantedBy   = "granted_by"
	ColDeniedBy    = "denied_by"
	ColReason      = "reason"
	// ColMethod is an approval request's method, MethodDeviceAuthorization or
	// MethodCIBA.
	ColMethod = "method"
	// ColCapabilities is an approval request's capabilities, space-joined.
	ColCapabilities = "capabilities"
	// ColUserCodeHash is the SHA-256 of the normalized user code.
	ColUserCodeHash = "user_code_hash"
	// ColLoginHint is a CIBA request's login hint, as the agent sent it.
	ColLoginHint = "login_hint"
	// ColBindingMessage is the message shown to the user with a request.
	ColBindingMessage = "binding_message"
	// ColInterval is the polling interval in seconds the agent was told.
	ColInterval = "interval_seconds"
	// ColKind is what an approval request asks for: NULL for capabilities
	// (and the agent's registration), ApprovalKindClaim for the claim of an
	// autonomous agent. Approving a claim transfers the agent to the user,
	// so the two must never be confused.
	ColKind = "kind"
	// ColJWKSURL is the https URL of a host's or an agent's JWKS, used
	// instead of a stored key under AllowJWKSURLs: the signing key is the
	// one whose kid the JWT's header names.
	ColJWKSURL = "jwks_url"
	// ColEnrollmentHash is the SHA-256 of a host's one-time enrollment
	// token, NULL once the host enrolled (or for a host created with a key).
	ColEnrollmentHash = "enrollment_token_hash"
	// ColEnrollmentExpiresAt is when the enrollment token stops working.
	ColEnrollmentExpiresAt = "enrollment_token_expires_at"
)

Columns of one table each.

View Source
const ApprovalKindClaim = "claim"

ApprovalKindClaim marks an approval request that claims an autonomous agent (ColKind).

View Source
const DefaultEnrollmentTokenTTL = time.Hour

DefaultEnrollmentTokenTTL is how long a host's enrollment token works, better-auth's enrollmentTokenTTL.

View Source
const PluginName = "agentauth"

PluginName is the plugin's permanent identity, and the segment its user operations are served under.

View Source
const WarnJWKSURLs = "" /* 161-byte string literal not displayed */

WarnJWKSURLs is what Init logs at every start under AllowJWKSURLs.

Variables

View Source
var (
	ErrInvalidRequest            = auth.NewError("agentauth.invalid_request", http.StatusBadRequest, "Malformed request, missing required fields, or invalid parameter types.")
	ErrInvalidJWT                = auth.NewError("agentauth.invalid_jwt", http.StatusUnauthorized, "JWT is invalid, expired, or signature failed.")
	ErrJTIReplay                 = auth.NewError("agentauth.jti_replay", http.StatusUnauthorized, "JWT has already been used.")
	ErrRequestBindingMismatch    = auth.NewError("agentauth.request_binding_mismatch", http.StatusUnauthorized, "Request binding does not match the JWT.")
	ErrInvalidPublicKey          = auth.NewError("agentauth.invalid_public_key", http.StatusBadRequest, "Public key is invalid or malformed.")
	ErrUnsupportedAlgorithm      = auth.NewError("agentauth.unsupported_algorithm", http.StatusBadRequest, "Key algorithm is not in the server's supported set.")
	ErrUnsupportedMode           = auth.NewError("agentauth.unsupported_mode", http.StatusBadRequest, "Requested mode is not supported by this server.")
	ErrAuthenticationRequired    = auth.NewError("agentauth.authentication_required", http.StatusUnauthorized, "An agent JWT, a host JWT or a signed-in user is required.")
	ErrUnauthorized              = auth.NewError("agentauth.unauthorized", http.StatusForbidden, "Caller is not authorized for this operation.")
	ErrAgentNotFound             = auth.NewError("agentauth.agent_not_found", http.StatusNotFound, "Agent not found.")
	ErrAgentRevoked              = auth.NewError("agentauth.agent_revoked", http.StatusForbidden, "Agent has been revoked.")
	ErrAgentPending              = auth.NewError("agentauth.agent_pending", http.StatusForbidden, "Agent is still pending approval.")
	ErrAgentRejected             = auth.NewError("agentauth.agent_rejected", http.StatusForbidden, "Agent registration was denied.")
	ErrAgentClaimed              = auth.NewError("agentauth.agent_claimed", http.StatusForbidden, "Agent has been claimed and is no longer active.")
	ErrAgentExpired              = auth.NewError("agentauth.agent_expired", http.StatusUnauthorized, "Agent session has expired.")
	ErrAbsoluteLifetimeExceeded  = auth.NewError("agentauth.absolute_lifetime_exceeded", http.StatusForbidden, "Agent's absolute lifetime has elapsed.")
	ErrAgentExists               = auth.NewError("agentauth.agent_exists", http.StatusConflict, "An agent with this public key is already registered.")
	ErrAgentLimitReached         = auth.NewError("agentauth.agent_limit_reached", http.StatusBadRequest, "Maximum number of active agents per user reached.")
	ErrAutonomousOwnerRequired   = auth.NewError("agentauth.autonomous_owner_required", http.StatusUnauthorized, "Autonomous agents require an owner to be resolved.")
	ErrHostNotFound              = auth.NewError("agentauth.host_not_found", http.StatusNotFound, "Host not found.")
	ErrHostRevoked               = auth.NewError("agentauth.host_revoked", http.StatusForbidden, "Host has been revoked.")
	ErrHostAlreadyLinked         = auth.NewError("agentauth.host_already_linked", http.StatusConflict, "Host is already linked to a different user.")
	ErrDynamicHostRegistration   = auth.NewError("agentauth.dynamic_host_registration_disabled", http.StatusForbidden, "Dynamic host registration is disabled: enable AllowDynamicHostRegistration, or create the host first with the create_host operation.")
	ErrInvalidCapabilities       = auth.NewError("agentauth.invalid_capabilities", http.StatusBadRequest, "One or more requested capability names don't exist.")
	ErrCapabilityBlocked         = auth.NewError("agentauth.capability_blocked", http.StatusBadRequest, "One or more requested capabilities are blocked by server policy.")
	ErrCapabilityNotFound        = auth.NewError("agentauth.capability_not_found", http.StatusNotFound, "Capability does not exist.")
	ErrAlreadyGranted            = auth.NewError("agentauth.already_granted", http.StatusConflict, "All requested capabilities are already granted.")
	ErrCapabilityNotGranted      = auth.NewError("agentauth.capability_not_granted", http.StatusForbidden, "Agent does not have an active grant for this capability.")
	ErrGrantRevoked              = auth.NewError("agentauth.grant_revoked", http.StatusForbidden, "Capability grant has been revoked.")
	ErrConstraintViolated        = auth.NewError("agentauth.constraint_violated", http.StatusForbidden, "One or more capability constraints were violated.")
	ErrUnknownConstraintOperator = auth.NewError("agentauth.unknown_constraint_operator", http.StatusBadRequest, "Constraint contains an unrecognized operator.")
	ErrExecuteNotConfigured      = auth.NewError("agentauth.execute_not_configured", http.StatusNotImplemented, "Server has not configured a capability execution handler.")
	ErrApprovalNotFound          = auth.NewError("agentauth.capability_request_not_found", http.StatusNotFound, "Capability request not found.")
	ErrApprovalResolved          = auth.NewError("agentauth.capability_request_already_resolved", http.StatusPreconditionFailed, "Capability request has already been resolved.")
	ErrApprovalOwnerMismatch     = auth.NewError("agentauth.capability_request_owner_mismatch", http.StatusForbidden, "Capability request does not belong to this user.")
	ErrInvalidUserCode           = auth.NewError("agentauth.invalid_user_code", http.StatusForbidden, "The user code is missing or does not match.")
	ErrApprovalExpired           = auth.NewError("agentauth.approval_expired", http.StatusForbidden, "The approval request has expired.")
	ErrEnrollmentTokenInvalid    = auth.NewError("agentauth.enrollment_token_invalid", http.StatusUnauthorized, "The enrollment token is invalid or was already used.")
	ErrEnrollmentTokenExpired    = auth.NewError("agentauth.enrollment_token_expired", http.StatusUnauthorized, "The enrollment token has expired.")
	ErrJWKSURLsDisabled          = auth.NewError("agentauth.jwks_url_disabled", http.StatusBadRequest, "This server does not accept JWKS URLs; send a public key.")
	ErrInvalidJWKSURL            = auth.NewError("agentauth.invalid_jwks_url", http.StatusBadRequest, "The JWKS URL must be a public https URL.")
)

Errors. The part after "agentauth." is the protocol's error code, which Handler answers as {"error": code, "message": message}.

View Source
var (
	ErrBadIssuer     = errors.New("agentauth: Issuer is an absolute https URL (http only for a loopback host) with no query or fragment")
	ErrBadAlgorithm  = errors.New("agentauth: AllowedKeyAlgorithms names only Ed25519 and P-256, and at least one")
	ErrBadMode       = errors.New("agentauth: Modes names only delegated and autonomous, and at least one")
	ErrBadDuration   = errors.New("agentauth: JWTMaxAge and EnrollmentTokenTTL are positive, and no lifetime is negative")
	ErrBadCapability = errors.New("agentauth: a capability has a unique, non-empty name without whitespace, an absolute location URL when it has one, and a known approval strength")
	ErrBadApproval   = errors.New("agentauth: ApprovalMethods names only ciba and device_authorization, and at least one")
	ErrNoPresence    = errors.New("agentauth: a capability needs WebAuthn approval, so ProofOfPresence must be set")
)

Configuration errors, returned by auth.New.

View Source
var (
	ErrWebAuthnNotEnrolled = auth.NewError("agentauth.webauthn_not_enrolled", http.StatusForbidden, "No passkeys registered. Register a passkey before approving capabilities that require proof of physical presence.")
	ErrWebAuthnFailed      = auth.NewError("agentauth.webauthn_verification_failed", http.StatusForbidden, "WebAuthn verification failed.")
)

Errors of the presence check.

View Source
var (
	TableHost       = sqlb.Table{Name: TableNameHost, Alias: TableAliasHost}
	HostID          = sqlb.Col[string](TableHost, ColID)
	HostName        = sqlb.Col[*string](TableHost, ColName)
	HostUser        = sqlb.Col[*string](TableHost, ColUserID)
	HostDefaultCaps = sqlb.Col[string](TableHost, ColDefaultCapabilities)
	HostPublicKey   = sqlb.Col[string](TableHost, ColPublicKey)
	HostKid         = sqlb.Col[string](TableHost, ColKid)
	HostStatusCol   = sqlb.Col[string](TableHost, ColStatus)
	HostActivated   = sqlb.Col[*time.Time](TableHost, ColActivatedAt)
	HostExpires     = sqlb.Col[*time.Time](TableHost, ColExpiresAt)
	HostLastUsed    = sqlb.Col[*time.Time](TableHost, ColLastUsedAt)
	HostCreated     = sqlb.Col[time.Time](TableHost, ColCreatedAt)
	HostUpdated     = sqlb.Col[time.Time](TableHost, ColUpdatedAt)
	HostJWKSURL     = sqlb.Col[*string](TableHost, ColJWKSURL)
	HostEnrollHash  = sqlb.Col[[]byte](TableHost, ColEnrollmentHash)
	HostEnrollUntil = sqlb.Col[*time.Time](TableHost, ColEnrollmentExpiresAt)

	TableAgent     = sqlb.Table{Name: TableNameAgent, Alias: TableAliasAgent}
	AgentID        = sqlb.Col[string](TableAgent, ColID)
	AgentName      = sqlb.Col[string](TableAgent, ColName)
	AgentUser      = sqlb.Col[*string](TableAgent, ColUserID)
	AgentHost      = sqlb.Col[string](TableAgent, ColHostID)
	AgentStatusCol = sqlb.Col[string](TableAgent, ColStatus)
	AgentMode      = sqlb.Col[string](TableAgent, ColMode)
	AgentPublicKey = sqlb.Col[string](TableAgent, ColPublicKey)
	AgentKid       = sqlb.Col[*string](TableAgent, ColKid)
	AgentLastUsed  = sqlb.Col[*time.Time](TableAgent, ColLastUsedAt)
	AgentActivated = sqlb.Col[*time.Time](TableAgent, ColActivatedAt)
	AgentExpires   = sqlb.Col[*time.Time](TableAgent, ColExpiresAt)
	AgentMetadata  = sqlb.Col[*string](TableAgent, ColMetadata)
	AgentCreated   = sqlb.Col[time.Time](TableAgent, ColCreatedAt)
	AgentUpdated   = sqlb.Col[time.Time](TableAgent, ColUpdatedAt)
	AgentJWKSURL   = sqlb.Col[*string](TableAgent, ColJWKSURL)

	TableGrant       = sqlb.Table{Name: TableNameGrant, Alias: TableAliasGrant}
	GrantID          = sqlb.Col[string](TableGrant, ColID)
	GrantAgent       = sqlb.Col[string](TableGrant, ColAgentID)
	GrantCapability  = sqlb.Col[string](TableGrant, ColCapability)
	GrantConstraints = sqlb.Col[*string](TableGrant, ColConstraints)
	GrantGrantedBy   = sqlb.Col[*string](TableGrant, ColGrantedBy)
	GrantDeniedBy    = sqlb.Col[*string](TableGrant, ColDeniedBy)
	GrantReason      = sqlb.Col[*string](TableGrant, ColReason)
	GrantStatusCol   = sqlb.Col[string](TableGrant, ColStatus)
	GrantExpires     = sqlb.Col[*time.Time](TableGrant, ColExpiresAt)
	GrantCreated     = sqlb.Col[time.Time](TableGrant, ColCreatedAt)
	GrantUpdated     = sqlb.Col[time.Time](TableGrant, ColUpdatedAt)

	TableApproval        = sqlb.Table{Name: TableNameApproval, Alias: TableAliasApproval}
	ApprovalID           = sqlb.Col[string](TableApproval, ColID)
	ApprovalMethod       = sqlb.Col[string](TableApproval, ColMethod)
	ApprovalAgent        = sqlb.Col[string](TableApproval, ColAgentID)
	ApprovalHost         = sqlb.Col[*string](TableApproval, ColHostID)
	ApprovalUser         = sqlb.Col[*string](TableApproval, ColUserID)
	ApprovalCapabilities = sqlb.Col[string](TableApproval, ColCapabilities)
	ApprovalStatusCol    = sqlb.Col[string](TableApproval, ColStatus)
	ApprovalCodeHash     = sqlb.Col[[]byte](TableApproval, ColUserCodeHash)
	ApprovalLoginHint    = sqlb.Col[*string](TableApproval, ColLoginHint)
	ApprovalBinding      = sqlb.Col[*string](TableApproval, ColBindingMessage)
	ApprovalInterval     = sqlb.Col[int64](TableApproval, ColInterval)
	ApprovalKind         = sqlb.Col[*string](TableApproval, ColKind)
	ApprovalExpires      = sqlb.Col[time.Time](TableApproval, ColExpiresAt)
	ApprovalCreated      = sqlb.Col[time.Time](TableApproval, ColCreatedAt)
	ApprovalUpdated      = sqlb.Col[time.Time](TableApproval, ColUpdatedAt)

	TableJTI   = sqlb.Table{Name: TableNameJTI, Alias: TableAliasJTI}
	JTIID      = sqlb.Col[string](TableJTI, ColID)
	JTIExpires = sqlb.Col[time.Time](TableJTI, ColExpiresAt)
)

Typed tables and columns.

View Source
var ApprovalMethodValues = []string{MethodCIBA, MethodDeviceAuthorization}

ApprovalMethodValues is every approval method, and the default of ApprovalMethods, better-auth's default order.

ApprovalStrengthValues is every strength.

View Source
var ErrBadOpenAPI = errors.New("agentauth: the OpenAPI document is not valid JSON with unique operationIds")

ErrBadOpenAPI is a spec the OpenAPI adapter cannot read.

View Source
var ErrUpstream = errors.New("agentauth: upstream API error")

ErrUpstream wraps an upstream answer that was not a success.

ModeValues is every mode, and the default of Modes.

Functions

This section is empty.

Types

type AgentSession

type AgentSession struct {
	Type    Mode         `json:"type"`
	AgentID string       `json:"agentId"`
	UserID  *string      `json:"userId"`
	Agent   SessionAgent `json:"agent"`
	Host    *SessionHost `json:"host"`
	User    SessionUser  `json:"user"`
}

AgentSession is an authenticated agent request, better-auth's AgentSession: the agent, its effective grants (the active, unexpired ones, narrowed to the JWT's capabilities claim when it has one), its host and the user it acts for.

func (AgentSession) HasGrant

func (s AgentSession) HasGrant(capability string) (SessionGrant, bool)

HasGrant reports whether the session holds an effective grant of capability, for an app route that serves a capability with a Location. Constraints are the route's to enforce, with Constraints.Validate.

type AgentStatus

type AgentStatus string

AgentStatus is an agent's lifecycle state.

const (
	AgentActive   AgentStatus = "active"
	AgentPending  AgentStatus = "pending"
	AgentExpired  AgentStatus = "expired"
	AgentRevoked  AgentStatus = "revoked"
	AgentRejected AgentStatus = "rejected"
	AgentClaimed  AgentStatus = "claimed"
)

Agent states.

type AgentStatusView

type AgentStatusView struct {
	AgentID     string      `json:"agent_id"`
	Name        string      `json:"name"`
	HostID      string      `json:"host_id"`
	Status      AgentStatus `json:"status"`
	Grants      []GrantView `json:"agent_capability_grants"`
	Mode        Mode        `json:"mode"`
	UserID      *string     `json:"user_id"`
	ActivatedAt *string     `json:"activated_at"`
	CreatedAt   *string     `json:"created_at"`
	LastUsedAt  *string     `json:"last_used_at"`
	ExpiresAt   *string     `json:"expires_at"`
}

AgentStatusView is an agent's status and grants.

type Approval

type Approval struct {
	Method string `json:"method"`
	// DeviceCode is the approval request's id.
	DeviceCode              string `json:"device_code,omitempty"`
	VerificationURI         string `json:"verification_uri,omitempty"`
	VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
	// UserCode is shown to the user, who types it on the approval page.
	UserCode  string `json:"user_code,omitempty"`
	ExpiresIn int64  `json:"expires_in"`
	Interval  int64  `json:"interval"`
}

Approval tells the agent how the user approves it: for device authorization RFC 8628's shape; for CIBA only the method, the lifetime and the polling interval, since the request reaches the user directly.

type ApprovalHints

type ApprovalHints struct {
	PreferredMethod string `json:"preferred_method,omitempty"`
	LoginHint       string `json:"login_hint,omitempty"`
	BindingMessage  string `json:"binding_message,omitempty"`
}

ApprovalHints are the request fields that steer an approval, protocol section 9: the agent's preferred method, the CIBA login hint and the message shown to the user.

type ApprovalMethodContext

type ApprovalMethodContext struct {
	// UserID is the user the approval is for, nil when nobody owns the
	// agent or its host yet (only device authorization can serve that).
	UserID       *string
	AgentName    string
	HostID       string
	Capabilities []string
	// PreferredMethod is the agent's preferred_method, possibly empty.
	PreferredMethod string
	// SupportedMethods is ApprovalMethods.
	SupportedMethods []string
}

ApprovalMethodContext is what an ApprovalMethodFunc chooses from, better-auth's resolveApprovalMethod context.

type ApprovalMethodFunc

type ApprovalMethodFunc func(ctx context.Context, mc ApprovalMethodContext) (string, error)

ApprovalMethodFunc chooses the approval method of one request, better-auth's resolveApprovalMethod. A method outside SupportedMethods, or CIBA for a request with no user, falls back to device authorization.

type ApprovalStatus

type ApprovalStatus string

ApprovalStatus is an approval request's state.

const (
	ApprovalPending  ApprovalStatus = "pending"
	ApprovalApproved ApprovalStatus = "approved"
	ApprovalDenied   ApprovalStatus = "denied"
)

Approval request states.

const ApprovalWebAuthnRequired ApprovalStatus = "webauthn_required"

ApprovalWebAuthnRequired is ApproveResult.Status when the approval waits for a WebAuthn assertion: nothing was approved, and WebAuthn carries the challenge to answer.

type ApprovalStrength

type ApprovalStrength string

ApprovalStrength is what a user must show to approve a capability.

const (
	// StrengthNone marks a capability that needs no user interaction; it
	// is informational, as in better-auth: the host's budget is what
	// grants without asking.
	StrengthNone ApprovalStrength = "none"
	// StrengthSession needs a signed-in user (device authorization or
	// CIBA). The default.
	StrengthSession ApprovalStrength = "session"
	// StrengthWebAuthn needs a WebAuthn assertion with user verification
	// on top of the session: proof that a person, not an agent driving
	// the browser, approved. Needs ProofOfPresence.
	StrengthWebAuthn ApprovalStrength = "webauthn"
)

Approval strengths.

type ApproveRequest

type ApproveRequest struct {
	// AgentID names the agent; ApprovalID names one approval request
	// instead (the CIBA inbox's approval_id). One of them is required.
	AgentID    string `json:"agent_id,omitempty"`
	ApprovalID string `json:"approval_id,omitempty"`
	// UserCode is the code the agent showed the user.
	UserCode string `json:"user_code,omitempty"`
	// Action is ActionApprove or ActionDeny.
	Action string `json:"action"`
	// Capabilities narrows an approval to these names; the rest are
	// denied. Empty approves every pending capability.
	Capabilities []string `json:"capabilities,omitempty"`
	// TTLSeconds overrides the approved grants' lifetime.
	TTLSeconds int64  `json:"ttl,omitempty"`
	Reason     string `json:"reason,omitempty"`
	// WebAuthn answers the challenge of an earlier ApprovalWebAuthnRequired
	// result.
	WebAuthn *PresenceResponse `json:"webauthn_response,omitempty"`
}

ApproveRequest is a signed-in user's answer to a pending agent or capability request. A device authorization request needs its user code; a CIBA request is answered by the user it was addressed to; with neither pending, only the agent's or host's owner may answer.

type ApproveResult

type ApproveResult struct {
	Status   ApprovalStatus     `json:"status"`
	AgentID  string             `json:"agent_id"`
	Added    []string           `json:"added"`
	WebAuthn *PresenceChallenge `json:"webauthn,omitempty"`
	// Claimed reports an approved claim (Claim): the user now owns the
	// agent, which is AgentClaimed.
	Claimed bool `json:"claimed,omitempty"`
}

ApproveResult is the outcome. Status ApprovalWebAuthnRequired approved nothing yet: answer WebAuthn and call again.

type AutonomousUserFunc

type AutonomousUserFunc func(ctx context.Context, hostID, agentID string) (*SessionUser, error)

AutonomousUserFunc names the user an autonomous agent on an unclaimed host acts as, better-auth's resolveAutonomousUser. Nil, or a nil user, refuses the agent with ErrAutonomousOwnerRequired.

type Bearer

type Bearer struct {
	Token string `json:"-"`
	// Method is the HTTP method, URL the request URL without its query.
	Method string `json:"-"`
	URL    string `json:"-"`
	// BodySHA256 is the base64url SHA-256 of the raw request body.
	BodySHA256 string `json:"-"`
}

Bearer is the JWT an agent or host presented, and the request it came with, for the request-binding claims (htm, htu, ath). Handler fills it from the Authorization header; a Go caller sets Token, and Method, URL and BodySHA256 when the token is bound to a request.

type CIBAAuthorizeRequest

type CIBAAuthorizeRequest struct {
	Bearer
	// LoginHint names the user by any sign-in identifier (an email, a
	// phone number, a username); only a verified one matches.
	LoginHint    string   `json:"login_hint"`
	Capabilities []string `json:"capabilities,omitempty"`
	// BindingMessage is shown to the user beside the request.
	BindingMessage string `json:"binding_message,omitempty"`
	AgentID        string `json:"agent_id"`
}

CIBAAuthorizeRequest is a host's backchannel authentication request (protocol section 9.2): ask the user named by LoginHint to approve the host's agent AgentID. Bearer is the host's JWT.

type CIBAAuthorizeResult

type CIBAAuthorizeResult struct {
	AuthReqID string `json:"auth_req_id"`
	ExpiresIn int64  `json:"expires_in"`
	Interval  int64  `json:"interval"`
}

CIBAAuthorizeResult is the request's handle. Its shape is the same whether or not LoginHint named a user, so it never reveals who has an account.

type Capability

type Capability struct {
	// Name is the identifier agents request and execute. Unique.
	Name string `json:"name"`
	// Description says what it does, for the agent and the approving user.
	Description string `json:"description"`
	// Location is the absolute URL where the capability executes when it is
	// not the execute endpoint; an agent JWT for it names it as aud.
	Location string `json:"location,omitempty"`
	// Input and Output are JSON Schemas of the arguments and of the result.
	Input  json.RawMessage `json:"input,omitempty"`
	Output json.RawMessage `json:"output,omitempty"`
	// RequiredConstraints are argument fields an agent must constrain when
	// it requests the capability (protocol section 2.13).
	RequiredConstraints []string `json:"-"`
	// GrantTTL is how long a grant of it lasts; zero never expires.
	GrantTTL time.Duration `json:"-"`
	// ApprovalStrength is what approving it takes (protocol section
	// 8.11); empty is StrengthSession.
	ApprovalStrength ApprovalStrength `json:"approval_strength,omitempty"`
}

Capability is one thing an agent may do, protocol section 4.

func FromOpenAPI

func FromOpenAPI(spec []byte) ([]Capability, error)

FromOpenAPI derives capabilities from an OpenAPI 3.x document, one per operation with an operationId, better-auth's fromOpenAPI: the name is the operationId, the description its description or summary, the input schema its parameters and JSON body, the output schema its 200 or 201 JSON answer.

type CapabilityList

type CapabilityList struct {
	Capabilities []CapabilityView `json:"capabilities"`
	HasMore      bool             `json:"has_more"`
	NextCursor   string           `json:"next_cursor,omitempty"`
}

CapabilityList is one page of the catalogue.

type CapabilityRequest

type CapabilityRequest struct {
	Name        string      `json:"name"`
	Constraints Constraints `json:"constraints,omitempty"`
}

CapabilityRequest is one requested capability: a bare name, or an object with a name and the constraints the agent proposes for its grant.

func (*CapabilityRequest) UnmarshalJSON

func (r *CapabilityRequest) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts "name" or {"name": ..., "constraints": {...}}. The parameter is the JSON boundary the standard library mandates.

type CapabilityView

type CapabilityView struct {
	Name        string          `json:"name"`
	Description string          `json:"description"`
	Location    string          `json:"location,omitempty"`
	Input       json.RawMessage `json:"input,omitempty"`
	Output      json.RawMessage `json:"output,omitempty"`
	InputFields []InputField    `json:"input_fields,omitempty"`
	// ApprovalStrength is the capability's, when it sets one.
	ApprovalStrength ApprovalStrength `json:"approval_strength,omitempty"`
	// GrantStatus is "granted" or "not_granted" for an authenticated
	// caller, and empty otherwise.
	GrantStatus string `json:"grant_status,omitempty"`
}

CapabilityView is a catalogue entry as an agent sees it.

type ClaimRequest

type ClaimRequest struct {
	Bearer
	AgentID string `json:"agent_id"`
	ApprovalHints
}

ClaimRequest starts the claim of an autonomous agent, protocol section 3.4: a host (Bearer, a host JWT; under AllowDynamicHostRegistration a new host registers itself here, as at /agent/register) asks a user to take over the agent AgentID, typically one the user found on a resource the agent made.

type ClaimResult

type ClaimResult struct {
	AgentID  string      `json:"agent_id"`
	HostID   string      `json:"host_id"`
	Name     string      `json:"name"`
	Mode     Mode        `json:"mode"`
	Status   AgentStatus `json:"status"`
	Grants   []GrantView `json:"agent_capability_grants"`
	Approval *Approval   `json:"approval"`
}

ClaimResult is the agent to be claimed and the approval the user must give: the user code to enter on the device authorization page.

type Claims

type Claims struct {
	Issuer    string   `json:"iss"`
	Subject   string   `json:"sub"`
	Audience  audience `json:"aud"`
	ExpiresAt int64    `json:"exp"`
	IssuedAt  int64    `json:"iat"`
	ID        string   `json:"jti"`
	// Capabilities narrows an agent JWT to these capabilities (protocol
	// section 5.3); nil means every grant.
	Capabilities []string `json:"capabilities"`
	// Htm, Htu and Ath bind the token to one request: its method, its URL
	// without the query, and the base64url SHA-256 of its body.
	Htm string `json:"htm"`
	Htu string `json:"htu"`
	Ath string `json:"ath"`
	// AgentPublicKey is the key of the agent a host JWT registers.
	AgentPublicKey *PublicKey `json:"agent_public_key"`
	// AgentJWKSURL is, instead of AgentPublicKey, the https URL the
	// registered agent's keys are fetched from (AllowJWKSURLs).
	AgentJWKSURL string `json:"agent_jwks_url"`
	// HostPublicKey is the host's own key, for dynamic registration.
	HostPublicKey *PublicKey `json:"host_public_key"`
	// HostName names the host for display.
	HostName string `json:"host_name"`
}

Claims are the claims an agent or host JWT carries. Only a verified token produces trusted Claims.

type Configuration

type Configuration struct {
	Version         string            `json:"version"`
	ProviderName    string            `json:"provider_name"`
	Description     string            `json:"description"`
	Issuer          string            `json:"issuer"`
	DefaultLocation string            `json:"default_location"`
	Algorithms      []string          `json:"algorithms"`
	Modes           []Mode            `json:"modes"`
	ApprovalMethods []string          `json:"approval_methods"`
	Endpoints       map[string]string `json:"endpoints"`
}

Configuration is the discovery document, protocol section 5.1.

type Constraint

type Constraint struct {
	Eq    *Primitive
	Min   *float64
	Max   *float64
	In    []Primitive
	NotIn []Primitive
	// Unknown holds operators the protocol does not define.
	Unknown map[string]json.RawMessage
	// contains filtered or unexported fields
}

Constraint is one argument's rule: a bare primitive is shorthand for eq; an object names operators. Unknown operators are kept, so they are stored as the agent sent them and refused at execution with ErrUnknownConstraintOperator, as in better-auth.

func Eq

func Eq(v Primitive) Constraint

Eq returns the shorthand constraint "equals v".

func (Constraint) MarshalJSON

func (c Constraint) MarshalJSON() ([]byte, error)

MarshalJSON writes the constraint in the form it was read.

func (*Constraint) UnmarshalJSON

func (c *Constraint) UnmarshalJSON(b []byte) error

UnmarshalJSON reads a bare primitive or an operator object. The parameter is the JSON boundary the standard library mandates.

type Constraints

type Constraints map[string]Constraint

Constraints scope a grant: each key is an argument name, each value the rule the argument must satisfy, such as {"amount": {"max": 1000}, "currency": {"in": ["USD", "EUR"]}}. Nil is an unrestricted grant.

func (Constraints) Covers

func (cs Constraints) Covers(req Constraints) bool

Covers reports whether a grant scoped by cs already allows everything a request scoped by req would, better-auth's constraintsCover: an unrestricted grant covers everything, a restricted one never covers an unrestricted request, and otherwise each requested field must be within the existing rule on it.

func (Constraints) Validate

func (cs Constraints) Validate(args map[string]any) error

Validate checks args against the constraints: ErrUnknownConstraintOperator when a rule names an operator the protocol does not define (checked first, as better-auth does), ErrConstraintViolated when an argument breaks a rule. A missing argument breaks every rule on it.

type CreateHostRequest

type CreateHostRequest struct {
	Name                string    `json:"name,omitempty"`
	PublicKey           PublicKey `json:"public_key"`
	JWKSURL             string    `json:"jwks_url,omitempty"`
	DefaultCapabilities []string  `json:"default_capabilities,omitempty"`
}

CreateHostRequest creates a host for the signed-in user, protocol section 3.2: the host's key, and the budget its agents receive without asking. Instead of a key the host may name a JWKS URL (AllowJWKSURLs); with neither, the host is created for enrollment and a device binds its key later with the returned token (EnrollHost).

type CreateHostResult

type CreateHostResult struct {
	HostID                   string     `json:"host_id"`
	DefaultCapabilities      []string   `json:"default_capabilities"`
	Status                   HostStatus `json:"status"`
	EnrollmentToken          string     `json:"enrollment_token,omitempty"`
	EnrollmentTokenExpiresAt *time.Time `json:"enrollment_token_expires_at,omitempty"`
	// Reactivated is true when the call took over or reactivated an
	// existing host with the same key (the host.reactivated event).
	Reactivated bool `json:"-"`
}

CreateHostResult is the created host. EnrollmentToken is set only for a host created for enrollment, and only in this answer: the server keeps its SHA-256.

type DescribeCapabilityRequest

type DescribeCapabilityRequest struct {
	Bearer
	Name string `json:"name"`
}

DescribeCapabilityRequest names one capability, protocol section 6.2.1.

type EnrollHostRequest

type EnrollHostRequest struct {
	Token     string    `json:"token"`
	PublicKey PublicKey `json:"public_key"`
	Name      string    `json:"name,omitempty"`
}

EnrollHostRequest binds a device's key to a host created for enrollment (CreateHost with neither a key nor a JWKS URL), protocol section 3.2. Token is the one-time enrollment token CreateHost returned.

type EnrollHostResult

type EnrollHostResult struct {
	HostID              string     `json:"host_id"`
	Name                *string    `json:"name"`
	DefaultCapabilities []string   `json:"default_capabilities"`
	Status              HostStatus `json:"status"`
}

EnrollHostResult is the enrolled host.

type Event

type Event struct {
	Type      string
	ActorID   string
	ActorType string
	AgentID   string
	HostID    string
	// Capability, Arguments, Status, Duration and Error describe a
	// capability.executed event.
	Capability string
	Arguments  map[string]any
	Status     string
	Duration   time.Duration
	Error      string
	// Capabilities are the capabilities a request, approval or denial
	// named.
	Capabilities []string
	// ApprovalID, Method, UserID and BindingMessage describe an
	// approval.created event: the request, its approval method, the user
	// it waits for (nil when nobody owns the agent yet) and the message to
	// show. A CIBA request reaches its user only through the app, so this
	// event is where the app sends the notification.
	ApprovalID     string
	Method         string
	UserID         *string
	BindingMessage string
}

Event is one audit event, better-auth's AgentAuthEvent: who did what to which agent or host. The capability.executed fields are set only on that type.

type EventFunc

type EventFunc func(ctx context.Context, e Event)

EventFunc receives events. It runs after the commit, in the background (auth.WithBackground), and its panics and slowness never reach the request, better-auth's emit that never throws.

type ExecuteFunc

type ExecuteFunc func(ctx context.Context, ex Execution) (any, error)

ExecuteFunc runs a capability for an agent, better-auth's onExecute. The plugin has checked the agent and the grant before it is called. Its result is sent to the agent as {"data": result}; it is any because each capability's result has its own shape and goes straight to the JSON encoder. An *auth.Error it returns reaches the agent as it is; any other error is an internal error.

func OpenAPIHandler

func OpenAPIHandler(spec []byte, o OpenAPIHandlerOptions) (ExecuteFunc, error)

OpenAPIHandler returns an ExecuteFunc that proxies each capability to its OpenAPI operation, better-auth's createOpenAPIHandler: path parameters are escaped into the path, query and header parameters set, the other arguments sent as the JSON body (not for GET or HEAD). A JSON answer is returned verbatim, any other as text; a 202's body is returned as it is. An answer that is neither 2xx is ErrUpstream, which reaches the agent as an internal error.

type ExecuteRequest

type ExecuteRequest struct {
	Bearer
	Capability string `json:"capability"`
	// Arguments are the capability's arguments, as JSON decoded them.
	Arguments map[string]any `json:"arguments,omitempty"`
}

ExecuteRequest runs a capability, protocol section 6.11. Bearer is the agent's JWT.

type ExecuteResult

type ExecuteResult struct {
	// Data is whatever ExecuteFunc returned, for the JSON encoder.
	Data any `json:"data"`
}

ExecuteResult is the capability's result, better-auth's sync answer.

type Execution

type Execution struct {
	Capability Capability
	Arguments  map[string]any
	Session    AgentSession
	// GrantID is the grant that authorized the call.
	GrantID string
	// ConsumeGrant spends the grant, for a single-use capability such as a
	// payment: it becomes GrantConsumed and authorizes nothing more.
	ConsumeGrant func(ctx context.Context) error
}

Execution is what ExecuteFunc receives.

type GrantStatus

type GrantStatus string

GrantStatus is a capability grant's state.

const (
	GrantActive   GrantStatus = "active"
	GrantPending  GrantStatus = "pending"
	GrantDenied   GrantStatus = "denied"
	GrantRevoked  GrantStatus = "revoked"
	GrantConsumed GrantStatus = "consumed"
)

Grant states. GrantConsumed is a single-use grant spent by Execution.ConsumeGrant.

type GrantView

type GrantView struct {
	Capability  string          `json:"capability"`
	Status      GrantStatus     `json:"status"`
	GrantedBy   *string         `json:"granted_by,omitempty"`
	Constraints Constraints     `json:"constraints,omitempty"`
	ExpiresAt   *string         `json:"expires_at,omitempty"`
	Description string          `json:"description,omitempty"`
	Input       json.RawMessage `json:"input,omitempty"`
	Output      json.RawMessage `json:"output,omitempty"`
	Reason      *string         `json:"reason,omitempty"`
}

GrantView is a grant as the protocol shows it, section 5.3.

type HeadersFunc

type HeadersFunc func(ctx context.Context, capability string, s AgentSession) (http.Header, error)

HeadersFunc returns the headers a proxied call carries upstream, such as the user's access token there, better-auth's resolveHeaders. They win over header parameters taken from the arguments, so an agent can never replace the credentials.

type HostCapabilitiesContext

type HostCapabilitiesContext struct {
	// Mode is the registering agent's mode; empty when a user creates or
	// claims the host.
	Mode Mode
	// UserID is the host's owner, nil for a host nobody owns yet.
	UserID *string
	// HostID is empty for a host about to be created.
	HostID   string
	HostName string
}

HostCapabilitiesContext is what a HostCapabilitiesFunc decides from, better-auth's DefaultHostCapabilitiesContext.

type HostCapabilitiesFunc

type HostCapabilitiesFunc func(ctx context.Context, hc HostCapabilitiesContext) ([]string, error)

HostCapabilitiesFunc computes a host's budget per host, the function form of better-auth's defaultHostCapabilities.

type HostStatus

type HostStatus string

HostStatus is a host's lifecycle state.

const (
	HostActive  HostStatus = "active"
	HostPending HostStatus = "pending"
	HostRevoked HostStatus = "revoked"
	// HostPendingEnrollment is a host created without a key, waiting for
	// a device to enroll with its one-time token (EnrollHost). It
	// authenticates nothing.
	HostPendingEnrollment HostStatus = "pending_enrollment"
)

Host states.

type InputField

type InputField struct {
	Name        string          `json:"name"`
	Type        json.RawMessage `json:"type,omitempty"`
	Description json.RawMessage `json:"description,omitempty"`
}

InputField is one argument of a capability, from its input schema.

type IntrospectGrant

type IntrospectGrant struct {
	Capability string      `json:"capability"`
	Status     GrantStatus `json:"status"`
}

IntrospectGrant is a grant in an introspection answer.

type IntrospectRequest

type IntrospectRequest struct {
	Token string `json:"token"`
}

IntrospectRequest asks whether an agent JWT is valid, protocol section 6.10, for a resource server that received one.

type Introspection

type Introspection struct {
	Active    bool              `json:"active"`
	AgentID   string            `json:"agent_id,omitempty"`
	HostID    string            `json:"host_id,omitempty"`
	UserID    *string           `json:"user_id,omitempty"`
	Grants    []IntrospectGrant `json:"agent_capability_grants,omitempty"`
	Mode      Mode              `json:"mode,omitempty"`
	ExpiresAt *string           `json:"expires_at,omitempty"`
}

Introspection is the answer: Active false and nothing else for any token that would not authenticate.

type ListCapabilitiesRequest

type ListCapabilitiesRequest struct {
	Bearer
	Cursor string `json:"cursor,omitempty"`
	Limit  int    `json:"limit,omitempty"`
}

ListCapabilitiesRequest lists the catalogue, protocol section 6.2. Bearer is optional: with an agent JWT each entry says whether the agent holds it, with a host JWT whether the host's budget covers it.

type Mode

type Mode string

Mode is how an agent acts: for a user who approved it, or on its own.

const (
	// ModeDelegated acts for the user who approved the agent.
	ModeDelegated Mode = "delegated"
	// ModeAutonomous acts on its own, under its host's capability budget,
	// until a user claims the host.
	ModeAutonomous Mode = "autonomous"
)

Modes.

type OpenAPIHandlerOptions

type OpenAPIHandlerOptions struct {
	// BaseURL is the upstream API's base URL; each operation's path is
	// appended to it. Required.
	BaseURL string
	// Headers adds headers to every upstream request.
	Headers HeadersFunc
	// Client sends the requests; nil is http.DefaultClient.
	Client *http.Client
}

OpenAPIHandlerOptions configures OpenAPIHandler.

type OpenAPIOptions

type OpenAPIOptions struct {
	OpenAPIHandlerOptions
	// HostCapabilities picks the default host budget among the
	// operations; nil leaves the budget to other options.
	HostCapabilities OperationFilter
	// Strength assigns each capability's approval strength.
	Strength StrengthFunc
	// Location is set on every capability, for an API whose agents call
	// it directly rather than through the execute endpoint.
	Location string
}

OpenAPIOptions configures FromOpenAPISpec.

type OperationFilter

type OperationFilter func(ctx context.Context, op OperationInfo, hc HostCapabilitiesContext) (bool, error)

OperationFilter decides per operation, better-auth's defaultHostCapabilities filter forms: AllOperations is its true, OperationsWithMethod its method and method-list forms, and any other function its callback, which also receives the host's context.

func AllOperations

func AllOperations() OperationFilter

AllOperations selects every operation.

func OperationsWithMethod

func OperationsWithMethod(methods ...string) OperationFilter

OperationsWithMethod selects the operations of the given HTTP methods, in any case, such as "GET" and "HEAD" for read-only access.

type OperationInfo

type OperationInfo struct {
	// Name is the operationId, the capability's name.
	Name string
	// Method is the HTTP method, upper case.
	Method      string
	Description string
}

OperationInfo is one OpenAPI operation as a filter sees it, better-auth's OpenAPICapabilityInfo.

type Option

type Option func(*Plugin)

Option configures the plugin; pass options to New.

func AbsoluteLifetime

func AbsoluteLifetime(d time.Duration) Option

AbsoluteLifetime refuses an agent this long after its creation, whatever happened since. Zero (the default) disables it.

func AgentMaxLifetime

func AgentMaxLifetime(d time.Duration) Option

AgentMaxLifetime overrides DefaultAgentMaxLifetime; zero disables it.

func AgentSessionTTL

func AgentSessionTTL(d time.Duration) Option

AgentSessionTTL overrides DefaultAgentSessionTTL; zero disables it.

func AllowDynamicHostRegistration

func AllowDynamicHostRegistration() Option

AllowDynamicHostRegistration lets an unknown host register agents with a host JWT that carries its own public key. A significant trust decision, off by default: without it, a host must first be created by a signed-in user (create_host).

func AllowJWKSURLs

func AllowJWKSURLs() Option

AllowJWKSURLs lets a host (CreateHost, UpdateHost) or an agent (the agent_jwks_url claim of a registering host JWT) name an https JWKS URL instead of a stored public key, better-auth's jwks_url: a JWT is then checked against the key whose kid its header names, fetched from the URL and cached (DefaultJWKSCacheTTL). Off by default, because the server then makes outbound requests to URLs a user or a host chose, and whoever can change the document at that URL can sign for the host or agent: Init logs WarnJWKSURLs at every start. The default client fetches https only, refuses private, loopback, link-local and CGNAT addresses after DNS resolution, follows no redirect and reads at most 1 MiB in 5 seconds.

func AllowedKeyAlgorithms

func AllowedKeyAlgorithms(algs ...string) Option

AllowedKeyAlgorithms narrows the key algorithms agents and hosts may use, by JWK curve name. The default is Ed25519 only, as in better-auth.

func ApprovalMethods

func ApprovalMethods(methods ...string) Option

ApprovalMethods narrows ApprovalMethodValues: the methods discovery lists and requests may use. Without MethodCIBA the CIBA endpoint answers invalid_request.

func AutonomousUser

func AutonomousUser(fn AutonomousUserFunc) Option

AutonomousUser sets the AutonomousUserFunc.

func BlockedCapabilities

func BlockedCapabilities(names ...string) Option

BlockedCapabilities can never be granted. A trailing ".*" blocks a family.

func Capabilities

func Capabilities(caps ...Capability) Option

Capabilities is the catalogue agents may request. With a catalogue, a request for a name outside it is refused (ErrInvalidCapabilities); with none, any name is accepted, as in better-auth.

func DefaultHostCapabilities

func DefaultHostCapabilities(names ...string) Option

DefaultHostCapabilities is the budget a dynamically registered or claimed host receives, and a host created without default_capabilities: capabilities its agents get without asking a user.

func DefaultHostCapabilitiesFunc

func DefaultHostCapabilitiesFunc(fn HostCapabilitiesFunc) Option

DefaultHostCapabilitiesFunc computes the budget per host instead of DefaultHostCapabilities' fixed list. Its names are checked against the catalogue like any other.

func DeviceAuthorizationPage

func DeviceAuthorizationPage(u string) Option

DeviceAuthorizationPage overrides DefaultDeviceAuthorizationPage: a path on the issuer's origin or an absolute URL. The page receives agent_id and code, and posts them to the approve_capability operation.

func EnrollmentTokenTTL

func EnrollmentTokenTTL(d time.Duration) Option

EnrollmentTokenTTL overrides DefaultEnrollmentTokenTTL.

func FromOpenAPISpec

func FromOpenAPISpec(spec []byte, o OpenAPIOptions) ([]Option, error)

FromOpenAPISpec derives the plugin's options from an OpenAPI 3.x document, better-auth's createFromOpenAPI: ProviderName and ProviderDescription from its info, the Capabilities, an OnExecute that proxies to the upstream API, and, with HostCapabilities, the default host budget. Pass them to New before any option that should override them:

opts, err := agentauth.FromOpenAPISpec(spec, agentauth.OpenAPIOptions{...})
p := agentauth.New(append(opts, agentauth.Issuer(iss))...)

func Issuer

func Issuer(iss string) Option

Issuer is the base URL of the protocol endpoints and the JWT audience, such as "https://app.example.com/api/agent". Empty (the default) is the app's auth.WithBaseURL.

func JWKSClient

func JWKSClient(c *http.Client) Option

JWKSClient replaces the guarded client AllowJWKSURLs fetches with, the one escape hatch: for a JWKS on a private network, or a test server. The replacement is trusted as it is; it should keep a timeout and refuse redirects.

func JWTMaxAge

func JWTMaxAge(d time.Duration) Option

JWTMaxAge overrides DefaultJWTMaxAge.

func MaxAgentsPerUser

func MaxAgentsPerUser(n int) Option

MaxAgentsPerUser overrides DefaultMaxAgentsPerUser; zero disables it.

func Modes

func Modes(modes ...Mode) Option

Modes narrows ModeValues.

func OnEvent

func OnEvent(fn EventFunc) Option

OnEvent installs an EventFunc, better-auth's onEvent.

func OnExecute

func OnExecute(fn ExecuteFunc) Option

OnExecute runs capabilities for the execute endpoint. Without it the endpoint answers ErrExecuteNotConfigured, and capabilities with a Location run on the app's own routes, which verify the agent with Plugin.VerifyAgentRequest.

func ProofOfPresence

func ProofOfPresence(v PresenceVerifier) Option

ProofOfPresence enables WebAuthn-gated approvals with v, better-auth's proofOfPresence: a capability with StrengthWebAuthn, a first agent on a pending host, and new capabilities for an active agent then need a passkey assertion on top of the signed-in session. Registering a new agent under an already approved host needs the session only.

func ProviderDescription

func ProviderDescription(d string) Option

ProviderDescription is the discovery document's description.

func ProviderName

func ProviderName(name string) Option

ProviderName and ProviderDescription fill the discovery document.

func RequireAuthForCapabilities

func RequireAuthForCapabilities() Option

RequireAuthForCapabilities makes listing and describing capabilities need an agent or host JWT.

func ResolveApprovalMethod

func ResolveApprovalMethod(fn ApprovalMethodFunc) Option

ResolveApprovalMethod installs an ApprovalMethodFunc. The default takes the agent's preferred_method when it is supported, and device authorization otherwise, as better-auth's default does.

func TrustProxy

func TrustProxy() Option

TrustProxy builds the URL a token's htu claim is compared with from X-Forwarded-Proto and X-Forwarded-Host, for a server behind a reverse proxy that terminates TLS, better-auth's trustProxy. Off by default: enable it only when the proxy overwrites both headers. The accepted audiences never come from the request, so this cannot widen them.

type PendingApproval

type PendingApproval struct {
	ApprovalID     string   `json:"approval_id"`
	Method         string   `json:"method"`
	AgentID        string   `json:"agent_id"`
	AgentName      *string  `json:"agent_name"`
	BindingMessage *string  `json:"binding_message"`
	Capabilities   []string `json:"capabilities"`
	// CapabilityConstraints and CapabilityReasons are the agent's pending
	// grants' constraints and reasons, by capability.
	CapabilityConstraints map[string]Constraints `json:"capability_constraints"`
	CapabilityReasons     map[string]string      `json:"capability_reasons"`
	ExpiresIn             int64                  `json:"expires_in"`
	CreatedAt             time.Time              `json:"created_at"`
}

PendingApproval is one request waiting for the signed-in user, the CIBA pending list's entry.

type PendingApprovals

type PendingApprovals struct {
	Requests []PendingApproval `json:"requests"`
}

PendingApprovals is the signed-in user's pending requests, newest first.

type Plugin

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

Plugin is the Agent Auth provider. Build it with New.

func New

func New(opts ...Option) *Plugin

New builds the plugin.

func (*Plugin) ApproveCapability

func (p *Plugin) ApproveCapability(ctx context.Context, in ApproveRequest) (ApproveResult, error)

ApproveCapability answers a pending request for the signed-in user. It needs a fresh sign-in, as better-auth's freshSessionWindow does.

func (*Plugin) CIBAAuthorize

func (p *Plugin) CIBAAuthorize(ctx context.Context, in CIBAAuthorizeRequest) (CIBAAuthorizeResult, error)

CIBAAuthorize creates a CIBA approval request for the host that signed in.Token.

func (*Plugin) Claim

func (p *Plugin) Claim(ctx context.Context, in ClaimRequest) (ClaimResult, error)

Claim records a claim request, better-auth's claimAgent. A signed-in user approving it with ApproveCapability (its user code) becomes the agent's owner: the agent becomes AgentClaimed, which authenticates nothing more, and its host, when nobody owns it, becomes the user's.

func (*Plugin) Configuration

func (p *Plugin) Configuration(ctx context.Context) (Configuration, error)

Configuration returns the discovery document.

func (*Plugin) CreateHost

func (p *Plugin) CreateHost(ctx context.Context, in CreateHostRequest) (CreateHostResult, error)

CreateHost creates a host owned by the signed-in user, or reactivates the user's host with the same key.

func (*Plugin) DataHooks

func (p *Plugin) DataHooks() auth.DataHooks

DataHooks delete a user's agents before the user (deleteUserAgents), which UserTables alone would leave behind.

func (*Plugin) DescribeCapability

func (p *Plugin) DescribeCapability(ctx context.Context, in DescribeCapabilityRequest) (CapabilityView, error)

DescribeCapability returns one catalogue entry with its schemas.

func (*Plugin) EnrollHost

func (p *Plugin) EnrollHost(ctx context.Context, in EnrollHostRequest) (EnrollHostResult, error)

EnrollHost enrolls a device as a host, better-auth's enrollHost: a user creates the host in the app (and its budget), hands the token to the device, and the device enrolls with its own key, so the private key never leaves the device. The token works once, for EnrollmentTokenTTL.

func (*Plugin) Execute

func (p *Plugin) Execute(ctx context.Context, in ExecuteRequest) (ExecuteResult, error)

Execute runs a capability for the agent that signed in.Token.

func (*Plugin) Handler

func (p *Plugin) Handler(opts ...authhttp.Option) http.Handler

Handler serves the protocol endpoints at their paths under the issuer, and the discovery document at /.well-known/agent-configuration: mount it at the root of the issuer's host. opts are the authhttp options the app's auth handler uses, so revoking by a signed-in user reads the same session. Every endpoint calls its operation through the pipeline; an unknown path answers 404.

func (*Plugin) Init

func (p *Plugin) Init(a *auth.Auth) error

Init captures the core and fills the issuer from the base URL.

func (*Plugin) Introspect

func (p *Plugin) Introspect(ctx context.Context, in IntrospectRequest) (Introspection, error)

Introspect checks an agent JWT and spends its jti, as better-auth does.

func (*Plugin) IssuerURL

func (p *Plugin) IssuerURL() string

IssuerURL is the issuer the plugin serves under: Issuer, or the base URL.

func (*Plugin) ListCapabilities

func (p *Plugin) ListCapabilities(ctx context.Context, in ListCapabilitiesRequest) (CapabilityList, error)

ListCapabilities returns a page of the catalogue.

func (*Plugin) Name

func (p *Plugin) Name() string

Name is PluginName.

func (*Plugin) OperationHooks

func (p *Plugin) OperationHooks() []auth.OperationHook

OperationHooks turns operation results into events: After for each success, Finally for an execution that failed. Nothing without OnEvent.

func (*Plugin) Operations

func (p *Plugin) Operations() []auth.Operation

Operations lists every operation; the self-served ones are wired but mounted only by Handler.

func (*Plugin) OptionErr

func (p *Plugin) OptionErr() error

OptionErr is every error the options recorded, such as auth.ErrNilOption naming an option given nil; auth.New refuses to start while it is not nil (auth.OptionChecker).

func (*Plugin) PendingApprovalsFor

func (p *Plugin) PendingApprovalsFor(ctx context.Context) (PendingApprovals, error)

PendingApprovalsFor lists the signed-in user's pending approval requests, better-auth's /agent/ciba/pending: what the app's approval inbox shows.

func (*Plugin) RateLimits

func (p *Plugin) RateLimits() []auth.RateRule

RateLimits limits EnrollHost per IP.

func (*Plugin) Reactivate

func (p *Plugin) Reactivate(ctx context.Context, in ReactivateRequest) (AgentStatusView, error)

Reactivate brings an expired agent back, better-auth's reactivateAgent, and answers its status in Status's shape. Its capabilities decay to the host's budget: every grant it held is revoked and the budget granted again, so a capability a user approved on top of the budget must be asked for again. An agent that has not expired is answered as it is; a revoked, rejected, claimed or pending one, or one past its AbsoluteLifetime, is refused.

func (*Plugin) Register

func (p *Plugin) Register(ctx context.Context, in RegisterRequest) (RegisterResult, error)

Register registers an agent under the host that signed in.Token.

func (*Plugin) RequestCapability

RequestCapability requests capabilities for the agent that signed in.Token.

func (*Plugin) Revoke

func (p *Plugin) Revoke(ctx context.Context, in RevokeRequest) (RevokeResult, error)

Revoke revokes an agent and every grant it holds.

func (*Plugin) RevokeCapability

RevokeCapability revokes every active or pending grant of the named capabilities, under every constraint scope, better-auth's revokeCapability. Executing a revoked capability then answers ErrGrantRevoked, and the host's budget does not grant it again.

func (*Plugin) RevokeHost

func (p *Plugin) RevokeHost(ctx context.Context, in RevokeHostRequest) (RevokeHostResult, error)

RevokeHost revokes a host and every agent under it, with their grants, better-auth's revokeHost.

func (*Plugin) RotateAgentKey

func (p *Plugin) RotateAgentKey(ctx context.Context, in RotateAgentKeyRequest) (RotateAgentKeyResult, error)

RotateAgentKey replaces an active agent's key, better-auth's rotateKey: the old key stops working at once. An agent that fetched its keys from a JWKS URL has a stored key from then on.

func (*Plugin) RotateHostKey

func (p *Plugin) RotateHostKey(ctx context.Context, in RotateHostKeyRequest) (RotateHostKeyResult, error)

RotateHostKey replaces the key of the host that signed in.Token; the old key stops working at once. better-auth renames the host to the new key's thumbprint; this port keeps the host id, because agents and grants name it, and records the new key's kid instead.

func (*Plugin) Session

func (p *Plugin) Session(ctx context.Context, b Bearer) (AgentSession, error)

Session authenticates an agent JWT and returns the agent's session, better-auth's getAgentSession. Every call spends the token's jti.

func (*Plugin) Status

func (p *Plugin) Status(ctx context.Context, in StatusRequest) (AgentStatusView, error)

Status returns an agent's status. A host polls it while its agent waits for approval.

func (*Plugin) Sweep

func (p *Plugin) Sweep(ctx context.Context, cutoff time.Time) (int64, error)

Sweep deletes spent JWT ids and approval requests that have expired.

func (*Plugin) Tables

func (p *Plugin) Tables() []sqlb.Table

Tables declares the plugin's tables and their aliases.

func (*Plugin) TypedMigrations

func (p *Plugin) TypedMigrations(engine string, t auth.SQLTypes) ([]auth.Migration, error)

TypedMigrations creates the tables for engine. A spent JWT id is a primary key, so a second insert of the same one fails on every engine.

func (*Plugin) UpdateAgent

func (p *Plugin) UpdateAgent(ctx context.Context, in UpdateAgentRequest) (UpdateAgentResult, error)

UpdateAgent updates an agent's name or metadata, better-auth's updateAgent. The metadata is what AgentSession.Agent.Metadata carries to the app's routes.

func (*Plugin) UpdateHost

func (p *Plugin) UpdateHost(ctx context.Context, in UpdateHostRequest) (UpdateHostResult, error)

UpdateHost renames a host, replaces its key or JWKS URL, or replaces its budget. A new key stops the old one at once, as RotateHostKey does.

func (*Plugin) UserTables

func (p *Plugin) UserTables() []auth.UserTable

UserTables names every user column, so DeleteUser removes the user's hosts, agents, the grants they gave and their approval requests.

func (*Plugin) Validate

func (p *Plugin) Validate(*auth.Config) error

Validate checks the issuer, the algorithms, the modes, the lifetimes and the catalogue.

func (*Plugin) VerifyAgentRequest

func (p *Plugin) VerifyAgentRequest(r *http.Request, opts ...authhttp.Option) (AgentSession, error)

VerifyAgentRequest authenticates the agent JWT of a request to one of the app's own routes, such as a capability with a Location: better-auth's verifyAgentRequest. It spends the token's jti. The route checks AgentSession.HasGrant, and enforces the grant's constraints itself.

type PresenceChallenge

type PresenceChallenge struct {
	Ceremony string          `json:"ceremony"`
	Options  json.RawMessage `json:"webauthn_options"`
}

PresenceChallenge is the WebAuthn challenge of an approval.

type PresenceResponse

type PresenceResponse struct {
	Ceremony   string          `json:"ceremony"`
	Credential json.RawMessage `json:"credential"`
}

PresenceResponse answers a PresenceChallenge: its ceremony, and the JSON the browser's PublicKeyCredential serializes to.

type PresenceVerifier

type PresenceVerifier interface {
	// PresenceEnrolled reports whether userID has a passkey to prove
	// presence with.
	PresenceEnrolled(ctx context.Context, userID string) (bool, error)
	// BeginPresence starts an assertion over userID's passkeys with user
	// verification required, and returns its single-use ceremony handle
	// and the options for navigator.credentials.get.
	BeginPresence(ctx context.Context, userID, scope string) (ceremony string, options json.RawMessage, err error)
	// VerifyPresence spends the ceremony and checks the assertion: one of
	// userID's passkeys, begun for scope, with the user verified.
	VerifyPresence(ctx context.Context, userID, scope, ceremony string, credential json.RawMessage) error
}

PresenceVerifier runs the WebAuthn ceremony that proves a person, not an agent driving the browser, approved a request (protocol section 8.11). plugin/passkey's Plugin implements it; the interface keeps the WebAuthn library out of this module. scope binds a ceremony to one approval, so an assertion made for one agent never approves another.

type Primitive

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

Primitive is a JSON string, number or boolean: the values a constraint compares against.

func Bool

func Bool(b bool) Primitive

Bool is a boolean Primitive.

func Number

func Number(n float64) Primitive

Number is a numeric Primitive.

func String

func String(s string) Primitive

String, Number and Bool build Primitives.

func (Primitive) MarshalJSON

func (v Primitive) MarshalJSON() ([]byte, error)

MarshalJSON writes the primitive as its JSON type.

func (*Primitive) UnmarshalJSON

func (v *Primitive) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts a string, a number or a boolean; anything else is not a primitive.

type PublicKey

type PublicKey struct {
	KeyType string `json:"kty"`
	Curve   string `json:"crv,omitempty"`
	X       string `json:"x,omitempty"`
	Y       string `json:"y,omitempty"`
	KeyID   string `json:"kid,omitempty"`
}

PublicKey is a public key in JWK form, as agents and hosts send it: OKP with crv Ed25519 and x, or EC with crv P-256, x and y.

func (PublicKey) Thumbprint

func (k PublicKey) Thumbprint() string

Thumbprint is the key's RFC 7638 thumbprint: the base64url SHA-256 of its required members in lexicographic order.

type ReactivateRequest

type ReactivateRequest struct {
	Bearer
	AgentID string `json:"agent_id"`
}

ReactivateRequest reactivates an expired agent, protocol section 6.6: a host JWT, and the agent of that host.

type RegisterRequest

type RegisterRequest struct {
	Bearer
	Name         string              `json:"name"`
	Capabilities []CapabilityRequest `json:"capabilities,omitempty"`
	Reason       string              `json:"reason,omitempty"`
	// Mode defaults to ModeDelegated.
	Mode     Mode   `json:"mode,omitempty"`
	HostName string `json:"host_name,omitempty"`
	// ForceApproval sends every capability to the user, even those in the
	// host's budget.
	ForceApproval bool `json:"force_approval,omitempty"`
	// ApprovalHints steer the approval when one is needed.
	ApprovalHints
}

RegisterRequest registers an agent, protocol section 6.3. Bearer is a host JWT whose agent_public_key claim is the new agent's key.

type RegisterResult

type RegisterResult struct {
	AgentID string      `json:"agent_id"`
	HostID  string      `json:"host_id"`
	Name    string      `json:"name"`
	Mode    Mode        `json:"mode"`
	Status  AgentStatus `json:"status"`
	Grants  []GrantView `json:"agent_capability_grants"`
	// Approval is set when the agent or a capability waits for a user.
	Approval *Approval `json:"approval,omitempty"`
}

RegisterResult is the registered agent.

type RequestCapabilityRequest

type RequestCapabilityRequest struct {
	Bearer
	Capabilities []CapabilityRequest `json:"capabilities"`
	Reason       string              `json:"reason,omitempty"`
	// ApprovalHints steer the approval when one is needed.
	ApprovalHints
}

RequestCapabilityRequest asks for more capabilities, protocol section 6.4. Bearer is the agent's JWT.

type RequestCapabilityResult

type RequestCapabilityResult struct {
	AgentID  string      `json:"agent_id"`
	Status   AgentStatus `json:"status"`
	Grants   []GrantView `json:"agent_capability_grants"`
	Approval *Approval   `json:"approval,omitempty"`
}

RequestCapabilityResult is the requested grants, and the approval when some wait for a user.

type RevokeCapabilityRequest

type RevokeCapabilityRequest struct {
	Bearer
	AgentID      string   `json:"agent_id,omitempty"`
	Capabilities []string `json:"capabilities"`
}

RevokeCapabilityRequest revokes capabilities of one agent without revoking the agent: by the agent itself (an agent JWT), its host (a host JWT) or the signed-in user who owns it.

type RevokeCapabilityResult

type RevokeCapabilityResult struct {
	AgentID  string   `json:"agent_id"`
	Revoked  []string `json:"revoked"`
	GrantIDs []string `json:"grant_ids"`
}

RevokeCapabilityResult names the capabilities that had a grant to revoke, and the grants revoked. A capability with nothing to revoke is left out, so a repeated call answers empty lists.

type RevokeHostRequest

type RevokeHostRequest struct {
	Bearer
	HostID string `json:"host_id,omitempty"`
}

RevokeHostRequest revokes a host, protocol section 6.10: the host itself with its JWT (HostID optional, and when given it must name that host), or the signed-in user who owns it (HostID required).

type RevokeHostResult

type RevokeHostResult struct {
	HostID        string     `json:"host_id"`
	Status        HostStatus `json:"status"`
	AgentsRevoked int        `json:"agents_revoked"`
}

RevokeHostResult is the revoked host and how many agents it took with it.

type RevokeRequest

type RevokeRequest struct {
	Bearer
	AgentID string `json:"agent_id,omitempty"`
}

RevokeRequest revokes an agent, protocol section 6.6: by the agent itself (an agent JWT, AgentID optional), by its host (a host JWT), or by the signed-in user who owns the agent or its host.

type RevokeResult

type RevokeResult struct {
	AgentID string      `json:"agent_id"`
	Status  AgentStatus `json:"status"`
}

RevokeResult is the revoked agent.

type RotateAgentKeyRequest

type RotateAgentKeyRequest struct {
	Bearer
	AgentID   string    `json:"agent_id"`
	PublicKey PublicKey `json:"public_key"`
}

RotateAgentKeyRequest replaces an agent's key, protocol section 6.8: a host JWT, the agent of that host and its new key.

type RotateAgentKeyResult

type RotateAgentKeyResult struct {
	AgentID string      `json:"agent_id"`
	Status  AgentStatus `json:"status"`
	// HostID is the host that rotated the key, for the agent.key_rotated
	// event; not part of the protocol's answer.
	HostID string `json:"-"`
}

RotateAgentKeyResult is the agent after rotation.

type RotateHostKeyRequest

type RotateHostKeyRequest struct {
	Bearer
	PublicKey PublicKey `json:"public_key"`
}

RotateHostKeyRequest replaces a host's key, protocol section 6.8. Bearer is a host JWT signed by the current key.

type RotateHostKeyResult

type RotateHostKeyResult struct {
	HostID string     `json:"host_id"`
	Status HostStatus `json:"status"`
}

RotateHostKeyResult is the host after rotation.

type SessionAgent

type SessionAgent struct {
	ID               string          `json:"id"`
	Name             string          `json:"name"`
	Mode             Mode            `json:"mode"`
	CapabilityGrants []SessionGrant  `json:"capabilityGrants"`
	HostID           string          `json:"hostId"`
	CreatedAt        time.Time       `json:"createdAt"`
	ActivatedAt      *time.Time      `json:"activatedAt"`
	Metadata         json.RawMessage `json:"metadata"`
}

SessionAgent is the agent of a session.

type SessionGrant

type SessionGrant struct {
	Capability  string      `json:"capability"`
	Constraints Constraints `json:"constraints"`
	GrantedBy   *string     `json:"grantedBy"`
	Status      GrantStatus `json:"status"`
}

SessionGrant is one effective grant of an agent session.

type SessionHost

type SessionHost struct {
	ID     string  `json:"id"`
	UserID *string `json:"userId"`
	Status string  `json:"status"`
}

SessionHost is the host of a session.

type SessionUser

type SessionUser struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

SessionUser is the user an agent acts for.

type StatusRequest

type StatusRequest struct {
	Bearer
	AgentID string `json:"agent_id,omitempty"`
}

StatusRequest asks for an agent's status, protocol section 6.5: with an agent JWT for itself, or a host JWT and the AgentID of one of its agents.

type StrengthFunc

type StrengthFunc func(op OperationInfo) ApprovalStrength

StrengthFunc assigns an approval strength per operation, better-auth's approvalStrength forms: StrengthForAll and StrengthByMethod, or any function.

func StrengthByMethod

func StrengthByMethod(m map[string]ApprovalStrength) StrengthFunc

StrengthByMethod maps HTTP methods to strengths, such as {"GET": StrengthSession, "POST": StrengthWebAuthn}; a method it does not name keeps the default.

func StrengthForAll

func StrengthForAll(s ApprovalStrength) StrengthFunc

StrengthForAll gives every operation s.

type UpdateAgentRequest

type UpdateAgentRequest struct {
	AgentID  string          `json:"agent_id"`
	Name     *string         `json:"name,omitempty"`
	Metadata json.RawMessage `json:"metadata,omitempty"`
}

UpdateAgentRequest renames an agent or replaces its metadata, for the signed-in user who owns it. A nil Name or empty Metadata leaves that field as it is; Metadata is a JSON object whose values are strings, numbers, booleans or null, and {} clears it.

type UpdateAgentResult

type UpdateAgentResult struct {
	AgentID   string          `json:"agent_id"`
	Name      string          `json:"name"`
	Metadata  json.RawMessage `json:"metadata"`
	UpdatedAt time.Time       `json:"updated_at"`
}

UpdateAgentResult is the agent as stored after the update.

type UpdateHostRequest

type UpdateHostRequest struct {
	HostID              string     `json:"host_id"`
	Name                *string    `json:"name,omitempty"`
	PublicKey           *PublicKey `json:"public_key,omitempty"`
	JWKSURL             *string    `json:"jwks_url,omitempty"`
	DefaultCapabilities []string   `json:"default_capabilities,omitempty"`
}

UpdateHostRequest changes a host the signed-in user owns, better-auth's updateHost. A nil field is left as it is. PublicKey and JWKSURL replace how the host signs; at most one of them is given.

type UpdateHostResult

type UpdateHostResult struct {
	HostID              string     `json:"id"`
	Name                *string    `json:"name"`
	DefaultCapabilities []string   `json:"default_capabilities"`
	JWKSURL             *string    `json:"jwks_url"`
	Status              HostStatus `json:"status"`
	UpdatedAt           time.Time  `json:"updated_at"`
}

UpdateHostResult is the host after the update.

Directories

Path Synopsis
Package agentclient is the agent side of the Agent Auth Protocol: a Go program (an agent runtime, a CLI, an MCP server) that holds a host key discovers a provider, registers agents, waits for the user's approval, and executes capabilities with short-lived, request-bound JWTs.
Package agentclient is the agent side of the Agent Auth Protocol: a Go program (an agent runtime, a CLI, an MCP server) that holds a host key discovers a provider, registers agents, waits for the user's approval, and executes capabilities with short-lived, request-bound JWTs.

Jump to

Keyboard shortcuts

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