Skip to main content
POST
/
auth
/
password
Change Password
curl --request POST \
  --url https://auth.nullpass.xyz/api/auth/password \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "currentPassword": "<string>",
  "newPassword": "<string>",
  "verificationCode": "<string>"
}
'

Endpoint

POST /api/auth/password

Overview

Changes the authenticated user’s password. Requires the current password for verification. Password is hashed with bcrypt before storage.

Request

currentPassword
string
required
Current password for verification
newPassword
string
required
New password. Minimum 8 characters.

Response

success
boolean
Always true on success
message
string
“Password changed successfully”

Implementation Details

Process Flow

  1. Authentication: Verifies user is authenticated
  2. Current Password Check: Compares provided password with stored hash
  3. Password Hashing: Hashes new password with bcrypt (10 rounds)
  4. Update: Updates password hash in database
  5. Audit Logging: Logs PASSWORD_CHANGE event

Code Reference

export async function POST(request: NextRequest) {
  const corsResponse = handleCors(request)
  if (corsResponse) return corsResponse

  const blocked = await protectRoute(request, { requested: 2 })
  if (blocked) return blocked

  const auth = await requireAuth(request)
  if ('error' in auth) return auth.error

  try {
    const body = await request.json()
    const validated = changePasswordSchema.parse(body)

    const user = await prisma.user.findUnique({
      where: { id: auth.userId },
      select: {
        id: true,
        passwordHash: true,
      },
    })

    if (!user || !user.passwordHash) {
      return errorResponse('User not found', 404, request.headers.get('origin'))
    }

    const isValid = await bcrypt.compare(validated.currentPassword, user.passwordHash)
    if (!isValid) {
      logger.warn('Password change failed: Invalid current password', auth.userId)
      return errorResponse('Invalid current password', 401, request.headers.get('origin'))
    }

    const newPasswordHash = await bcrypt.hash(validated.newPassword, 10)

    await prisma.user.update({
      where: { id: auth.userId },
      data: {
        passwordHash: newPasswordHash,
      },
    })

    await createAuditLog(auth.userId, 'PASSWORD_CHANGE', {})

    return jsonResponse({ success: true, message: 'Password changed successfully' }, 200, request.headers.get('origin'))
  } catch (error: any) {
    if (error.name === 'ZodError') {
      logger.warn('Password change validation error:', error.errors)
      return errorResponse(error.errors[0].message, 400, request.headers.get('origin'))
    }
    logger.error('Change password error:', error)
    return errorResponse('Internal server error', 500, request.headers.get('origin'))
  }
}

Status Codes

200
OK
Password changed successfully
400
Bad Request
Validation error (new password too short, etc.)
401
Unauthorized
Invalid current password or missing authentication
404
Not Found
User not found
403
Forbidden
Blocked by Arcjet

Example Request

curl -X POST https://auth.nullpass.xyz/api/auth/password \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "currentPassword": "oldpassword123",
    "newPassword": "newsecurepassword456"
  }'

Example Response

{
  "success": true,
  "message": "Password changed successfully"
}

Security Considerations

  • Current password must be verified before change
  • New password is hashed with bcrypt (10 rounds)
  • Rate limiting applied (2 requests per bucket)
  • All password changes are logged in audit trail
  • Old password hash is completely replaced (no history kept)

Audit Events

  • PASSWORD_CHANGE: Password successfully changed

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
currentPassword
string
required
newPassword
string
required
Minimum string length: 8
verificationCode
string

Required if 2FA is enabled

Response

200

Password changed successfully