Files
HealthLog/src/app/api/tokens/route.ts
T
Marc-André Bombeck 8012aa095b feat(tokens): retire the generic API-token mint
POST /api/tokens minted ["medication:ingest"], and that token could never do
the job the settings copy advertised: /api/ingest/medication gates on the
per-medication medication:<id>:ingest grant as well, which this endpoint never
issued. It was refused at the ingest surface and admitted everywhere else.

With Bearer scope now fail-closed the token is inert rather than dangerous,
which is a worse thing to hand a user than the bug it replaces. Remove the
mint. The working credential comes from the per-medication API-endpoint
toggle, which issues both grants scoped to one medication.

List and revoke stay so existing tokens remain visible and revocable, and the
settings card now points at the per-medication toggle instead of offering a
name field. Six locales updated.
2026-07-19 00:51:29 +02:00

45 lines
1.5 KiB
TypeScript

/**
* `/api/tokens` — list and (via `[id]`) revoke a user's API tokens.
*
* The generic mint that used to live here (`POST`, minting
* `["medication:ingest"]`) was removed alongside the fail-closed scope
* default. That token could never do its advertised job — the external ingest
* surface gates on the per-medication `medication:<id>:ingest` grant, which
* this endpoint never issued — while the old fail-open default let it reach
* every other authenticated route. The working credential is minted by the
* per-medication API-endpoint toggle
* (`/api/medications/[id]/api-endpoint`), which issues both grants.
*
* Listing and revoking stay so existing tokens remain visible and revocable.
*/
import { prisma } from "@/lib/db";
import { apiHandler, requireAuth } from "@/lib/api-handler";
import { annotate } from "@/lib/logging/context";
import { apiSuccess, apiError } from "@/lib/api-response";
import { isApiGloballyEnabled } from "@/lib/app-settings";
export const GET = apiHandler(async () => {
const { user } = await requireAuth();
annotate({ action: { name: "tokens.list" } });
if (!(await isApiGloballyEnabled())) {
return apiError("API is globally disabled", 403);
}
const tokens = await prisma.apiToken.findMany({
where: { userId: user.id },
select: {
id: true,
name: true,
permissions: true,
lastUsedAt: true,
expiresAt: true,
createdAt: true,
revoked: true,
},
orderBy: { createdAt: "desc" },
});
return apiSuccess(tokens);
});