# Authentication — NORTH28 STUDIO

> The North28 MCP server at `POST /api/mcp` is **publicly accessible — no credentials required** for any current tool. This document is the canonical auth reference for agents, describing discovery, registration, token acquisition, usage, errors, and revocation so the full RFC 9728 / agent-auth flow can be followed.

---

## Public REST API (no credentials)

These read-only endpoints are **publicly accessible without any token**:

| Method | Path | Description |
|---|---|---|
| GET | `/api/studio` | Studio identity (JSON) |
| GET | `/api/services` | Service catalogue (JSON) |
| GET | `/api/pages` | Public pages list (JSON) |
| POST | `/api/batch` | Batch fetch (max 10 paths) |

```sh
curl https://north28.studio/api/studio
curl https://north28.studio/api/services
```

Markdown twins: `/api/studio.md`, `/api/services.md`, `/api/pages.md`. Full reference: https://north28.studio/docs and https://north28.studio/openapi.json.

Portfolio content is **not** in these endpoints; browse https://north28.studio/works. Internal WordPress REST is not public on north28.studio.

---

## Pick a Method

```
Are you an AI agent or automated client?
│
├─ Yes → Do you need write access or private data?
│         ├─ No  → USE PUBLIC ACCESS (no token needed) ──► §Use-Credential
│         └─ Yes → REGISTER a client ──────────────────► §Register → §Claim → §Use-Credential
│
└─ No  → Same decision applies to human-operated API clients.
```

---

## 1 · Discover

Fetch the Protected Resource Metadata (RFC 9728) to find all auth endpoints:

```http
GET /.well-known/oauth-protected-resource HTTP/1.1
Host: north28.studio
Accept: application/json
```

Response:

```json
{
  "resource": "https://north28.studio/api/mcp",
  "authorization_servers": ["https://north28.studio"],
  "scopes_supported": ["mcp:read"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://north28.studio/auth.md"
}
```

From `authorization_servers[0]`, fetch the Authorization Server metadata (RFC 8414):

```http
GET /.well-known/oauth-authorization-server HTTP/1.1
Host: north28.studio
Accept: application/json
```

Response includes `token_endpoint`, `registration_endpoint`, `revocation_endpoint`, `jwks_uri`, `scopes_supported`, and an `agent_auth` block (`register_uri`, `claim_uri`, `revocation_uri`, `identity_types_supported`). This is the complete discovery chain.

---

## 2 · Register

Agents that require private or write access must register a client. Send a dynamic client registration request (RFC 7591):

```http
POST /oauth/register HTTP/1.1
Host: north28.studio
Content-Type: application/json

{
  "client_name": "my-agent",
  "grant_types": ["client_credentials"],
  "token_endpoint_auth_method": "none",
  "scope": "mcp:read",
  "agent_name": "my-agent",
  "agent_version": "1.0.0",
  "agent_description": "Reads NORTH28 STUDIO data for summarisation."
}
```

The `agent_name`, `agent_version`, and `agent_description` fields are the **agent_auth extension** metadata. On success the server returns a `client_id` (and optionally `client_secret`) used in §Claim.

> **Note:** For the current read-only public MCP tools, registration is not required. Skip directly to §Use-Credential.

### agent_auth Extension Block

```json
{
  "agent_auth": {
    "skill": "https://north28.studio/auth.md",
    "register_uri": "https://north28.studio/oauth/register",
    "claim_uri": "https://north28.studio/oauth/token",
    "revocation_uri": "https://north28.studio/oauth/revoke",
    "identity_types_supported": ["anonymous"],
    "anonymous": {
      "credential_types_supported": ["access_token"]
    },
    "agent_metadata_fields": ["agent_name", "agent_version", "agent_description"]
  }
}
```

This block is also published in `/.well-known/oauth-authorization-server` under the `agent_auth` key.

---

## 3 · Claim (Token Acquisition)

Exchange your `client_id` for a Bearer token using the `client_credentials` grant:

```http
POST /oauth/token HTTP/1.1
Host: north28.studio
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=<your-client-id>&scope=mcp%3Aread
```

Successful response:

```json
{
  "access_token": "<token>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "mcp:read"
}
```

Store the `access_token`; it expires after `expires_in` seconds. Request a new token before expiry.

---

## 4 · Use-Credential

### Public access (no token)

All current MCP tools are public. Send a JSON-RPC request with no `Authorization` header:

```http
POST /api/mcp HTTP/1.1
Host: north28.studio
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "get_studio_info", "arguments": {} }
}
```

### With Bearer token (future private tools)

Attach the token from §Claim in the `Authorization` header:

```http
POST /api/mcp HTTP/1.1
Host: north28.studio
Content-Type: application/json
Authorization: Bearer <access_token>

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "get_studio_info", "arguments": {} }
}
```

---

## 5 · Errors

### OAuth error codes (token endpoint)

| Code | HTTP | Description |
|---|---|---|
| `invalid_request` | 400 | Missing or malformed parameter |
| `invalid_client` | 401 | Unknown or invalid `client_id` |
| `invalid_grant` | 400 | Expired or invalid grant |
| `unauthorized_client` | 400 | Client not authorised for this grant type |
| `unsupported_grant_type` | 400 | `grant_type` not supported |
| `invalid_scope` | 400 | Requested scope unknown or not permitted |

### MCP / resource server error codes

| Code | HTTP | Description |
|---|---|---|
| `invalid_token` | 401 | Token is expired, revoked, or malformed — re-authenticate |
| `insufficient_scope` | 403 | Token lacks the required scope (`mcp:read`) |
| `invalid_request` | 400 | Malformed JSON-RPC request body |
| `method_not_found` | 404 | JSON-RPC method does not exist |

### Error response format (resource server)

```json
{
  "error": "invalid_token",
  "error_description": "The access token is expired. Re-authenticate using POST /oauth/token."
}
```

### JSON-RPC error format

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32601, "message": "Method not found: tools/unknown" }
}
```

---

## 6 · Revocation

Revoke a token when it is no longer needed (RFC 7009):

```http
POST /oauth/revoke HTTP/1.1
Host: north28.studio
Content-Type: application/x-www-form-urlencoded

token=<access_token>&client_id=<your-client-id>
```

The server responds with `200 OK` whether or not the token was valid (per RFC 7009). After revocation, requests using the token will receive `invalid_token`.

---

## 7 · Key Verification (Web Bot Auth)

To verify that a response originates from North28, fetch the Ed25519 public key:

```http
GET /.well-known/jwks.json HTTP/1.1
Host: north28.studio
Accept: application/json
```

Key ID `north28-bot-auth-2026-01` (`crv: Ed25519`) is valid from `2026-05-28` to `2027-05-28`. Verify HTTP message signatures (RFC 9421) using the `x` value of the JWK.

---

## Discovery Reference

| Endpoint | Standard | Description |
|---|---|---|
| `/.well-known/oauth-protected-resource` | RFC 9728 | PRM — resource + authorization server list |
| `/.well-known/oauth-authorization-server` | RFC 8414 | AS metadata — token, registration, revocation endpoints |
| `/.well-known/jwks.json` | RFC 7517 | Ed25519 public key for signature verification |
| `/.well-known/http-message-signatures-directory` | RFC 9421 | Bot-auth key directory |
| `/.well-known/api-catalog` | RFC 9727 | Full API catalog (linkset+json) |
| `/auth.md` | — | This document |

---

*NORTH28 STUDIO — Last updated: 2026.*
