Auth Integration
Summary
Authentication is a service, not a library. Every Aetos application shares a single identity service — the auth-server — and consumes it through its HTTP contract. An app never implements registration, login, password hashing, or token issuance itself.
The agent does not need an MCP bundle for the auth-server. It reads the
pinned OpenAPI contract (below) and declares a bearer security scheme in its own
spec; openapi_first then generates the verifying dependency. No auth code is
written.
Read the contract
The auth-server OpenAPI document is pinned in the standards bundle and served as an MCP resource:
It is the authoritative client contract (OpenAPI 3.1). Read it to learn the endpoints and the request/response schemas:
| Endpoint | Purpose |
|---|---|
POST /register |
Create a user (201, returns PublicUser) |
POST /login |
Authenticate and issue a JWT (LoginResponse) |
GET /me |
Current user's PublicUser (bearer required) |
POST /logout |
Stateless logout (LogoutResponse) |
POST /introspect |
Internal token introspection (active, user) |
The pinned snapshot is versioned (currently 0.0.5). If the caller has a live
auth-server and needs the current contract, they can pass the full endpoint
({AUTH_SERVER}/openapi.json) and read it directly instead of the pin.
Wire it up: declare, don't code
Authentication is added by editing the app's own openapi.yaml — nothing
else. Declare a global bearer requirement and point the scheme at the auth
server (values come from the environment):
openapi_first reads securitySchemes + security and generates a FastAPI
dependency that extracts the bearer token, POSTs {"token": <jwt>} to
{AUTH_SERVER}/introspect, and returns the user when active == true. It
raises 401 on an invalid or inactive token and 503 when the auth server is
unreachable. The generated handlers receive the authenticated user — you author
no authentication code.
Public operations
A route that must stay open opts out by overriding security with an empty
list:
Environment
Path 1: jwtlib client (no generator)
Apps that do not use a generated dependency verify requests with the py-jwt
client:
authenticate_request POSTs to {JWT_SERVER}/introspect (3 second timeout) and
treats {"active": true, "user": {...}} as valid. Use this for seeding scripts
and tests (jwtlib.register_user, jwtlib.login_user).
Golden rules
- Never trust an unverified token. Decode through the generated dependency
or
jwtlibbefore handling the request. - Treat
active: falseas unauthenticated, even if the JWT decodes. - Do not implement your own JWT parsing. Revocation and issuer changes must stay centralized in the auth-server.
- Do not add auth until the app's outcome is validated. Ship the app authless first; layer the security scheme once the core behaviour works.
Notes
- The default template is authless on purpose. Add the
securitySchemesblock above only when auth is required. Pull the shared slice withget_app_component("auth"). - The auth-server is deployed as a service (no importable package); the client
surface is the spec, plus
jwtlibfor the low-level path.