# auth.md

DealScore authenticates agents with **OAuth 2.1 (PKCE)** as the default MCP connect path. API keys remain for curl, scripts, and clients that cannot do OAuth. This is not a second account system — the same email magic link used for `/keys` signs you in to consent.

## Audience

MCP clients connecting to DealScore, and programmatic clients calling REST. Live homepage scores require sign-in and share the Free meter.

## MCP OAuth (primary)

1. Add MCP Streamable HTTP at https://cardealscore.com/api/mcp with no pre-shared key.
2. The client GETs `https://cardealscore.com/.well-known/oauth-protected-resource` (RFC 9728) after a 401 `WWW-Authenticate` challenge.
3. Then GETs `https://cardealscore.com/.well-known/oauth-authorization-server` (RFC 8414). OpenID Discovery is also at `https://cardealscore.com/.well-known/openid-configuration`.
4. Register if needed (`POST https://cardealscore.com/oauth/register`, RFC 7591) or use a Client ID Metadata Document URL as `client_id`.
5. Browser opens `https://cardealscore.com/oauth/authorize` — sign in with the email magic link if needed, then Allow.
6. Token exchange: `POST https://cardealscore.com/oauth/token` (authorization code + PKCE S256, then refresh tokens).
7. Call MCP with `Authorization: Bearer <access_token>`. Scope is `mcp` (tools access).

Access tokens are stored hashed (SHA-256), same care as API keys. Prefix `dsoat_` so they cannot be confused with `ds_` API keys.

## API keys (secondary)

Sign in with an email magic link, then create or revoke keys on https://cardealscore.com/keys.

[Sign in to create an API key](https://cardealscore.com/login). Email magic link — no password. Manage keys at https://cardealscore.com/keys. Live scores share a Free meter of 40 scored deals per month. Sign in on the homepage before live scoring. Details: https://cardealscore.com/docs/api#get-a-key

1. Header `x-api-key: YOUR_KEY`
2. Header `Authorization: Bearer YOUR_KEY`

Send one of those on:

- `POST https://cardealscore.com/api/score`
- `POST https://cardealscore.com/api/explain`
- Streamable HTTP https://cardealscore.com/api/mcp (if not using OAuth)

`GET https://cardealscore.com/api/health` is open and does not take a key.

In local development, if `DEALSCORE_API_KEYS` is unset, programmatic routes work without a key.

## Credential use

Use API keys and OAuth access tokens only as HTTP headers. Do not put them in query strings.
