Authorizing requests
adapter-core ships an auth entry point that answers two questions for an agent that serves its own HTTP surface: who is calling, and may they use this agent? Mount one middleware and every request is identified from the platform’s sign-in headers, then checked against the agent’s access list before it reaches your routes.
Auth is a separate entry point from the messaging one, so an agent that serves a UI never loads the messaging stack and an agent that calls serve() never loads this one.
When you need it
Use it when your agent receives HTTP requests directly: a frontend agent, or a custom server you serve alongside messaging. A messaging agent on the web or slack adapter needs nothing, because the platform runs the same check before your code sees a message.
This is the packaged form of Manually authorize requests. Read that guide when you need the raw contract, such as for a language the SDK does not cover.
Install
Node.js
Python
Guard every request
Construct an Authorizer once, then mount the binding for your framework. Zero-argument construction works in a deployed agent, because every input comes from the environment the platform injects.
Node.js
Python
One binding covers each way an agent is commonly served. All of them run the same check and return the same status codes.
Both Python middlewares default to constructing their own Authorizer, so app.add_middleware(AstroAuthMiddleware) needs no argument. Pass one when you want to share an instance or override its configuration.
In TypeScript, Express and Fastify know nothing about astroPrincipal, so declare it on your framework’s request type to read it without a cast. Hono and withAuth hand you the principal directly and need no declaration.
What the guard answers
For every request, the guard identifies the caller, authorizes that identity against the agent’s access list, and maps the result onto a status code.
A denial returns a JSON body of the form {"error": "Forbidden"} and never reaches your route.
The principal
A request that carries no identity yields no principal. That is not a refusal: the guard sends it on as an anonymous caller, because the access list is what decides whether an open surface admits it.
Access lists
The SDK authorizes against the custom adapter by default, so it checks the access list of the interface your agent serves itself, not the built-in chat’s list. Set that list on the agent’s Configure screen. Who can talk to an agent covers the entry types and how they combine.
Two behaviors are worth knowing before you mount the middleware:
- An interface with no entries at all admits members of the owning account. Adding any entry turns that fallback off, and the list becomes the only way in.
- Anyone with an Astropods account keeps sign-in at the front door and admits every signed-in user, which is the open setting that works with the guard.
Public removes sign-in, so no request reaching your agent carries an identity, and it stores no entries on the access list. Every request then authorizes as anonymous with nothing to match, and the guard answers 401. To serve a public surface, either add an anyone entry to the list or do not mount the guard on it.
Identity verification
The platform’s sign-in layer injects two headers. x-amzn-oidc-identity carries the user id as a bare string. x-amzn-oidc-data carries the same identity as a signed JWT, along with the rest of the claims.
The SDK verifies x-amzn-oidc-data by default: it reads kid and signer from the token header, fetches the ES256 public key for that region, caches it by kid, and checks expiry. A header that fails verification is treated as no identity at all, so the request continues as anonymous and the access list decides.
Set verifyIdentity: false (verify_identity=False in Python) to skip verification and trust x-amzn-oidc-identity as sent. Do that only when nothing can reach your agent except through the platform’s front door. The principal then carries no email or name, because those come from the verified claims, and the SDK logs a warning at startup.
Caching, timeouts, and outages
Because decisions are cached, an access-list edit takes up to one cache window to reach a running agent.
Local development
ast project start does not inject the deploy token, so the Authorizer starts in dev mode: it allows every request and logs a warning naming the reason. Set ASTRO_AUTH_DEV_USER_ID to have identify return a fixed principal, which is enough to exercise the code paths that read the caller.
A deployed agent never falls into dev mode quietly. A token that is present but unreadable fails construction at startup instead of downgrading to allow-all.
Configuration
Every option has an environment source, and a constructor argument overrides it.
Lower-level pieces
Call the two halves yourself when a binding does not fit, or when you want the identity without enforcing the list:
Node.js
Python
guard(authz, headers) runs the sequence and returns the status code a binding would have sent, which is the piece to reuse when wiring a framework the SDK does not cover. Below that sit AlbIdentityVerifier, AuthorizeClient, DecisionCache, and decodeDeployToken for supplying your own identity or transport.
Next steps
- Serve a frontend from your agent: the interface this check protects
- Access control: the entry types an access list accepts
- Manually authorize requests: the underlying contract, for a language the SDK does not cover