From f31483edd0f8966aca3ab77bf3e9bafdabd68de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ccaneryy=E2=80=9D?= Date: Mon, 29 Jun 2026 15:09:03 +0300 Subject: [PATCH 1/2] fix(auth): validate login credentials against database --- package.json | 2 + .../email-verification-routes.test.ts | 20 +++++++ src/app/api/auth/login/route.ts | 52 ++++++++----------- src/lib/db/pool.ts | 25 +++++++++ 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 4f5902ab..abd64e31 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "migrate": "npx tsx src/lib/db/migrate.ts" }, "dependencies": { + "bcrypt": "^5.1.1", "@apollo/client": "^3.8.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^8.0.0", @@ -108,6 +109,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", + "@types/bcrypt": "^5.0.2", "@types/chai": "^5.2.3", "@types/d3-array": "^3.2.2", "@types/d3-color": "^3.1.3", diff --git a/src/app/api/auth/__tests__/email-verification-routes.test.ts b/src/app/api/auth/__tests__/email-verification-routes.test.ts index 356b3653..d66c76a3 100644 --- a/src/app/api/auth/__tests__/email-verification-routes.test.ts +++ b/src/app/api/auth/__tests__/email-verification-routes.test.ts @@ -43,6 +43,20 @@ vi.mock('@/lib/auth/email-verification', () => ({ getVerificationTokenTtlMinutes: vi.fn(() => 15), })); +vi.mock('bcrypt', () => ({ + default: { + compare: vi.fn().mockResolvedValue(true), + }, +})); + +vi.mock('@/lib/db/pool', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + findUserByEmail: vi.fn(), + }; +}); + describe('email verification routes', () => { beforeEach(() => { vi.clearAllMocks(); @@ -94,6 +108,12 @@ describe('email verification routes', () => { it('blocks login until verification is complete', async () => { const { getVerificationStatus } = await import('@/lib/auth/email-verification'); + const { findUserByEmail } = await import('@/lib/db/pool'); + vi.mocked(findUserByEmail).mockResolvedValue({ + id: 'user-1', + password_hash: 'hashed-password', + role: 'STUDENT', + }); vi.mocked(getVerificationStatus).mockResolvedValue({ required: true, status: 'pending', diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index d8c7acc0..3afd4241 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -1,10 +1,12 @@ import { NextRequest, NextResponse } from 'next/server'; +import bcrypt from 'bcrypt'; import { withRateLimit } from '@/lib/ratelimit'; import { validateBody } from '@/lib/validation'; import { LoginRequestSchema } from '@/types/api/auth.dto'; import type { AuthResponseDTO, AuthErrorDTO } from '@/types/api/auth.dto'; import { edgeLog } from '@/../infra/edge-config'; import { getVerificationStatus } from '@/lib/auth/email-verification'; +import { findUserByEmail, TIMING_SAFE_DUMMY_HASH } from '@/lib/db/pool'; export const runtime = 'nodejs'; @@ -25,22 +27,18 @@ export async function POST( if (!result.ok) return addHeaders(result.error) as NextResponse; const { email, password } = result.data; - const verification = await getVerificationStatus(email); + const user = await findUserByEmail(email); + const passwordHash = user?.password_hash ?? TIMING_SAFE_DUMMY_HASH; + const credentialsMatch = await bcrypt.compare(password, passwordHash); - // Mock: demo credentials - if (email === 'demo@teachlink.com' && password === 'password123') { + if (!user || !credentialsMatch) { return addHeaders( - NextResponse.json( - { - message: 'Login successful', - user: { id: '1', name: 'Demo User', email }, - token: `mock-jwt-token-${Date.now()}`, - }, - { status: 200 }, - ), + NextResponse.json({ message: 'Invalid credentials' }, { status: 401 }), ); } + const verification = await getVerificationStatus(email); + if (verification && verification.required && verification.status !== 'verified') { return addHeaders( NextResponse.json( @@ -53,25 +51,21 @@ export async function POST( ); } - // Mock: accept any valid email + password >= 6 chars - if (password.length >= 6) { - return addHeaders( - NextResponse.json( - { - message: 'Login successful', - user: { - id: Math.random().toString(36).substring(2, 9), - name: email.split('@')[0], - email, - }, - token: `mock-jwt-token-${Date.now()}`, + return addHeaders( + NextResponse.json( + { + message: 'Login successful', + user: { + id: user.id, + name: email.split('@')[0], + email, + role: user.role, }, - { status: 200 }, - ), - ); - } - - return addHeaders(NextResponse.json({ message: 'Invalid email or password' }, { status: 401 })); + token: `mock-jwt-token-${Date.now()}`, + }, + { status: 200 }, + ), + ); } catch (error) { console.error('Login error:', error); diff --git a/src/lib/db/pool.ts b/src/lib/db/pool.ts index f1fc9dd8..f17941f0 100644 --- a/src/lib/db/pool.ts +++ b/src/lib/db/pool.ts @@ -201,6 +201,31 @@ class DatabasePool { } } +export interface UserAuthRecord { + id: string; + password_hash: string; + role: string; +} + +/** + * Precomputed bcrypt hash used when no user record exists so password + * verification takes comparable time and cannot reveal valid emails. + */ +export const TIMING_SAFE_DUMMY_HASH = + '$2b$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy'; + +export async function findUserByEmail(email: string): Promise { + const result = await query('SELECT id, password_hash, role FROM users WHERE email = $1', [ + email, + ]); + + if (!result.rows.length) { + return null; + } + + return result.rows[0] as UserAuthRecord; +} + export const dbPool = DatabasePool; export const query = (text: string, params?: unknown[]) => { const traceId = logContextStorage.getStore()?.traceId ?? ''; From 82e1c36a0cdde6df46d10aca8ba8d9e0cb116c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ccaneryy=E2=80=9D?= Date: Mon, 29 Jun 2026 20:38:12 +0300 Subject: [PATCH 2/2] fix(ci): update lockfile and resolve auth test import paths - Sync pnpm-lock.yaml after adding bcrypt dependencies - Fix prettier formatting in login route - Correct signup/login route imports in a11y auth tests --- pnpm-lock.yaml | 233 +++++++++++++++++- .../email-verification-routes.test.ts | 4 +- src/app/api/auth/login/route.ts | 4 +- 3 files changed, 234 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 216b4ccf..04817162 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ importers: '@types/uuid': specifier: ^10.0.0 version: 10.0.0 + bcrypt: + specifier: ^5.1.1 + version: 5.1.1 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -215,6 +218,9 @@ importers: '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) + '@types/bcrypt': + specifier: ^5.0.2 + version: 5.0.2 '@types/chai': specifier: ^5.2.3 version: 5.2.3 @@ -1688,6 +1694,10 @@ packages: '@lit/reactive-element@2.1.2': resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@monaco-editor/loader@1.7.0': resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} @@ -2947,6 +2957,9 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/bcrypt@5.0.2': + resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} + '@types/bn.js@5.2.0': resolution: {integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==} @@ -3498,6 +3511,9 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abitype@1.0.8: resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} peerDependencies: @@ -3546,6 +3562,10 @@ packages: aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -3601,6 +3621,14 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3815,6 +3843,10 @@ packages: resolution: {integrity: sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==} engines: {node: '>=10.0.0'} + bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + bech32@1.1.4: resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} @@ -3981,6 +4013,10 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -4048,6 +4084,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -4069,6 +4109,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -4277,6 +4320,9 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -4803,6 +4849,10 @@ packages: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -4826,6 +4876,11 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -4969,6 +5024,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hash-base@3.1.2: resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} engines: {node: '>= 0.8'} @@ -5004,6 +5062,10 @@ packages: resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} engines: {node: '>=16'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -5695,6 +5757,10 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -5778,10 +5844,22 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + mipd@0.0.7: resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} peerDependencies: @@ -5793,6 +5871,11 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + moment-timezone@0.5.48: resolution: {integrity: sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==} @@ -5911,6 +5994,11 @@ packages: resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} engines: {node: '>=18'} + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -5919,6 +6007,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + nwsapi@2.2.23: resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} @@ -6617,6 +6709,11 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + ripemd160-min@0.0.6: resolution: {integrity: sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==} engines: {node: '>=8'} @@ -7018,6 +7115,11 @@ packages: tar-stream@3.2.0: resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + teex@1.0.1: resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} @@ -7643,6 +7745,9 @@ packages: engines: {node: '>=8'} hasBin: true + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + wif@2.0.6: resolution: {integrity: sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==} @@ -7809,6 +7914,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -9359,6 +9467,21 @@ snapshots: dependencies: '@lit-labs/ssr-dom-shim': 1.6.0 + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.1.2 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.8.1 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + '@monaco-editor/loader@1.7.0': dependencies: state-local: 1.0.7 @@ -10915,6 +11038,10 @@ snapshots: dependencies: '@babel/types': 7.29.7 + '@types/bcrypt@5.0.2': + dependencies: + '@types/node': 20.19.41 + '@types/bn.js@5.2.0': dependencies: '@types/node': 20.19.41 @@ -11707,6 +11834,8 @@ snapshots: '@xtuc/long@4.2.2': {} + abbrev@1.1.1: {} + abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): optionalDependencies: typescript: 5.9.3 @@ -11746,6 +11875,12 @@ snapshots: aes-js@4.0.0-beta.5: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + agent-base@7.1.4: {} ajv-formats@2.1.1(ajv@8.20.0): @@ -11796,6 +11931,13 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + aproba@2.1.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -12048,6 +12190,14 @@ snapshots: basic-ftp@5.3.1: {} + bcrypt@5.1.1: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + bech32@1.1.4: optional: true @@ -12323,6 +12473,8 @@ snapshots: dependencies: readdirp: 5.0.0 + chownr@2.0.0: {} + chrome-trace-event@1.0.4: {} chromium-bidi@14.0.0(devtools-protocol@0.0.1608973): @@ -12387,6 +12539,8 @@ snapshots: color-name@1.1.4: {} + color-support@1.1.3: {} + colorette@2.0.20: {} colors@1.0.3: {} @@ -12399,6 +12553,8 @@ snapshots: concat-map@0.0.1: {} + console-control-strings@1.1.0: {} + convert-source-map@2.0.0: {} cookie-es@1.2.3: {} @@ -12604,6 +12760,8 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 + delegates@1.0.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -13335,6 +13493,10 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -13356,6 +13518,18 @@ snapshots: functions-have-names@1.2.3: {} + gauge@3.0.2: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} @@ -13503,6 +13677,8 @@ snapshots: dependencies: has-symbols: 1.1.0 + has-unicode@2.0.1: {} + hash-base@3.1.2: dependencies: inherits: 2.0.4 @@ -13551,6 +13727,13 @@ snapshots: http_ece@1.2.0: {} + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -14414,6 +14597,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: semver: 7.8.1 @@ -14483,14 +14670,27 @@ snapshots: minimist@1.2.8: {} + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + minipass@7.1.3: {} + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + mipd@0.0.7(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 mitt@3.0.1: {} + mkdirp@1.0.4: {} + moment-timezone@0.5.48: dependencies: moment: 2.30.1 @@ -14570,8 +14770,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - node-addon-api@5.1.0: - optional: true + node-addon-api@5.1.0: {} node-exports-info@1.6.0: dependencies: @@ -14595,12 +14794,23 @@ snapshots: node-releases@2.0.46: {} + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + normalize-path@3.0.0: {} npm-run-path@4.0.1: dependencies: path-key: 3.1.1 + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + nwsapi@2.2.23: {} object-assign@4.1.1: {} @@ -15502,6 +15712,10 @@ snapshots: rfdc@1.4.1: {} + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + ripemd160-min@0.0.6: {} ripemd160@2.0.3: @@ -16027,6 +16241,15 @@ snapshots: - bare-buffer - react-native-b4a + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + teex@1.0.1: dependencies: streamx: 2.26.0 @@ -16726,6 +16949,10 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + wif@2.0.6: dependencies: bs58check: 2.1.2 @@ -16916,6 +17143,8 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + yaml@2.9.0: {} yargs-parser@18.1.3: diff --git a/src/app/api/auth/__tests__/email-verification-routes.test.ts b/src/app/api/auth/__tests__/email-verification-routes.test.ts index d66c76a3..d40539bb 100644 --- a/src/app/api/auth/__tests__/email-verification-routes.test.ts +++ b/src/app/api/auth/__tests__/email-verification-routes.test.ts @@ -2,8 +2,8 @@ import { describe, expect, it, beforeEach, vi } from 'vitest'; import { GET as verifyGET, POST as verifyPOST } from '../email-verification/verify/route'; import { POST as resendPOST } from '../email-verification/resend/route'; import { POST as restorePOST } from '../email-verification/restore/route'; -import { POST as signupPOST } from '../../signup/route'; -import { POST as loginPOST } from '../../login/route'; +import { POST as signupPOST } from '../signup/route'; +import { POST as loginPOST } from '../login/route'; vi.mock('@/lib/ratelimit', () => ({ withRateLimit: vi.fn(() => ({ diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 3afd4241..82299b65 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -32,9 +32,7 @@ export async function POST( const credentialsMatch = await bcrypt.compare(password, passwordHash); if (!user || !credentialsMatch) { - return addHeaders( - NextResponse.json({ message: 'Invalid credentials' }, { status: 401 }), - ); + return addHeaders(NextResponse.json({ message: 'Invalid credentials' }, { status: 401 })); } const verification = await getVerificationStatus(email);