-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy path[id].js
More file actions
41 lines (34 loc) · 1.5 KB
/
Copy path[id].js
File metadata and controls
41 lines (34 loc) · 1.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { sql } from '../_lib/db.js';
import { getSessionUser, authenticateBearer, extractBearer, hasScope } from '../_lib/auth.js';
import { logAudit } from '../_lib/audit.js';
import { cors, json, error, wrap, method, rateLimited } from '../_lib/http.js';
import { requireCsrf } from '../_lib/csrf.js';
import { limits, clientIp } from '../_lib/rate-limit.js';
export default wrap(async (req, res) => {
if (cors(req, res, { methods: 'DELETE,OPTIONS', credentials: true })) return;
if (!method(req, res, ['DELETE'])) return;
const session = await getSessionUser(req);
const bearer = session ? null : await authenticateBearer(extractBearer(req));
if (!session && !bearer) return error(res, 401, 'unauthorized', 'sign in required');
if (bearer && !hasScope(bearer.scope, 'profile'))
return error(res, 403, 'insufficient_scope', 'requires profile scope');
const userId = session?.id ?? bearer.userId;
if (session && !(await requireCsrf(req, res, session.id))) return;
const rl = await limits.authIp(clientIp(req));
if (!rl.success) return rateLimited(res, rl);
const { id } = req.query;
const [row] = await sql`
update api_keys
set revoked_at = now()
where id = ${id} and user_id = ${userId} and revoked_at is null
returning id
`;
if (!row) return error(res, 404, 'not_found', 'API key not found or already revoked');
logAudit({
userId,
action: 'revoke_api_key',
resourceId: row.id,
meta: { via: session ? 'session' : 'bearer' },
});
return json(res, 200, { data: { id: row.id, revoked: true } });
});