# auth.md — KingdomOfBooks API Agent Authentication

## API Identity

- **Resource identifier**: `https://api.kingdomofbook.com`
- **API base URL**: `https://api.kingdomofbook.com`
- **Swagger UI**: `https://api.kingdomofbook.com/swagger`
- **OpenAPI document**: `https://api.kingdomofbook.com/swagger/v1/swagger.json`

## Discovery Metadata

This API publishes standard agent-readiness metadata:

| Endpoint | Spec | Purpose |
|----------|------|---------|
| `/.well-known/oauth-authorization-server` | RFC 8414 | Authorization server metadata (token endpoint, grant types, scopes) |
| `/.well-known/oauth-protected-resource` | RFC 9728 | Protected resource metadata (how to obtain access tokens) |
| `/.well-known/mcp/server-card.json` | SEP-1649 | MCP Server Card (WebMCP transport + tool catalog) |

## Authentication Methods

### 1. JWT Bearer Token (Primary)

Obtain a JWT by exchanging user credentials at the token endpoint.

**Request:**
```http
POST /api/Authentication/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "your-password"
}
```

**Response (200 OK):**
```json
{
  "id": "user-id",
  "email": "user@example.com",
  "userName": "username",
  "token": "eyJhbGci...",
  "expiresIn": 3600,
  "refreshToken": "base64-refresh-token",
  "refreshTokenExpiration": "2026-07-06T12:00:00Z",
  "roles": ["User"],
  "referrelCode": "ABC123",
  "profilePic": "https://..."
}
```

**Use the token on subsequent requests:**
```http
Authorization: Bearer <token>
```

The token is also accepted from the `access_token` cookie if the login response set one.

**Token details:**
- Algorithm: HS256
- Issuer: `https://api.kingdomofbook.com`
- Audience: `https://api.kingdomofbook.com`
- Lifetime: 60 minutes
- Claims: `sub` (user ID), `email`, `jti`, `roles` (JSON array)

**Refresh token:**
```http
POST /api/Authentication/refresh-token
Content-Type: application/json

{
  "accessToken": "<expired-or-current-token>",
  "refreshToken": "<refresh-token>"
}
```

Refresh tokens expire after 14 days.

### 2. Google OIDC (Social Login)

Send a Google ID token (obtained client-side from Google Sign-In) to exchange for a local JWT.

```http
POST /api/Authentication/login/Google
Content-Type: application/json

{
  "IdToken": "<google-id-token>"
}
```

Returns the same response shape as password login. The server validates the Google ID token against ClientId `730714280260-cpq6i8oqiac41rdjuld6dcgpltlsm36o.apps.googleusercontent.com`.

### 3. API Key (External Integrations)

For server-to-server integrations that don't need per-user identity:

```http
X-API-Key: <your-api-key>
```

Contact the API operator to obtain an API key.

## Scopes

| Scope | Description |
|-------|-------------|
| `books:read` | Read-only access to the catalog: books, categories, authors, publishers, bundles, and reviews. |

## Agent Registration

### Registration Method 1: Email and Password

Create a new user account, then authenticate to obtain JWT credentials.

**Step 1 — Register:**
```http
POST /api/Authentication/register
Content-Type: application/json

{
  "email": "agent@example.com",
  "password": "SecurePassword123",
  "userName": "agent-user"
}
```
Response (200 OK):
```json
{
  "id": "user-id",
  "email": "agent@example.com",
  "userName": "agent-user",
  "token": "eyJhbGci...",
  "expiresIn": 3600,
  "refreshToken": "base64-refresh-token",
  "refreshTokenExpiration": "2026-07-06T12:00:00Z",
  "roles": ["User"]
}
```

**Step 2 — Authenticate (token expires):**
```http
POST /api/Authentication/login
Content-Type: application/json

{
  "email": "agent@example.com",
  "password": "SecurePassword123"
}
```
Returns the same response shape as registration (new token + refresh token).

**Step 3 — Refresh token:**
```http
POST /api/Authentication/refresh-token
Content-Type: application/json

{
  "accessToken": "<current-token>",
  "refreshToken": "<refresh-token>"
}
```

