api-key-auth.js
2.38 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* API Key Authentication Middleware
*
* 用于外部 Open API (/api/open/*) 的认证。
* 从 Authorization: Bearer <key> 或 X-API-Key: <key> 头提取 API Key,
* 校验是否在 OPEN_API_KEYS 列表中。
*/
const config = require('../config');
/**
* 提取 API Key
* @param {import('express').Request} req
* @returns {string|null}
*/
function extractApiKey(req) {
// 优先 Authorization: Bearer <key>
const authHeader = req.headers['authorization'];
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.slice(7).trim();
}
// 备选 X-API-Key
const xApiKey = req.headers['x-api-key'];
if (xApiKey) return String(xApiKey).trim();
// Query param fallback (方便测试)
if (req.query.api_key) return String(req.query.api_key).trim();
return null;
}
/**
* Express 中间件:校验 API Key
*/
function requireApiKey(req, res, next) {
const key = extractApiKey(req);
if (!key) {
return res.status(401).json({
error: 'Unauthorized',
message: 'API Key is required. Provide via Authorization: Bearer <key> or X-API-Key header.',
});
}
let validUser = null;
try {
const userDb = require('../services/user-db');
const row = userDb.findByApiKey(key);
// 过 toPublicUser 再挂上去:findByApiKey 返回的是**原始数据库行**,带 password_hash。
// JWT 那条路(jwt-auth.extractUser)一直是脱敏后才挂的,这里以前不是 ——
// 两条鉴权路径行为不一致,而 req.user 是 handler 唯一的身份来源:
// 将来任何一个 res.json({ user: req.user }) 都会把密码哈希发给外部 API 调用方。
// 目前所有 handler 只读 req.user.id / .email,还没真漏,但这个口子不该留着。
validUser = row ? userDb.toPublicUser(row) : null;
} catch (err) {
console.error('[OpenAPI] DB check failed for API key:', err);
}
const validKeys = config.openApi?.apiKeys || [];
if (!validUser && !validKeys.includes(key)) {
// 都不匹配时拦截
return res.status(401).json({
error: 'Unauthorized',
message: 'Invalid API Key. Please provide a valid user key or system key.',
});
}
// 将 key 和(如有)的 user 附加到 req 上(后续用于任务归属和下载跟踪)
req.apiKey = key;
if (validUser) {
req.user = validUser;
}
next();
}
module.exports = { requireApiKey, extractApiKey };