Add API token authentication support

This commit is contained in:
zeer
2026-04-25 21:01:51 +08:00
parent 7b8f4aae06
commit 7202071545
4 changed files with 74 additions and 9 deletions

View File

@@ -11,6 +11,7 @@ const { sendMail, fetchLatestEmails, fetchMessageDetail } = require('./mailServi
const app = express();
const port = Number(process.env.PORT || 3000);
const appPassword = String(process.env.APP_PASSWORD || '').trim();
const apiToken = String(process.env.API_TOKEN || '').trim();
const authCookieName = 'mail_sr_auth';
const authCookieValue = appPassword
? crypto.createHash('sha256').update(appPassword).digest('hex')
@@ -22,7 +23,7 @@ app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/auth/status', (req, res) => {
res.json({ ok: true, authenticated: isAuthenticated(req) });
res.json({ ok: true, authenticated: isAuthenticated(req), tokenEnabled: Boolean(apiToken) });
});
app.post('/api/auth/login', (req, res) => {
@@ -45,7 +46,7 @@ app.post('/api/auth/logout', (_req, res) => {
});
app.use('/api', (req, res, next) => {
if (!appPassword) {
if (!appPassword && !apiToken) {
return next();
}
@@ -53,11 +54,11 @@ app.use('/api', (req, res, next) => {
return next();
}
if (!isAuthenticated(req)) {
return res.status(401).json({ error: '请先输入访问密码' });
if (isApiTokenAuthenticated(req) || isAuthenticated(req)) {
return next();
}
return next();
return res.status(401).json({ error: '请先登录或提供有效的 API Token' });
});
const getChannelStmt = db.prepare('SELECT * FROM channels WHERE id = ?');
@@ -422,6 +423,25 @@ function isAuthenticated(req) {
return cookies[authCookieName] === authCookieValue;
}
function isApiTokenAuthenticated(req) {
if (!apiToken) {
return false;
}
const bearerToken = getBearerToken(req.headers.authorization || '');
const headerToken = String(req.headers['x-api-token'] || '').trim();
return bearerToken === apiToken || headerToken === apiToken;
}
function getBearerToken(authorizationHeader) {
const value = String(authorizationHeader || '').trim();
if (!value.toLowerCase().startsWith('bearer ')) {
return '';
}
return value.slice(7).trim();
}
function parseCookies(cookieHeader) {
return String(cookieHeader || '')
.split(';')