# Authenticating to Guidon

Guidon's MCP server (`https://useguidon.com/api/mcp`) and REST API (`https://useguidon.com/api/v1`) accept a bearer token: `Authorization: Bearer <token>`. There are two ways to get one. Both act as a Guidon user - only in the projects that user can access, with their role, limited to the token's scopes.

1. **OAuth 2.1** (for MCP clients and apps acting for someone): the user approves your app in their browser; you get a one-hour access token and a refresh token. Claude, ChatGPT, Cursor, VS Code and other MCP clients do this on their own once given `https://useguidon.com/api/mcp`.
2. **A personal API key** (for scripts, CI and the user's own agents): the user creates one in [Profile → API keys](https://useguidon.com/profile) and gives it to you. It doesn't expire until revoked.

## Discover

Call the API without a token. The `401` carries the protected resource metadata (RFC 9728):

```
WWW-Authenticate: Bearer resource_metadata="https://useguidon.com/.well-known/oauth-protected-resource/api/mcp"
```

That document names the authorization server (`authorization_servers: ["https://useguidon.com"]`); its metadata (RFC 8414) is at [https://useguidon.com/.well-known/oauth-authorization-server](https://useguidon.com/.well-known/oauth-authorization-server).

## Pick a method

- Acting for a person through a client they use (an MCP client, an assistant): **OAuth 2.1 authorization code with PKCE**.
- Running unattended for the user who set you up: **their API key**. There is no client-credentials grant and no anonymous access - every token belongs to a person.

## Register

Public clients only - no client secret; PKCE protects the code.

- **Client ID Metadata Document** (preferred): use an https URL that serves your client metadata as your `client_id` (`client_id_metadata_document_supported: true`). It must contain `client_id` (that same URL), `client_name` and `redirect_uris`.
- **Dynamic Client Registration** (RFC 7591): `POST https://useguidon.com/oauth/register` with JSON `{ "client_name": "...", "redirect_uris": ["https://..."] }` -> `201` with your `client_id`.

Redirect URIs: `https://`, `http://localhost` / `127.0.0.1` (any port at run time), or your app's own scheme.

## Authorize

Send the user's browser to:

```
https://useguidon.com/oauth/authorize?response_type=code&client_id=...&redirect_uri=...
  &code_challenge=<BASE64URL(SHA256(verifier))>&code_challenge_method=S256
  &scope=tasks:read%20tasks:write&state=...&resource=https://useguidon.com/api/mcp
```

They sign in to Guidon (if needed) and approve. You get `code`, `state` and `iss` (always `https://useguidon.com`) on the redirect URI. Codes last 5 minutes and work once.

## Exchange

```
POST https://useguidon.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&redirect_uri=...&client_id=...&code_verifier=...&resource=https://useguidon.com/api/mcp
```

The answer: `{ "access_token": "guidon_...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "gdr_...", "scope": "..." }`.

Refresh before or after the hour with `grant_type=refresh_token&refresh_token=...&client_id=...`. Refresh tokens rotate - every refresh returns a new one and the old one stops working; presenting a used one revokes the whole authorization. They last 90 days.

## Use the access_token

`Authorization: Bearer <access_token>` on `https://useguidon.com/api/mcp` (MCP Streamable HTTP) or `https://useguidon.com/api/v1/...` (REST, see [OpenAPI](https://useguidon.com/openapi.json)). Never in a query string.

| Scope | Allows |
|---|---|
| `tasks:read` | See your projects, tasks, comments and attempts |
| `tasks:write` | Create, edit and delete tasks |
| `tasks:status` | Start tasks and move them between columns |
| `comments:write` | Comment on tasks |
| `attempts:write` | Record what was tried on a task |
| `project:read` | Read decisions, knowledge, roadmap, memory and tech stack |
| `project:write` | Change decisions, knowledge, roadmap, memory and tech stack |

Leaving `scope` out asks for all of them.

## Errors

| Where | Error | Meaning |
|---|---|---|
| API / MCP | `401` + `WWW-Authenticate` | No token, expired or revoked - refresh, or start over at Discover |
| API / MCP | `403` `forbidden` | The token lacks the scope, or the user lacks the permission |
| /oauth/authorize | `access_denied` | The user said no |
| /oauth/authorize | `invalid_request` | PKCE missing (`code_challenge` + `S256` are required) |
| /oauth/authorize | `invalid_scope` / `invalid_target` | Unknown scope / `resource` isn't this server |
| /oauth/token | `invalid_grant` | Code or refresh token invalid, expired, used, or the user disconnected the app |
| /oauth/register | `invalid_redirect_uri` | A redirect URI that isn't https, loopback http or an app scheme |

## Revocation

- You: `POST https://useguidon.com/oauth/revoke` with `token=<refresh or access token>` (RFC 7009). A refresh token ends the whole authorization.
- The user: Profile → Connected apps → Disconnect, at any time. Access tokens stop working immediately.
- API keys: revoked by the user in Profile → API keys.