**Step 4 — Use credentials:**
```http
Authorization: Bearer <token>
```

### Registration Method 2: Google OIDC

Send a Google ID token to create an account or log in.

```http
POST /api/Authentication/login/Google
Content-Type: application/json

{
  "IdToken": "<google-id-token>"
}
```

Returns the same response shape as email/password registration. The server validates the Google ID token against ClientId `730714280260-cpq6i8oqiac41rdjuld6dcgpltlsm36o.apps.googleusercontent.com`.

### Registration Method 3: API Key

For server-to-server integrations that don't need per-user identity:

```http
X-API-Key: <your-api-key>
```

Contact the API operator to obtain an API key.

## Roles

| Role | Access |
|------|--------|
| `User` | Personal endpoints (favorites, reviews, addresses, orders, basket) + all read-only catalog endpoints |
| `Admin` | All User access + admin CRUD on books, categories, authors, publishers, bundles, orders, banners, vouchers, analytics |
| `SuperAdmin` | All Admin access + role management |

## Key Endpoints for Agents (books:read scope)

All endpoints below are available without authentication for read-only catalog browsing. Authenticated calls with the `User` role return the user-facing view (hides digital download URLs).

### Books
- `GET /api/Books` — Search and browse books (query params: `search`, `categoryId`, `authorId`, `publisherName`, `coverType`, `language`, `sort`, `pageIndex`, `pageSize`, `isLatest`, `isMostSelling`)
- `GET /api/Books/{id}` — Get a single book by ID
- `GET /api/Books/most-selling` — Best-selling books
- `GET /api/Books/categorized` — Books grouped by category
- `GET /api/Books/reviews?bookId={id}` — Reviews for a book

### Categories
- `GET /api/Categories` — List categories (`search`, `appearInHomePage`, `pageIndex`, `pageSize`)
- `GET /api/Categories/{id}` — Get a single category

### Authors
- `GET /api/Authors` — List authors (`search`, `pageIndex`, `pageSize`)
- `GET /api/Authors/{id}` — Get a single author

### Publishers
- `GET /api/Publishers` — List publishers (`search`, `pageIndex`, `pageSize`)
- `GET /api/Publishers/{id}` — Get a single publisher

### Bundles
- `GET /api/Bundles` — List bundles (`search`, `bookId`, `pageIndex`, `pageSize` — max 50)
- `GET /api/Bundles/{id}` — Get a single bundle

## Rate Limits

| Policy | Limit | Applies to |
|--------|-------|------------|
| `PerIpFixedWindow` | 100 requests / minute | All API endpoints (per IP) |
| `PerIpAuthFixedWindow` | 5 requests / minute | Login, register, password reset |

When rate-limited, the API responds with `429 Too Many Requests` and a `Retry-After: 10` header.

## Localization

### Currency
Prices are returned in the user's local currency. Set the `store_country` cookie (ISO country code) to control currency conversion. The `UsdPrice` field always contains the original USD price. Default currency is USD.

### Language
Book names and descriptions have Arabic (`Name_Ar`, `Description_Ar`) and English (`Name`, `Description`) fields. Set `Accept-Language: ar` to get Arabic-fallback behavior on the user-facing endpoints.

## Cover Images

Book cover images (`PictureUrl`) and author profile pictures (`ProfilePic`) are returned as time-limited Minio presigned URLs. These URLs expire after 12 hours. Do not cache them long-term — re-fetch the book/author record to get a fresh URL.

## WebMCP

In-browser AI agents can connect to this API via WebMCP at:

```
https://api.kingdomofbook.com/agent
```

The `/agent` page calls `navigator.modelContext.provideContext()` with 13 read-only catalog tools. See `/.well-known/mcp/server-card.json` for the full tool catalog.

## Security Notes

- All endpoints require HTTPS in production.
- JWT tokens are validated with issuer, audience, lifetime, and signature checks.
- The `access_token` cookie is HttpOnly, Secure, and SameSite=None for cross-site API calls.
- An `XSRF-TOKEN` cookie is set on login for CSRF protection on cookie-authenticated mutations.
