diff options
Diffstat (limited to 'app/api/auth/verify-mfa/route.ts')
| -rw-r--r-- | app/api/auth/verify-mfa/route.ts | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/app/api/auth/verify-mfa/route.ts b/app/api/auth/verify-mfa/route.ts new file mode 100644 index 00000000..f9d1b51e --- /dev/null +++ b/app/api/auth/verify-mfa/route.ts @@ -0,0 +1,65 @@ +// app/api/auth/verify-mfa/route.ts + +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/app/api/auth/[...nextauth]/route'; +import { verifySmsToken } from '@/lib/users/auth/passwordUtil'; + +const verifyMfaSchema = z.object({ + userId: z.string(), + token: z.string().length(6, '6자리 인증번호를 입력해주세요'), +}); + +export async function POST(request: NextRequest) { + try { + // 세션 확인 + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json( + { error: '인증이 필요합니다' }, + { status: 401 } + ); + } + + const body = await request.json(); + const { userId, token } = verifyMfaSchema.parse(body); + + // 본인 확인 + if (session.user.id !== userId) { + return NextResponse.json( + { error: '권한이 없습니다' }, + { status: 403 } + ); + } + + // MFA 토큰 검증 + const result = await verifySmsToken(parseInt(userId), token); + + if (result.success) { + return NextResponse.json({ + success: true, + message: 'MFA 인증이 완료되었습니다' + }); + } else { + return NextResponse.json( + { error: result.error }, + { status: 400 } + ); + } + + } catch (error) { + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: error.errors[0]?.message || '잘못된 요청입니다' }, + { status: 400 } + ); + } + + console.error('MFA verify API error:', error); + return NextResponse.json( + { error: '서버 오류가 발생했습니다' }, + { status: 500 } + ); + } +}
\ No newline at end of file |
