# BD Event — Authentication

How authentication works for the public BD Event JSON API (`/api/community.php`, `/api/submissions.php`, `/api/subscriptions.php`).

## Overview

- There is **no public API key**, **no OAuth**, and **no user accounts** on the public surface.
- The public catalog itself needs no authentication — fetch `/`, `/secc.php` and `/event.php?id=N` freely (also as Markdown).
- Write endpoints authenticate the *visitor session* with a **CSRF token** tied to a browser-style cookie session (`PHPSESSID`-style cookie named `bd_session`).
- A separate, private **Telegram webhook** uses a static secret header — see [webhooks.md](webhooks.md).

## Session bootstrap (CSRF)

1. Send `GET /api/community.php?action=bootstrap`.
   The server creates (or reuses) a session cookie and returns:

   ```json
   { "ok": true, "csrf": "8c3f…" }
   ```

2. Include the returned `csrf` value in every subsequent **write** request body:

   ```json
   { "action": "rsvp", "event_id": 12, "status": "going", "csrf": "8c3f…" }
   ```

3. The server validates it with `hash_equals`. A wrong or missing token returns `403`:

   ```json
   { "ok": false, "error": "Phiên không hợp lệ. Tải lại trang." }
   ```

   (Translates to "Invalid session. Reload the page.")

### Notes

- The token rotates when the session is created; keep the same session cookie for all requests in the flow.
- Send the body as `application/json`, or as regular form fields — the API accepts either (`php://input` JSON first, then `$_POST`).
- Private fields (email, Zalo) are never exposed in any public response.

## Rate limiting

| Endpoint / action | Limit |
| --- | --- |
| `community` comment | 8 requests / hour per visitor |
| `community` rsvp | 15 requests / hour per visitor |
| `submissions` submit | 5 requests / hour per visitor |
| `subscriptions` subscribe | 5 requests / hour per visitor |

Rates are keyed by a hashed visitor key derived from the session cookie (not the raw IP alone). Exceeding the limit returns `429`-style handling through the standard error envelope.

## Spam honeypot

Write forms accept a hidden `honeypot` field (HTML class `hp`). If a bot fills it in, the request is silently accepted and discarded — a public response is still returned so bots cannot tell they were trapped.

## Error envelope

All endpoints answer JSON:

- Success: `{ "ok": true, ... }`
- Failure: `{ "ok": false, "error": "…" }`

HTTP status codes: `400` invalid action, `401` unauthorized, `403` CSRF failed, `404` not found, `422` validation error, `500` server error.

## Contact & reporting

Do not attempt admin/auth flows as an agent. For corrections or data questions, use the [contact page](/contact).