Developer-friendly · Headless · Apache 2.0
OAuth 2.1 and OpenID Connect, run by you
A standards-first authorization server for humans, services and AI agents, from a side project to a national ID rollout. Headless by design: build your own sign-in screens on the Interaction API (OpenAPI 3.1, typed client), or use the reference pages. PKCE everywhere, RFC 8693 delegation, the MCP authorization profile and hybrid ML-DSA-65 signing behind a flag. TypeScript on Fastify, with PostgreSQL and Redis.
# Needs Docker 23+ with Compose 2, and OpenSSL
❯ git clone https://github.com/qauth-labs/qauth.git && cd qauth
❯ cp .env.docker.example .env
# add JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, KEK and DB_PASSWORD to .env
❯ docker compose up -d
❯ curl http://localhost:3000/ready
How to generate each value: Docker guide.
What ships today
Capabilities
The full list, and what each switch turns on, is on the status page. Off by default: WALLET_FEDERATION_ENABLED, HYBRID_SIGNING_ENABLED, ID_JAG_ENABLED (enterprise-managed authorization) and DPOP_ENABLED (RFC 9449).
Agent-native authorization
An agent client type, RFC 8693 on-behalf-of token exchange with an act claim, ReadOnly / Admin / Exec scope modes capped per client, step-up before dangerous operations, and an audit trail per agent. For any agent calling any API, not just MCP.
MCP authorization profile
Revision 2026-07-28: RFC 9728 protected-resource metadata, Client ID Metadata Documents with RFC 7591 as the fallback, audience binding and step-up challenges. @qauth-labs/resource-guard handles the resource-server side.
OAuth 2.1 and OpenID Connect core
Authorization code with mandatory PKCE, client_credentials, rotating refresh tokens, discovery, JWKS, ID tokens, userinfo, introspection, revocation, RFC 8707 resource indicators, and RP-initiated and back-channel logout.
Post-quantum hybrid signing
Ed25519 by default. One flag adds hybrid ML-DSA-65 + Ed25519 signatures and a mixed AKP+OKP JWKS, with the post-quantum component served through introspection.
Self-hosted, multi-tenant
Realms with their own issuer and hosts, a developer portal for clients and API keys, and encrypted signing keys. Structured logs, Prometheus metrics and OpenTelemetry events go to your monitoring; nothing is sent to us.
Federation
Sign-in through a realm's upstream OpenID Connect providers today. EU Digital Identity Wallet sign-in over OID4VP 1.0 with SD-JWT VC ships behind a flag; it is tested against a mock wallet, and the real-wallet pass is still open.
Core protocol
One auth server, every kind of client
Before an agent ever delegates anything, QAuth is an OAuth 2.1 and OpenID Connect server: authorization code with mandatory PKCE (S256) for your users, client_credentials for your services, rotating refresh tokens that revoke their family on replay, discovery, JWKS, userinfo, introspection and revocation. No implicit flow and no proprietary SDK required. Agent delegation and MCP support sit on this same core.
Agent authorization
Agents act. Your auth server answers for them.
Agent auth isn't user auth with extra steps. A token has to say "agent X acting for user Y", so RFC 8693 token exchange mints a delegated token with an act claim. Agents can't file a ticket for a client ID either, so they register at machine speed through Client ID Metadata Documents, with RFC 7591 as the fallback. A development agent never reaches production: the environment is a policy dimension. All of it ships today and works the same in front of an MCP server or a plain REST API.
Integrate
Auth that gets outof your way
Protect your resource servers, MCP or plain REST, with standards-based OAuth 2.1: RFC 9728 discovery, client registration by metadata document, and PKCE. The SDK is optional; any OAuth library works.
Run QAuth
Docker Compose builds the images, runs the migrations and starts the auth server with PostgreSQL 18 and Redis 7. The quickstart above has the commands.
Protect a resource server
Register @qauth-labs/resource-guard on an MCP server, or verify bearer tokens against the JWKS yourself on a plain REST API. The tokens are audience-bound either way.
Let clients sign in
MCP clients such as Claude Code discover your authorization server, register with a Client ID Metadata Document and run authorization_code with PKCE. Services use client_credentials instead.
import Fastify from 'fastify'
import { resourceGuardPlugin } from '@qauth-labs/resource-guard/fastify'
const app = Fastify()
// Protect your MCP resource server: audience-bound tokens only
await app.register(resourceGuardPlugin, {
resource: 'https://mcp.yourapp.com',
authorizationServer: 'https://auth.yourapp.com',
requiredScopes: ['mcp:read'],
})
app.get('/mcp', { preHandler: app.requireBearer }, async (request) => {
// request.tokenClaims: verified signature, issuer, audience, scope
return { tools: listTools() }
})
// No token → 401 Unauthorized
// WWW-Authenticate: Bearer resource_metadata=
// "https://mcp.yourapp.com/.well-known/oauth-protected-resource"
// The MCP client (e.g. Claude Code) discovers your auth server from
// that document, registers via CIMD or RFC 7591, then runs
// authorization_code + PKCE for an audience-bound access token.
// Authorization: Bearer <token> → resource-guard verifies it → 200 OK
# Plain OAuth 2.1: no MCP, no agent, just your service
❯ curl -X POST https://auth.yourapp.com/oauth/token -d grant_type=client_credentials -d scope=documents:read
Post-quantum
Hybrid ML-DSA-65 signing ships today
Signing sits behind algorithm-agnostic interfaces. Set SIGNING_ALGORITHM_MODE=ed25519+ml-dsa-65 and HYBRID_SIGNING_ENABLED=true and QAuth signs with ML-DSA-65 next to Ed25519, publishes a mixed JWKS and serves the post-quantum component through introspection. Verifiers that only know Ed25519 keep working.