Files
CloudFlare-ImgBed/functions/utils/userAuth.js
T
MarSeventh 5e48fde2ca feat: session-based auth with password hashing
- Add passwordHash.js: SHA-256 + salt hashing with plaintext backward compatibility
- Add sessionManager.js: HttpOnly cookie sessions with separate admin_session/user_session
- Add sessionCheck.js: session validation endpoint for frontend route guards
- Add logout.js: session destruction endpoint with authType support
- Update login.js: return user_session cookie on successful login
- Update check.js: return admin_session cookie on successful admin auth
- Update _middleware.js: check admin_session before Basic Auth, remove WWW-Authenticate header
- Update security.js: hash passwords on save, mask in GET, clear sessions on password change
- Update dualAuth.js/userAuth.js: use verifyPassword and session validation
2026-04-17 23:11:45 +08:00

100 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { fetchSecurityConfig } from './sysConfig';
import { validateApiToken } from './tokenValidator';
import { getDatabase } from './databaseAdapter.js';
import { verifyPassword } from './passwordHash.js';
import { validateSession } from './sessionManager.js';
/**
* 客户端用户认证
* @param {Object} env - 环境变量
* @param {URL} url - 请求的URL
* @param {Request} request - 请求对象
* @param {string|null} requiredPermission - 如果提供,则进行Token验证
* @return {Promise<boolean>} 返回是否认证通过
*/
export async function userAuthCheck(env, url, request, requiredPermission = null) {
// 首先检查会话 Cookieuser 或 admin 都可以通过用户端认证)
const userSession = await validateSession(env, request, 'user');
if (userSession.valid) {
return true;
}
const adminSession = await validateSession(env, request, 'admin');
if (adminSession.valid) {
return true;
}
// 然后使用Token验证
const tokenValidation = await validateApiToken(request, getDatabase(env), requiredPermission);
if (tokenValidation.valid) {
return true;
}
// Token验证失败,继续尝试传统认证方式
const securityConfig = await fetchSecurityConfig(env);
const rightAuthCode = securityConfig.auth.user.authCode;
// 优先从请求 URL 参数获取 authCode
let authCode = url.searchParams.get('authCode');
// 如果 URL 参数中没有 authCode,从 Referer 中获取
if (!authCode) {
const referer = request.headers.get('Referer');
if (referer) {
try {
const refererUrl = new URL(referer);
authCode = new URLSearchParams(refererUrl.search).get('authCode');
} catch (e) {
console.error('Invalid referer URL:', e);
}
}
}
// 如果 Referer 中没有 authCode,从请求头中获取
if (!authCode) {
authCode = request.headers.get('authCode');
}
// 如果请求头中没有 authCode,从 Cookie 中获取
if (!authCode) {
const cookies = request.headers.get('Cookie');
if (cookies) {
authCode = getCookieValue(cookies, 'authCode');
}
}
if (isAuthCodeDefined(rightAuthCode) && !(await isValidAuthCode(rightAuthCode, authCode))) {
return false;
}
return true;
}
export function UnauthorizedResponse(reason) {
return new Response(reason, {
status: 401,
statusText: "Unauthorized",
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, authCode',
"Content-Type": "text/plain;charset=UTF-8",
"Cache-Control": "no-store",
"Content-Length": reason.length,
},
});
}
async function isValidAuthCode(rightAuthCode, authCode) {
if (!authCode) return false;
return await verifyPassword(authCode, rightAuthCode);
}
function isAuthCodeDefined(authCode) {
return authCode !== undefined && authCode !== null && authCode.trim() !== '';
}
function getCookieValue(cookies, name) {
const match = cookies.match(new RegExp('(^| )' + name + '=([^;]+)'));
return match ? decodeURIComponent(match[2]) : null;
}