ITADN

feat: allow API key authentication for /domains endpoints

#369OpenReylanLugo 创建于 2026-05-10
R
ReylanLugocommented
## Is your feature request related to a problem? The `/domains` endpoints (`GET project/:projectId`, `POST`, `GET :id/verify`, `DELETE :id`) currently only accept dashboard JWT authentication via cookies (`isAuthenticated` middleware). Domain management is therefore only possible from the web dashboard, not via the project's secret API key. This is inconsistent with the rest of the Plunk API surface — `/v1/send`, `/v1/track`, `/contacts/*`, `/templates/*`, `/events/*`, etc. all accept `Authorization: Bearer sk_…` and let callers manage project resources programmatically. For self-hosters and platforms automating Plunk on top of their own product (provisioning per-tenant projects, rotating sender domains, polling DKIM verification status, etc.), this gap forces either browser automation against the dashboard or maintaining a fork. ### Industry precedent The closest comparable provider, **Resend**, exposes its full domain management surface via Bearer API key (`Authorization: Bearer re_xxxxxxxxx`): | Method | Path | Source | |---|---|---| | POST | `/domains` | https://resend.com/docs/api-reference/domains/create-domain | | GET | `/domains` | https://resend.com/docs/api-reference/domains/list-domains | | GET | `/domains/{id}` | https://resend.com/docs/api-reference/domains/get-domain | | PATCH | `/domains/{id}` | https://resend.com/docs/api-reference/domains/update-domain | | DELETE | `/domains/{id}` | https://resend.com/docs/api-reference/domains/delete-domain | | POST | `/domains/{id}/verify`| https://resend.com/docs/api-reference/domains/verify-domain | Bringing Plunk's `/domains` to parity with this pattern is what most users automating an email platform expect today. ## Describe the solution you'd like Switch the `/domains` controller from `isAuthenticated` (cookie-only) to `requireAuth` (cookie OR API key), matching the pattern used by every other project-scoped endpoint in Plunk: - `GET /domains/project/:projectId` — list domains; accept API key scoped to that project. - `POST /domains` — create domain; accept API key (require `body.projectId === auth.projectId`). - `GET /domains/:id/verify` — check verification; accept API key whose project owns the domain. - `DELETE /domains/:id` — remove domain; same scoping as verify. ### Authorization model API keys in Plunk are project-scoped credentials with full access to the project's resources. JWT (dashboard) auth retains its role-based checks (`requireAdminAccess` for `POST` / `DELETE`). For API key requests the only check needed is "does this API key own the target project?" — same model already in use across `/contacts`, `/templates`, `/v1/send`, etc. ## Alternatives or workarounds - Run a headless browser to log in to the dashboard and submit forms (fragile, slow, requires storing user credentials). - Maintain a fork (the workaround currently in use; this issue exists to upstream the change). ## Additional context Implementation is small and surgical (~60 added lines, no schema changes): - `apps/api/src/controllers/Domains.ts`: switch four `@Middleware([isAuthenticated, …])` → `@Middleware([requireAuth, …])` and branch each handler on `auth.type === 'apiKey'`. - `apps/api/src/services/DomainService.ts`: make `userId` optional in `checkDomainOwnership` (API key flow has no user); preserve existing return shape. - Bonus UX: when a user/API key tries to add a domain already linked to *their own* project, return a clear "already linked to this project" message instead of the generic cross-project error. PR with the implementation and test results: #368.
0 条评论