feat: enhance user registration and authentication flow, improve attachment handling, and strengthen security measures

This commit is contained in:
shuaiplus
2026-02-14 00:34:08 +08:00
parent b33ee64c58
commit 4772c17e44
8 changed files with 131 additions and 25 deletions
+8 -8
View File
@@ -27,12 +27,6 @@ export async function handleRegister(request: Request, env: Env): Promise<Respon
return errorResponse(message, 400);
}
// Check if already registered
const isRegistered = await storage.isRegistered();
if (isRegistered) {
return errorResponse('Registration is closed', 403);
}
let body: {
email?: string;
name?: string;
@@ -88,7 +82,11 @@ export async function handleRegister(request: Request, env: Env): Promise<Respon
updatedAt: new Date().toISOString(),
};
await storage.saveUser(user);
const created = await storage.createFirstUser(user);
if (!created) {
return errorResponse('Registration is closed', 403);
}
await storage.setRegistered();
return jsonResponse({ success: true }, 200);
@@ -200,6 +198,7 @@ export async function handleGetRevisionDate(request: Request, env: Env, userId:
// POST /api/accounts/verify-password
export async function handleVerifyPassword(request: Request, env: Env, userId: string): Promise<Response> {
const storage = new StorageService(env.DB);
const auth = new AuthService(env);
const user = await storage.getUserById(userId);
if (!user) {
@@ -217,7 +216,8 @@ export async function handleVerifyPassword(request: Request, env: Env, userId: s
return errorResponse('masterPasswordHash is required', 400);
}
if (body.masterPasswordHash !== user.masterPasswordHash) {
const valid = await auth.verifyPassword(body.masterPasswordHash, user.masterPasswordHash);
if (!valid) {
return errorResponse('Invalid password', 400);
}
+6 -1
View File
@@ -1,4 +1,4 @@
import { Env, Attachment } from '../types';
import { Env, Attachment, DEFAULT_DEV_SECRET } from '../types';
import { StorageService } from '../services/storage';
import { jsonResponse, errorResponse } from '../utils/response';
import { generateUUID } from '../utils/uuid';
@@ -210,6 +210,11 @@ export async function handlePublicDownloadAttachment(
cipherId: string,
attachmentId: string
): Promise<Response> {
const secret = (env.JWT_SECRET || '').trim();
if (!secret || secret.length < 32 || secret === DEFAULT_DEV_SECRET) {
return errorResponse('Server configuration error', 500);
}
const url = new URL(request.url);
const token = url.searchParams.get('token');
+3 -1
View File
@@ -68,10 +68,12 @@ export async function handleGetCiphers(request: Request, env: Env, userId: strin
? ciphers
: ciphers.filter(c => !c.deletedAt);
const attachmentsByCipher = await storage.getAttachmentsByCipherIds(filteredCiphers.map(c => c.id));
// Get attachments for all ciphers
const cipherResponses = [];
for (const cipher of filteredCiphers) {
const attachments = await storage.getAttachmentsByCipher(cipher.id);
const attachments = attachmentsByCipher.get(cipher.id) || [];
cipherResponses.push(cipherToResponse(cipher, attachments));
}
+7 -6
View File
@@ -32,12 +32,7 @@ export async function handleToken(request: Request, env: Env): Promise<Response>
return identityErrorResponse('Email and password are required', 'invalid_request', 400);
}
const user = await storage.getUser(email);
if (!user) {
return identityErrorResponse('Username or password is incorrect. Try again', 'invalid_grant', 400);
}
// Check if login is rate limited (only after confirming user exists)
// Check login lockout before user lookup to reduce user-enumeration signal
const loginCheck = await rateLimit.checkLoginAttempt(email);
if (!loginCheck.allowed) {
return identityErrorResponse(
@@ -47,6 +42,12 @@ export async function handleToken(request: Request, env: Env): Promise<Response>
);
}
const user = await storage.getUser(email);
if (!user) {
await rateLimit.recordFailedLogin(email);
return identityErrorResponse('Username or password is incorrect. Try again', 'invalid_grant', 400);
}
const valid = await auth.verifyPassword(passwordHash, user.masterPasswordHash);
if (!valid) {
// Record failed login attempt
+2 -1
View File
@@ -14,6 +14,7 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
const ciphers = await storage.getAllCiphers(userId);
const folders = await storage.getAllFolders(userId);
const attachmentsByCipher = await storage.getAttachmentsByCipherIds(ciphers.map(c => c.id));
// Build profile response
const profile: ProfileResponse = {
@@ -43,7 +44,7 @@ export async function handleSync(request: Request, env: Env, userId: string): Pr
// Build cipher responses with attachments
const cipherResponses: CipherResponse[] = [];
for (const cipher of ciphers) {
const attachments = await storage.getAttachmentsByCipher(cipher.id);
const attachments = attachmentsByCipher.get(cipher.id) || [];
cipherResponses.push(cipherToResponse(cipher, attachments));
}