> ## Documentation Index
> Fetch the complete documentation index at: https://docs.skinai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Guest capability tokens, and OAuth 2.1 for the account endpoint.

## Guest: `/mcp`

No credential is needed to connect. Each scan is authorised by its own capability:

* `start_skin_scan` returns a `run_id` and a 64-hex `access_token`.
* Every later call on that scan must send both.
* The server stores only a keyed hash of the token.
* A wrong token and a missing scan both return `not_found`, so a `run_id` cannot be probed.
* `delete_skin_scan` revokes the token immediately. A guest scan's token stops working after seven days.

The token is the only authorisation, so a scan started by one client can be read by another that holds the token. Treat it as a secret: don't log it or show it to the person.

**The upload link** (`/scan/:id`) is opened by the person, not the agent. The page runs Cloudflare Turnstile, records consent, and sets a `__Host-` cookie. The upload is accepted only from the browser that consented, so an agent holding the link cannot swap the photo.

## Account: `/mcp/account`

OAuth 2.1 with PKCE (`S256` only), dynamic client registration and refresh tokens. Without a valid bearer token the endpoint returns `401`.

| Item                          | Value                                               |
| ----------------------------- | --------------------------------------------------- |
| Authorization server metadata | `/.well-known/oauth-authorization-server`           |
| Registration                  | `POST /register` (RFC 7591)                         |
| Authorization                 | `GET /authorize`                                    |
| Token, refresh and revocation | `POST /token`                                       |
| Grant types                   | `authorization_code`, `refresh_token`               |
| Client auth                   | `none`, `client_secret_basic`, `client_secret_post` |
| Implicit flow, plain PKCE     | Disabled                                            |

**Scopes**

| Scope          | Grants                                   |
| -------------- | ---------------------------------------- |
| `scan:write`   | Start scans and save them to the account |
| `scan:read`    | Read the account's scans and scores      |
| `catalog:read` | Product and ingredient tools             |

At `/authorize` the person signs in with **Google or Apple** through Skan's Firebase project, which is the same identity the Skan app uses. Anonymous identities are refused. They then approve the listed scopes. Redirect URIs must match the registered value exactly; a mismatch or an unknown client gets a `400` page and is never redirected.

Signed-in calls carry a verified account identity. Limits apply per account instead of per network, and `delete_my_skan_data` becomes available.

## Saving a guest result

A completed guest scan returns a `save_url`. The person opens it, signs in with Google or Apple and explicitly chooses to save; that is the only way a guest scan joins an account. No tool saves a result or creates an account.
