|
| 1 | +import { setCors, handleOptions } from './lib/cors.js'; |
| 2 | +import { requireClientCreds } from './lib/env.js'; |
| 3 | +import { revokeToken } from './lib/github.js'; |
| 4 | + |
1 | 5 | // Revoking oauth user tokens for more security at Zunalita! |
2 | 6 | export default async function handler(req, res) { |
3 | | - // Set CORS headers |
4 | | - res.setHeader("Access-Control-Allow-Origin", "https://zunalita.github.io"); |
5 | | - res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS"); |
6 | | - res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); |
7 | | - |
8 | | - if (req.method === "OPTIONS") { |
9 | | - return res.status(204).end(); |
10 | | - } |
| 7 | + setCors(res); |
| 8 | + if (handleOptions(req, res)) return; |
11 | 9 |
|
12 | | - if (req.method !== "POST") { |
13 | | - return res.status(405).json({ error: "method not allowed" }); |
| 10 | + if (req.method !== 'POST') { |
| 11 | + return res.status(405).json({ error: 'method not allowed' }); |
14 | 12 | } |
15 | 13 |
|
16 | | - const { token } = req.body; |
17 | | - |
| 14 | + const { token } = req.body || {}; |
18 | 15 | if (!token) { |
19 | | - return res.status(400).json({ error: "missing token" }); |
| 16 | + return res.status(400).json({ error: 'missing token' }); |
20 | 17 | } |
21 | 18 |
|
22 | | - // --- Prefix check (quick validation) --- |
23 | | - const validPrefixes = ["ghp_", "gho_", "ghu_", "ghs_", "ghr_"]; |
| 19 | + // quick format validation |
| 20 | + const validPrefixes = ['ghp_', 'gho_', 'ghu_', 'ghs_', 'ghr_']; |
24 | 21 | if (!validPrefixes.some(prefix => token.startsWith(prefix))) { |
25 | | - return res.status(400).json({ error: "invalid token format" }); |
| 22 | + return res.status(400).json({ error: 'invalid token format' }); |
26 | 23 | } |
27 | 24 |
|
28 | | - const client_id = process.env.GITHUB_CLIENT_ID; |
29 | | - const client_secret = process.env.GITHUB_CLIENT_SECRET; |
30 | | - |
31 | | - if (!client_id || !client_secret) { |
32 | | - return res.status(500).json({ error: "server misconfigured" }); |
| 25 | + let client_id, client_secret; |
| 26 | + try { |
| 27 | + ({ client_id, client_secret } = requireClientCreds()); |
| 28 | + } catch (err) { |
| 29 | + console.error(err); |
| 30 | + return res.status(500).json({ error: 'server misconfigured' }); |
33 | 31 | } |
34 | 32 |
|
35 | 33 | try { |
36 | | - const tokenRevocationRes = await fetch( |
37 | | - `https://api.github.com/applications/${client_id}/token`, |
38 | | - { |
39 | | - method: "DELETE", |
40 | | - headers: { |
41 | | - "Content-Type": "application/json", |
42 | | - "Accept": "application/json", |
43 | | - "Authorization": `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString("base64")}`, |
44 | | - }, |
45 | | - body: JSON.stringify({ access_token: token }), |
46 | | - } |
47 | | - ); |
48 | | - |
49 | | - if (!tokenRevocationRes.ok) { |
50 | | - const errText = await tokenRevocationRes.text(); |
51 | | - throw new Error(`gitHub responded with an error during token revocation: ${errText}`); |
52 | | - } |
53 | | - |
54 | | - res.status(200).json({ message: "token revoked successfully" }); |
| 34 | + await revokeToken(token, client_id, client_secret); |
| 35 | + return res.status(200).json({ message: 'token revoked successfully' }); |
55 | 36 | } catch (err) { |
56 | 37 | console.error(err); |
57 | | - res.status(500).json({ error: "token revocation failed" }); |
| 38 | + return res.status(500).json({ error: 'token revocation failed' }); |
58 | 39 | } |
59 | 40 | } |
0 commit comments