BIO.RE
Authentication

Change Password

Authenticated password change. Requires current password. All sessions invalidated on success.

POST /api/v1/auth/change-password — 🔑 User-auth (Bearer JWT) · Rate limit: 3 req / hour

Authenticated password change. Verifies the current password before applying the new one. All existing sessions are revoked on success — user is logged out everywhere except the device that performed the change.

This endpoint requires a valid JWT (Authorization: Bearer <accessToken>). It overrides the controller-level @Public() decorator via @SetMetadata(IS_PUBLIC_KEY, false).

Request

Headers

HeaderValueNotes
AuthorizationBearer <accessToken>Required — JWT from /auth/login
Content-Typeapplication/jsonRequired

Body — ChangePasswordDto

FieldTypeRequiredValidationNotes
currentPasswordstringmin 1 charbcrypt-compared against User.passwordHash
newPasswordstring8–128 chars; must include upper + lower + digitMust differ from current; bcrypt hashed

Response

200 OKSuccessOnlyResponseDto

{ "success": true }

Errors

HTTPcode / i18nKeyReason
400(DTO validation)New password fails policy (length, character class)
400auth.change_password.same_as_currentNew password matches current password
401auth.change_password.invalid_currentcurrentPassword does not match User.passwordHash
401(no JWT or invalid)Not authenticated
429(throttle)Rate limit exceeded (3 req/hour)

Side effects

  1. Verify currentPassword against User.passwordHash (bcrypt compare).
  2. Confirm new password is different from current (bcrypt compare again).
  3. Hash new password (bcrypt).
  4. Atomic transaction: update User.passwordHash, revoke ALL Session records EXCEPT the current one (the device performing the change stays logged in).
  5. Send security alert email: "Your password was changed on {date} from {device}".
  6. Audit log: auth.change_password.success.

Code samples

curl -X POST https://api.bio.re/api/v1/auth/change-password \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <accessToken>' \
  -d '{
    "currentPassword": "OldP@ss123",
    "newPassword": "NewSecureP@ss456"
  }'
async function changePassword(currentPassword: string, newPassword: string, accessToken: string): Promise<void> {
  const res = await fetch('https://api.bio.re/api/v1/auth/change-password', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${accessToken}`,
    },
    body: JSON.stringify({ currentPassword, newPassword }),
  });
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Change failed'), {
      code: json?.error?.code,
    });
  }
}
import { useMutation } from '@tanstack/react-query';

export function useChangePassword() {
  return useMutation({
    mutationFn: async (input: { currentPassword: string; newPassword: string }) => {
      const res = await fetch('/api/v1/auth/change-password', {
        method: 'POST',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${getAccessToken()}`,
        },
        body: JSON.stringify(input),
      });
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Change failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
    },
    onSuccess: () => {
      toast.success(t('auth.change_password.success'));
    },
  });
}

Try it

POST
/api/v1/auth/change-password
AuthorizationBearer <token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

curl -X POST "https://loading/api/v1/auth/change-password" \  -H "Content-Type: application/json" \  -d '{    "currentPassword": "string",    "newPassword": "stringst"  }'
{
  "success": true
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}
{
  "success": false,
  "error": {
    "code": "AUTH_UNAUTHORIZED",
    "message": "Invalid credentials",
    "i18nKey": "auth.login.invalid_credentials",
    "i18nVars": {
      "field": "email"
    },
    "details": [
      {
        "message": "email must be an email"
      }
    ],
    "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/auth/auth.controller.ts225–243
DTO (request)apps/api-core/src/modules/auth/dto/index.ts123–135 (ChangePasswordDto)
Serviceapps/api-core/src/modules/auth/auth.service.tschangePassword()
Prisma modelspackages/prisma/prisma/schema.prismaUser, Session

On this page