# auth.md

**Authentication & access for AI agents on getuniqcli.com.**

> The public catalog surface (product search, product detail, live price & stock) is
> **open and unauthenticated** — no key, token, or sign-in required. Add the MCP server
> or call the REST/OpenAPI endpoints directly. Everything below the catalog (placing
> orders, quotes inside the portal, order history, saved carts, payment) is a **human,
> signed-in portal** and is intentionally NOT available to third-party agents today.

## What agents CAN do without auth
- Search the catalog: `GET https://getuniqcli.com/api/public/products?q=...`
- Get a product: `GET https://getuniqcli.com/api/public/products/{sku}`
- MCP server (Streamable HTTP): `https://getuniqcli.com/api/mcp`
- Machine-readable API description: `https://getuniqcli.com/openapi.json`

These return **buyer-safe fields only** — sell price, stock status, specs, images. No
distributor part numbers, cost, or margin is ever exposed.

## Rate limits & raising your tier
The catalog surface is open at an **anonymous per-IP rate limit** — roughly 60 requests/
minute on the MCP server, 120 requests/minute on the REST API, and about 2,000 requests/
day. Exceeding it returns **HTTP 429** with a `Retry-After` header; back off and retry.
If you run a bulk or scheduled workload and need higher limits, raise your tier with EITHER
of two credential rails — both mediated (human-approved), both **rate-tier only** (never an
identity, never ordering/quotes/account access):
1. **Opaque `uqk_` key.** Request one (form at https://getuniqcli.com/ai#api-keys or email
   api@getuniqcli.com); a person mints it by hand and emails it. Send it as
   `Authorization: Bearer uqk_…`.
2. **OAuth 2.0 client-credentials access token** (see the next section). Send it as
   `Authorization: Bearer <jwt>`.

## OAuth 2.0 (limited)
We run a **deliberately minimal** OAuth 2.0 authorization server whose ONLY purpose is to
raise your catalog rate tier. It is honest about its narrow shape:
- **Grant:** `client_credentials` only. No authorization endpoint, no user login, no
  refresh tokens, no PKCE — there is no user to authorize.
- **Scope:** `catalog:read` only — the same open read surface (MCP + REST catalog). A token
  unlocks **nothing** transactional.
- **Access token:** a short-lived (**10-minute**) **ES256 JWT**. **A token raises your rate
  tier and is NEVER an identity** — it is not accepted by the portal, carries no account, and
  authorizes no order/quote/invoice/cart/pricing data. Verification is signature-only against
  our public JWKS; an invalid or expired token silently drops you to the anonymous tier
  (never rejected outright).
- **Client registration: mediated, not self-serve.** `POST https://getuniqcli.com/oauth/register`
  (RFC 7591-shaped) files a **PENDING** request and returns **202 with NO credential** — a
  person reviews it and, on approval, mints your `client_id`/`client_secret` by hand. No
  secret is ever issued by the registration endpoint.
- **Token endpoint:** `POST https://getuniqcli.com/oauth/token` with `grant_type=client_credentials`
  and `scope=catalog:read`. Authenticate with `client_secret_basic` or
  `client_secret_post`. Returns `{ "access_token": "…", "token_type": "Bearer",
  "expires_in": 600, "scope": "catalog:read" }`.
- **Keys:** `GET https://getuniqcli.com/oauth/jwks.json` (public EC P-256 verification keys).
- **Metadata:** `GET https://getuniqcli.com/.well-known/oauth-authorization-server` (RFC 8414).
- **Revocation.** Email api@getuniqcli.com to revoke a client. Revocation stops NEW tokens
  immediately; any outstanding token is stateless and ages out within its **≤10-minute** TTL,
  during which the worst a revoked client gets is its old rate tier on the open read surface.

### Machine-readable discovery
```json
{
  "issuer": "https://getuniqcli.com",
  "token_endpoint": "https://getuniqcli.com/oauth/token",
  "jwks_uri": "https://getuniqcli.com/oauth/jwks.json",
  "registration_endpoint": "https://getuniqcli.com/oauth/register",
  "grant_types_supported": ["client_credentials"],
  "scopes_supported": ["catalog:read"],
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
  "agent_auth": {
    "register_uri": "https://getuniqcli.com/oauth/register",
    "registration_mode": "mediated_pending_approval",
    "credential_types": ["oauth2_client_credentials", "api_key"],
    "token_use": "rate-tier envelope only — never identity, never portal access",
    "revocation_contact": "api@getuniqcli.com"
  }
}
```

## What requires a human sign-in (NOT an agent capability)
Placing orders, requesting a formal quote inside the portal, viewing order status,
invoices, saved carts, and payment are gated behind Microsoft Entra External ID sign-in
on https://getuniqcli.com/portal. Authorization is decided by our portal directory (role + approval
status), not by identity-provider claims — **and never by an API key or OAuth token** (either
one only lifts catalog rate limits). An agent that needs to transact on a buyer's behalf
should hand the buyer a link:
- Request a quote: https://getuniqcli.com/get-a-quote
- Contact a rep: connect@getuniqcli.com

Microsoft Entra External ID signs humans into the portal; it is not an agent-auth API, is
entirely separate from the OAuth AS above, and is not exposed here. See https://getuniqcli.com/ai for
how to add the read-only connector.
