From f0e523376c6e72f6222430ce37b8b10eebd4870c Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Mon, 6 Jul 2026 01:35:26 +0800 Subject: [PATCH] fix: add admin auth request compatibility --- src/handlers/auth-requests.ts | 66 +++++++++++++++++++++++++++++++++++ src/router-authenticated.ts | 12 +++++-- src/router-public.ts | 4 +-- 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/handlers/auth-requests.ts b/src/handlers/auth-requests.ts index 121001b..fa8c350 100644 --- a/src/handlers/auth-requests.ts +++ b/src/handlers/auth-requests.ts @@ -227,6 +227,72 @@ export async function handleCreateAuthRequest(request: Request, env: Env): Promi return jsonResponse(toAuthRequestResponse(request, authRequest)); } +export async function handleCreateAdminAuthRequest( + request: Request, + env: Env, + userId: string, + userEmail: string +): Promise { + const storage = new StorageService(env.DB); + const body = await readJsonBody(request); + if (!body) return errorResponse('Invalid request payload', 400); + + const email = normalizeText(readBodyValue(body, ['email', 'Email']), 320).toLowerCase() || userEmail.toLowerCase(); + const publicKey = normalizeText(readBodyValue(body, ['publicKey', 'PublicKey']), 8192); + const accessCode = normalizeText(readBodyValue(body, ['accessCode', 'AccessCode']), 25); + const requestedType = Number(readBodyValue(body, ['type', 'Type'])); + const deviceInfo = readAuthRequestDeviceInfo( + { + deviceIdentifier: normalizeText(readBodyValue(body, ['deviceIdentifier', 'DeviceIdentifier']), 128), + deviceName: normalizeText(readBodyValue(body, ['deviceName', 'DeviceName']), 128), + deviceType: String(readBodyValue(body, ['deviceType', 'DeviceType']) ?? ''), + }, + request + ); + + if (requestedType !== AUTH_REQUEST_TYPE_ADMIN_APPROVAL) { + return errorResponse('Invalid AuthRequestType. Expected AdminApproval.', 400); + } + if (email !== userEmail.toLowerCase()) { + return errorResponse('Email does not match authenticated user.', 400); + } + if (!publicKey || !accessCode || !deviceInfo.deviceIdentifier) { + return errorResponse('Public key, device identifier, and access code are required.', 400); + } + const rateLimitResponse = await enforceAuthRequestCreateRateLimit(request, env, email, deviceInfo.deviceIdentifier); + if (rateLimitResponse) return rateLimitResponse; + + const user = await storage.getUserById(userId); + if (!user || user.status !== 'active') { + return errorResponse('User not found.', 404); + } + + await storage.pruneExpiredAuthRequests(); + const now = new Date().toISOString(); + const authRequest: AuthRequestRecord = { + id: generateUUID(), + userId: user.id, + organizationId: null, + type: AUTH_REQUEST_TYPE_ADMIN_APPROVAL, + requestDeviceIdentifier: deviceInfo.deviceIdentifier, + requestDeviceType: deviceInfo.deviceType, + requestIpAddress: getClientIp(request), + requestCountryName: getCountryName(request), + responseDeviceIdentifier: null, + accessCode, + publicKey, + key: null, + masterPasswordHash: null, + approved: null, + creationDate: now, + responseDate: null, + authenticationDate: null, + }; + await storage.createAuthRequest(authRequest); + notifyUserAuthRequest(env, user.id, authRequest.id, deviceInfo.deviceIdentifier); + return jsonResponse(toAuthRequestResponse(request, authRequest)); +} + export async function handleGetAuthRequest(request: Request, env: Env, userId: string, id: string): Promise { const storage = new StorageService(env.DB); const authRequest = await storage.getAuthRequestByIdForUser(id, userId); diff --git a/src/router-authenticated.ts b/src/router-authenticated.ts index a6336c8..ff99b0c 100644 --- a/src/router-authenticated.ts +++ b/src/router-authenticated.ts @@ -90,6 +90,7 @@ import { handleUpdateAccountPasskeyEncryption, } from './handlers/account-passkeys'; import { + handleCreateAdminAuthRequest, handleGetAuthRequest, handleListAuthRequests, handleListPendingAuthRequests, @@ -379,17 +380,22 @@ export async function handleAuthenticatedRoute( if (method === 'DELETE') return handleDeleteFolder(request, env, userId, folderId); } - if (path === '/api/auth-requests' || path === '/api/auth-requests/') { + if (path === '/api/auth-requests' || path === '/api/auth-requests/' || path === '/auth-requests' || path === '/auth-requests/') { if (method === 'GET') return handleListAuthRequests(request, env, userId); return errorResponse('Method not allowed', 405); } - if (path === '/api/auth-requests/pending') { + if (path === '/api/auth-requests/pending' || path === '/auth-requests/pending') { if (method === 'GET') return handleListPendingAuthRequests(request, env, userId); return errorResponse('Method not allowed', 405); } - const authRequestMatch = path.match(/^\/api\/auth-requests\/([a-f0-9-]+)$/i); + if (path === '/api/auth-requests/admin-request' || path === '/auth-requests/admin-request') { + if (method === 'POST') return handleCreateAdminAuthRequest(request, env, userId, currentUser.email); + return errorResponse('Method not allowed', 405); + } + + const authRequestMatch = path.match(/^\/(?:api\/)?auth-requests\/([a-f0-9-]+)$/i); if (authRequestMatch) { if (method === 'GET') return handleGetAuthRequest(request, env, userId, authRequestMatch[1]); if (method === 'PUT') return handleUpdateAuthRequest(request, env, userId, authRequestMatch[1]); diff --git a/src/router-public.ts b/src/router-public.ts index 4131e81..9d925b6 100644 --- a/src/router-public.ts +++ b/src/router-public.ts @@ -412,13 +412,13 @@ export async function handlePublicRoute( return handleDownloadSendFile(request, env, sendDownloadMatch[1], sendDownloadMatch[2]); } - if ((path === '/api/auth-requests' || path === '/api/auth-requests/') && method === 'POST') { + if ((path === '/api/auth-requests' || path === '/api/auth-requests/' || path === '/auth-requests' || path === '/auth-requests/') && method === 'POST') { const blocked = await enforcePublicRateLimit('public-sensitive', LIMITS.rateLimit.sensitivePublicRequestsPerMinute); if (blocked) return blocked; return handleCreateAuthRequest(request, env); } - const authRequestResponseMatch = path.match(/^\/api\/auth-requests\/([a-f0-9-]+)\/response$/i); + const authRequestResponseMatch = path.match(/^\/(?:api\/)?auth-requests\/([a-f0-9-]+)\/response$/i); if (authRequestResponseMatch && method === 'GET') { const blocked = await enforcePublicRateLimit('public-sensitive', LIMITS.rateLimit.sensitivePublicRequestsPerMinute); if (blocked) return blocked;