
Security News
White House Authorizes Private Companies to Conduct Offensive Cyber Operations
A new federal program will let vetted U.S. cybersecurity firms help investigate and disrupt foreign cybercrime groups under government direction.
@aauth/agent
Advanced tools
Agent-side AAuth protocol library — HTTP Signatures, person tokens, token exchange, deferred polling
The agent-side AAuth protocol library. Signs HTTP requests, obtains person tokens, handles AAuth challenge-response flows, exchanges resource tokens for auth tokens at the person server, and polls 202 deferred responses.
Renamed from @aauth/mcp-agent: the package contains no MCP and never did. Its only runtime dependencies are @aauth/protocol and @hellocoop/httpsig.
Part of aauth-dev/packages-js. Protocol spec: dickhardt/AAuth.
npm install @aauth/agent
createAAuthFetch(options): FetchLikeCreates a protocol-aware fetch that handles the full AAuth flow: signs requests, obtains a person token when a resource challenges with requirement=person-token, parses 401 AAuth-Requirement challenges, exchanges resource tokens with the person server, caches auth tokens, handles AAuth-Access session tokens, and retries.
import { createAAuthFetch } from '@aauth/agent'
const fetch = createAAuthFetch({
getKeyMaterial: async () => ({
signingKey: privateKeyJwk,
signatureKey: { type: 'jwt', jwt: agentToken }
}),
// Person server — the `ps` claim of the agent token.
authServerUrl: 'https://ps.example',
// Optional: declare protocol capabilities
capabilities: ['interaction', 'clarification'],
// Optional: the mission the agent is operating under, as the base64url
// SHA-256 of the approved mission blob. Forwarded when a person token is
// requested; it then flows person token → resource token → auth token.
missionS256: 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
// Optional callbacks
onInteraction: (url, code) => {
console.log(`Visit ${url}?code=${code}`)
},
onClarification: async (question) => {
return prompt(question)
},
// Optional hints for the person server
justification: 'Read project files',
loginHint: 'user@example.com',
tenant: 'acme.com',
domainHint: 'acme.com',
})
const response = await fetch('https://resource.example/api')
There is no AAuth-Mission header in protocol -11 — it and its IANA registration were removed. A mission reaches a resource only inside a PS-issued token, as the mission_s256 claim.
requestPersonToken(options): Promise<PersonTokenResult>Requests a person token from the PS's person_token_endpoint. A person token identifies the person the agent acts for to one resource. A resource MUST have verified one before it issues a resource token, and the agent MUST present one on every authorization endpoint request.
import { requestPersonToken } from '@aauth/agent'
const { personToken, expiresIn } = await requestPersonToken({
signedFetch: psSignedFetch, // createSignedFetch(..., { signBody: true })
personServerUrl: 'https://ps.example',
resource: 'https://resource.example',
missionS256: '...', // optional
subagentToken: '...', // optional — parent requesting for a sub-agent
onInteraction: (url, code) => { /* the PS may ask the person first */ },
})
The request is a signed POST presenting the agent token via Signature-Key: sig=jwt;jwt="…", with body {resource, mission_s256?, subagent_token?}. A 202 with requirement=interaction is polled at its Location like any other deferred response. upstream_token (call chaining) is not implemented.
Present the token in place of the agent token:
Signature-Key: sig=jwt;jwt="<person token>"
createPersonTokenCache(options): PersonTokenCacheCaches person tokens per (resource, mission_s256) — a person token is scoped to one resource and, when it carries mission_s256, to one mission.
import { createPersonTokenCache } from '@aauth/agent'
const personTokens = createPersonTokenCache({
signedFetch: psSignedFetch,
personServerUrl: 'https://ps.example',
})
const token = await personTokens.get('https://resource.example', missionS256)
// One rotation of the agent's signing key invalidates every cached token at
// once — they all bind that key through `cnf`. Flush and re-request lazily.
personTokens.clear()
set(resource, missionS256, token, expiresIn) seeds a token obtained elsewhere, such as the person_tokens map a PS returns with a mission approval.
createSignedFetch(getKeyMaterial, options?): FetchLikeCreates a fetch that signs requests with HTTP Message Signatures but does not handle AAuth challenges. Use this when you only need request signing.
import { createSignedFetch } from '@aauth/agent'
const signedFetch = createSignedFetch(async () => ({
signingKey: privateKeyJwk,
signatureKey: { type: 'hwk' }
}), {
capabilities: ['interaction'],
})
// For PS and AS endpoints: a request carrying a body additionally signs
// `content-digest` and `content-type`.
const psSignedFetch = createSignedFetch(getKeyMaterial, { signBody: true })
Set signBody only for PS and AS endpoints. Resources declare what they need through additional_signature_components in their metadata, so a blanket body mandate toward a resource would be wrong.
exchangeToken(options): Promise<TokenExchangeResult>Exchanges a resource token for an auth token at the person server. Handles metadata discovery (/.well-known/aauth-person.json), 202 deferred responses, and interaction polling.
import { exchangeToken } from '@aauth/agent'
const { authToken, expiresIn } = await exchangeToken({
signedFetch: psSignedFetch,
authServerUrl: 'https://ps.example',
resourceToken: '...',
justification: 'Read project files',
})
The auth token request has no mission parameter — the mission reaches the PS inside the resource token, which copied it from the person token.
fetchAuthServerMetadata(options) / resolveAuthServerMetadata(options)Fetches and validates /.well-known/aauth-person.json. Both auth_token_endpoint (renamed from token_endpoint in -11) and person_token_endpoint (new in -11) are REQUIRED; a person server publishing neither cannot complete a flow, and the document is rejected. resolveAuthServerMetadata returns a caller-supplied cached copy when there is one.
pollDeferred(options): Promise<DeferredResult>Polls a 202 Location URL until a terminal response. Handles Retry-After, Prefer: wait, clarification chat, and interaction codes.
import { pollDeferred } from '@aauth/agent'
const { response, error } = await pollDeferred({
signedFetch,
locationUrl: 'https://ps.example/pending/abc123',
interactionCode: 'ABCD1234',
onInteraction: (url, code) => { /* show to user */ },
maxPollDuration: 900, // seconds, default 900
})
Header parsing (parseRequirementHeader, buildCapabilitiesHeader, …), access_mode planning, token typ and dwk constants, and JWT decoding live in @aauth/protocol. This package consumes them and defines none of them.
All signing functions take a GetKeyMaterial callback. This decouples key management from the protocol — you provide keys however you want:
type GetKeyMaterial = () => Promise<{
signingKey: JsonWebKey // Ed25519 private key for HTTP signatures
signatureKey:
| { type: 'jwt', jwt: string } // agent, person, or auth token
| { type: 'hwk' } // bare public key (pseudonym)
}>
For local development, use @aauth/local-keys to provide this callback from the OS keychain.
MIT
FAQs
Agent-side AAuth protocol library — HTTP Signatures, person tokens, token exchange, deferred polling
We found that @aauth/agent demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
A new federal program will let vetted U.S. cybersecurity firms help investigate and disrupt foreign cybercrime groups under government direction.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.