fix(auth): clean up MFA routes — remove redundant imports, use userRepo.update()
- Remove redundant dynamic import('node:crypto'), use top-level nodeCrypto
- Remove getCollection import, use userRepo.update() for mfaEnabled/mfaMethods
- Expand update() Pick type to include mfaEnabled, mfaMethods, emailVerified
- Remove unused _reply param from MFA policy PUT handler
This commit is contained in:
parent
362b915ea9
commit
bdb3e95e00
@ -16,7 +16,6 @@
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { BadRequestError, UnauthorizedError, ForbiddenError } from '../../../lib/errors.js';
|
||||
import * as nodeCrypto from 'node:crypto';
|
||||
import { getCollection } from '../../../lib/datastore.js';
|
||||
import * as userRepo from '../repository.js';
|
||||
import * as mfaRepo from './repository.js';
|
||||
import * as jwt from '../jwt.js';
|
||||
@ -27,7 +26,6 @@ import {
|
||||
MfaDisableSchema,
|
||||
MfaPolicySchema,
|
||||
} from './types.js';
|
||||
import type { UserDoc } from '../types.js';
|
||||
import type { MfaPolicyDoc } from './types.js';
|
||||
|
||||
export async function mfaRoutes(app: FastifyInstance) {
|
||||
@ -44,8 +42,7 @@ export async function mfaRoutes(app: FastifyInstance) {
|
||||
}
|
||||
|
||||
// Generate TOTP secret (base32)
|
||||
const { randomBytes } = await import('node:crypto');
|
||||
const secretBuffer = randomBytes(20);
|
||||
const secretBuffer = nodeCrypto.randomBytes(20);
|
||||
const secret = base32Encode(secretBuffer);
|
||||
|
||||
// Encrypt the secret
|
||||
@ -113,11 +110,7 @@ export async function mfaRoutes(app: FastifyInstance) {
|
||||
|
||||
// Update user doc
|
||||
try {
|
||||
await getCollection<UserDoc>('users', '/id').update(payload.sub, payload.sub, {
|
||||
mfaEnabled: true,
|
||||
mfaMethods: ['totp'],
|
||||
updatedAt: now,
|
||||
} as Partial<UserDoc>);
|
||||
await userRepo.update(payload.sub, { mfaEnabled: true, mfaMethods: ['totp'] });
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
@ -205,12 +198,7 @@ export async function mfaRoutes(app: FastifyInstance) {
|
||||
|
||||
// Update user doc
|
||||
try {
|
||||
const now = new Date().toISOString();
|
||||
await getCollection<UserDoc>('users', '/id').update(payload.sub, payload.sub, {
|
||||
mfaEnabled: false,
|
||||
mfaMethods: [],
|
||||
updatedAt: now,
|
||||
} as Partial<UserDoc>);
|
||||
await userRepo.update(payload.sub, { mfaEnabled: false, mfaMethods: [] });
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
@ -265,7 +253,7 @@ export async function mfaRoutes(app: FastifyInstance) {
|
||||
return { policy: policy ?? null };
|
||||
});
|
||||
|
||||
app.put('/auth/mfa/policies/:productId', async (req, _reply) => {
|
||||
app.put('/auth/mfa/policies/:productId', async req => {
|
||||
const role = req.jwtPayload?.role;
|
||||
if (!role || !['super_admin', 'admin'].includes(role)) {
|
||||
throw new ForbiddenError('Admin access required');
|
||||
|
||||
@ -88,7 +88,19 @@ export async function countByPlan(productId: string): Promise<Record<string, num
|
||||
export async function update(
|
||||
id: string,
|
||||
updates: Partial<
|
||||
Pick<UserDoc, 'displayName' | 'role' | 'plan' | 'status' | 'phone' | 'bio' | 'avatarUrl'>
|
||||
Pick<
|
||||
UserDoc,
|
||||
| 'displayName'
|
||||
| 'role'
|
||||
| 'plan'
|
||||
| 'status'
|
||||
| 'phone'
|
||||
| 'bio'
|
||||
| 'avatarUrl'
|
||||
| 'mfaEnabled'
|
||||
| 'mfaMethods'
|
||||
| 'emailVerified'
|
||||
>
|
||||
>
|
||||
): Promise<UserDoc | null> {
|
||||
try {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user