# auth.md

This document tells agents how to authenticate against the Aard service so
they can call the MCP server at `https://api.aard.ai/mcp` and the REST
endpoints under `https://api.aard.ai/`.

MCP and REST use different credential paths today. MCP clients authenticate
with OAuth 2.1 / OIDC through WorkOS AuthKit. Direct REST callers use Aard API
keys issued from the account dashboard. There is no custom agent-only
registration flow today. See the `agent_auth` block in the discovery documents
below for machine-readable details.

## Discovery

The MCP protected resource URI is `https://api.aard.ai/mcp`. OAuth access
tokens issued for MCP must carry this exact string as the `aud` claim.

1. Fetch RFC 9728 Protected Resource Metadata from one of:
   - `https://api.aard.ai/.well-known/oauth-protected-resource` (canonical, served by the gateway)
   - `https://aard.ai/.well-known/oauth-protected-resource` (website-origin metadata, includes the `agent_auth` extension and points to the MCP docs)
2. Read `authorization_servers[0]` to locate the WorkOS AuthKit-hosted
   authorization server. In production this is
   `https://striking-choice-88.authkit.app`.
3. Fetch RFC 8414 Authorization Server Metadata at
   `${authorization_server}/.well-known/oauth-authorization-server` to learn
   the `authorization_endpoint`, `token_endpoint`, `jwks_uri`, and
   `registration_endpoint`.
4. Read the `agent_auth` block in
   `https://aard.ai/.well-known/oauth-authorization-server` for the
   auth.md extension metadata (skill URL, register URI, supported identity
   and credential types).

The site publishes website-origin discovery at the bare site root because some
agent runtimes probe the website host first. The site PRM is not a
byte-equivalent copy of the API/MCP PRM: its `resource` is `https://aard.ai`
because it is served by `aard.ai`, while the API PRM advertises the MCP
resource `https://api.aard.ai/mcp`.

## Supported flows

### OAuth 2.1 authorization code with PKCE (humans + interactive agents)

The default flow for MCP clients (Claude Desktop, Cursor, Zed, etc.) and
for agents acting on behalf of a signed-in user.

- `response_type=code`, PKCE required.
- `scope=openid profile email offline_access`. No other scopes are accepted
  by AuthKit — passing `mcp:*` or `tool:*` returns `error=invalid_scope`.
- `resource=https://api.aard.ai/mcp` (RFC 8707) — mandatory; the gateway
  exact-matches the `aud` claim on every request.
- Authorization endpoint, token endpoint, and JWKS are served by AuthKit;
  see the RFC 8414 document for the live URLs.
- Tokens are short-lived JWTs signed with RS256 against the AuthKit JWKS.
  Refresh via the standard `refresh_token` grant — there is no Aard-specific
  refresh flow.

### Dynamic Client Registration (RFC 7591)

MCP clients that need to self-register call AuthKit's
`/oauth2/register` endpoint directly. DCR is enabled on production
(`https://striking-choice-88.authkit.app/oauth2/register`) and currently
disabled on staging — see `docs/MCP_OAUTH.md` for the dashboard toggle.

The site also exposes `https://aard.ai/api/oauth/register` as a stub that
returns `501 not_implemented` with a Link header pointing at the manual
setup docs. It is not a registration endpoint; clients should use the
AuthKit endpoint advertised in the AS metadata.

### Agent-attested and OTP-claim flows (not yet supported)

Aard does not currently expose the auth.md ID-JAG, verified-email, or
anonymous-with-claim registration flows. The `agent_auth` block omits
`identity_types_supported` accordingly.

The auth.md-native endpoints exist as placeholders and return
`501 not_implemented` with a Link header pointing back here:

- `POST https://aard.ai/api/agent/auth` — registration
- `POST https://aard.ai/api/agent/auth/claim` — claim ceremony
- `POST https://aard.ai/api/agent/auth/revoke` — revocation

Do not treat these as live. They return `501` so a direct probe finds a
documented dead-end rather than a `404`; the discovery documents do not
advertise them as `register_uri` / `claim_uri` / `revocation_uri`. When the
credential-issuance backend lands we will populate those keys and
`identity_types_supported` in both discovery documents and update this file.

## Using MCP OAuth

Present the access token as a bearer credential in the `Authorization`
header on every request to `https://api.aard.ai/mcp`:

```http
Authorization: Bearer <access_token>
```

The gateway verifies `iss` against `WORKOS_OAUTH_ISSUER` and `aud` against
`https://api.aard.ai/mcp` and rejects anything else with a
`WWW-Authenticate: Bearer` challenge that points back at the PRM document.

MCP authorization (which tools the principal may call, which agencies and
indicators they may query) is enforced server-side from the principal's
plan and entitlements, not from the token's `scope` claim. A
successfully-verified token opens the full v1 MCP tool surface
(`inspect`, `discover`, `ask`, `build_url`) for any authenticated user. See
`docs/MCP_OAUTH.md` for the full rationale.

## Using REST API keys

Direct REST endpoints under `https://api.aard.ai/v1/*` do not accept MCP OAuth
tokens. Create an API key at `https://aard.ai/account` and present it as a
bearer credential:

```http
Authorization: Bearer aard_live_...
```

REST OAuth is not currently supported. If it is added later it will use its own
resource/audience rather than reusing the MCP resource.

## Revocation

Token revocation is handled by the WorkOS authorization server. There is
no Aard-specific revocation endpoint. Sign-out and session revocation in
the WorkOS dashboard invalidate refresh tokens; access tokens expire on
their own short TTL.

## More

- MCP server card: `https://aard.ai/.well-known/mcp-server-card`
- MCP setup guide: `https://aard.ai/docs/mcp-oauth` (HTML setup and troubleshooting docs)
- auth.md skill: `https://isitagentready.com/.well-known/agent-skills/auth-md/SKILL.md`
- auth.md spec: `https://github.com/workos/auth.md`
