services/api/src/modules/auth/auth.validation.js
Overview
This file centralizes the input validation rules for the authentication module. It normalizes request bodies and rejects invalid values by throwing ApiError with 400 status codes and specific error metadata.
It supports the auth flows that need strict, deterministic input checks before the controller delegates to the service layer. The file covers registration, sign-in, Google credential handling, account deletion, account restoration, and phone verification.
[!IMPORTANT] This file does not expose HTTP endpoints. It only validates data that arrives from
auth.controller.jsbefore service execution. fileciteturn0file8turn0file9
How it works
The module follows a simple pattern: it extracts values from body, normalizes them, validates them against fixed rules, and returns a cleaned object. If any rule fails, it throws ApiError immediately.
The validation functions are designed to be consumed by the auth controller. In the documented auth flow, the controller calls validateRegisterInput and validateSignInInput before invoking the service layer. fileciteturn0file8turn0file9
flowchart TD
Body[Request body] --> Register[validateRegisterInput]
Body --> GoogleAuth[validateGoogleAuthInput]
Body --> GoogleReauth[validateGoogleAccountDeletionReauthInput]
Body --> PasswordCreate[validatePasswordCreateInput]
Body --> SignIn[validateSignInInput]
Body --> Delete[validateDeleteAccountInput]
Body --> RestoreResend[validateAccountRestorationResendInput]
Body --> Restore[validateRestoreAccountInput]
Body --> PhoneStart[validatePhoneVerificationStartInput]
Body --> PhoneCheck[validatePhoneVerificationCheckInput]
Register --> CleanRegister[Normalized registration payload]
GoogleAuth --> CleanGoogle[Credential only]
GoogleReauth --> CleanGoogleReauth[Credential only]
PasswordCreate --> CleanPasswordCreate[Token and new password]
SignIn --> CleanSignIn[Email and password]
Delete --> CleanDelete[Deletion confirmation payload]
RestoreResend --> CleanResend[Email only]
Restore --> CleanRestore[Token and ALTCHA payload]
PhoneStart --> CleanPhoneStart[Phone only]
PhoneCheck --> CleanPhoneCheck[Phone and verification code]
Register -.-> Error[ApiError 400]
GoogleAuth -.-> Error
GoogleReauth -.-> Error
PasswordCreate -.-> Error
SignIn -.-> Error
Delete -.-> Error
RestoreResend -.-> Error
Restore -.-> Error
PhoneStart -.-> Error
PhoneCheck -.-> Error
The main design is deterministic. Each validator has a fixed contract, and each failure returns a predictable error message or code.
[!NOTE]
validateRegisterInputbuildsnamefromfirstNameandlastNamewhen the body does not providename.
Key components
External dependency
{
"title": "ApiError",
"content": "Exception class used to reject invalid auth input with HTTP 400 and structured error metadata.",
"type": "class",
"filePath": "services/api/src/utils/apiError.js",
"badges": [
"public"
]
}
ApiError is the error carrier used by every validator in this file. The validators use it to report bad requests and, in some cases, structured fields and codes.
Internal helper
{
"title": "normalizeUsername",
"content": "Normalizes usernames by trimming whitespace, removing leading @ characters, and converting the value to lowercase.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
Although it is not exported, this helper is central to username handling. It ensures the same username normalization logic is used every time registration validates a username.
Exported validators
{
"title": "validateRegisterInput",
"content": "Validates and normalizes registration data, including identity fields, password rules, role restrictions, and ALTCHA proof.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateGoogleAuthInput",
"content": "Validates that the Google credential is present and returns it in a normalized object.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateGoogleAccountDeletionReauthInput",
"content": "Validates the Google credential used to issue account-deletion reauthentication.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validatePasswordCreateInput",
"content": "Validates the Google reauth token and the new password used to create a password for a Google-only user.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateSignInInput",
"content": "Validates email and password for sign-in requests and returns the normalized credentials.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateDeleteAccountInput",
"content": "Validates account deletion confirmation, ALTCHA proof, and optional deletion grace preferences.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateAccountRestorationResendInput",
"content": "Validates the email used to resend an account restoration link.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validateRestoreAccountInput",
"content": "Validates the restoration token and ALTCHA proof used to restore an account.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validatePhoneVerificationStartInput",
"content": "Validates the phone number used to start a phone verification flow.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
{
"title": "validatePhoneVerificationCheckInput",
"content": "Validates the phone number and verification code used to complete phone verification.",
"type": "function",
"filePath": "services/api/src/modules/auth/auth.validation.js",
"badges": [
"public"
]
}
Data & fields
Registration payload
[
{
"name": "firstName",
"type": "string",
"required": true,
"description": "Trimmed first name. It must exist and contain at least 2 characters."
},
{
"name": "lastName",
"type": "string",
"required": true,
"description": "Trimmed last name. It must exist and contain at least 2 characters."
},
{
"name": "name",
"type": "string",
"description": "Full name. If missing, the validator builds it from firstName and lastName."
},
{
"name": "email",
"type": "string",
"required": true,
"description": "Lowercased email validated against the module email pattern."
},
{
"name": "phone",
"type": "string",
"required": true,
"description": "Phone number normalized to E.164 before validation."
},
{
"name": "username",
"type": "string",
"description": "Optional username normalized to lowercase without leading @ characters."
},
{
"name": "password",
"type": "string",
"required": true,
"description": "Password that must satisfy the password strength regex."
},
{
"name": "passwordConfirm",
"type": "string",
"required": true,
"description": "Confirmation password that must match password exactly."
},
{
"name": "accountRole",
"type": "string",
"description": "Account type. Only creator and seeker are accepted. Defaults to creator."
},
{
"name": "altchaPayload",
"type": "string",
"required": true,
"description": "ALTCHA proof required to pass the anti-robot check."
},
{
"name": "role",
"type": "string",
"description": "Disallowed in public registration. Its presence causes an error."
},
{
"name": "systemRole",
"type": "string",
"description": "Disallowed in public registration. Its presence causes an error."
}
]
Google authentication payload
[
{
"name": "credential",
"type": "string",
"required": true,
"description": "Google credential token used by the auth flow."
}
]
Google reauthentication payload for account deletion
[
{
"name": "credential",
"type": "string",
"required": true,
"description": "Google credential token reused to issue the deletion reauth token."
}
]
Password creation payload
[
{
"name": "googleReauthToken",
"type": "string",
"required": true,
"description": "Token proving the user reauthenticated with Google."
},
{
"name": "newPassword",
"type": "string",
"required": true,
"description": "New password that must contain at least 8 characters."
}
]
Sign-in payload
[
{
"name": "email",
"type": "string",
"required": true,
"description": "Email lowercased before validation."
},
{
"name": "password",
"type": "string",
"required": true,
"description": "Password required for sign-in."
}
]
Account deletion payload
[
{
"name": "currentPassword",
"type": "string",
"description": "Password used when the account uses password authentication."
},
{
"name": "googleReauthToken",
"type": "string",
"description": "Token used when the account deletion flow depends on Google reauthentication."
},
{
"name": "confirmation",
"type": "string",
"required": true,
"description": "Must equal ELIMINAR exactly."
},
{
"name": "altchaPayload",
"type": "string",
"required": true,
"description": "ALTCHA proof required for the deletion request."
},
{
"name": "deletionGracePreferences",
"type": "object",
"description": "Optional allowlist of account deletion grace preferences."
}
]
[
{
"name": "keepPublishedListingsVisible",
"type": "boolean",
"description": "Controls whether published listings stay visible during the grace period."
},
{
"name": "allowLeadCapture",
"type": "boolean",
"description": "Controls whether lead capture remains available during the grace period."
},
{
"name": "allowComments",
"type": "boolean",
"description": "Controls whether comments remain available during the grace period."
}
]
The validator only merges these three fields. Any other key is ignored silently.
Account restoration payloads
[
{
"name": "token",
"type": "string",
"required": true,
"description": "Restoration token required to restore the account."
},
{
"name": "altchaPayload",
"type": "string",
"required": true,
"description": "ALTCHA proof required for restoration."
}
]
[
{
"name": "email",
"type": "string",
"required": true,
"description": "Email used to resend a restoration link."
}
]
Phone verification payloads
[
{
"name": "phone",
"type": "string",
"required": true,
"description": "Phone number normalized and checked as E.164."
}
]
[
{
"name": "phone",
"type": "string",
"required": true,
"description": "Phone number normalized and checked as E.164."
},
{
"name": "code",
"type": "string",
"required": true,
"description": "Verification code required to complete phone verification."
}
]
Behaviors
normalizeUsername— trims input, removes leading@characters, lowercases the value, and returns a normalized string.validateRegisterInput— validates the public registration payload, enforces name, email, phone, username, password, account role, and ALTCHA rules, and returns a normalized object.validateGoogleAuthInput— ensures the Google credential exists and returns{ credential }.validateGoogleAccountDeletionReauthInput— validates the same Google credential shape used to issue account-deletion reauth tokens.validatePasswordCreateInput— requires a Google reauth token and a new password of at least 8 characters.validateSignInInput— checks email format and requires a non-empty password.normalizeDeletionGracePreferences— merges only the allowed deletion grace preference booleans and ignores any other keys.validateDeleteAccountInput— checks the confirmation keyword, ALTCHA payload, and optional deletion grace preferences, then returns a normalized object.validateAccountRestorationResendInput— validates the email used to resend account restoration links.validateRestoreAccountInput— requires both the restoration token and ALTCHA payload.validatePhoneVerificationStartInput— validates a phone number in E.164 format.validatePhoneVerificationCheckInput— validates the phone number and the verification code.
[!NOTE]
validateGoogleAuthInputandvalidateGoogleAccountDeletionReauthInputhave the same field contract. The file separates them by intent, not by data shape. fileciteturn0file8turn0file9
How the validators are used in the auth flow
The auth controller imports these validators and runs them before service calls. The repository context shows the controller using validateRegisterInput and validateSignInInput as the first gate in the registration and sign-in path. fileciteturn0file8turn0file9
sequenceDiagram
participant Client
participant Controller as auth.controller.js
participant Validation as auth.validation.js
participant Service as auth.service.js
Client->>Controller: Auth request body
Controller->>Validation: validate*Input
Validation-->>Controller: Clean data or ApiError
Controller->>Service: Domain operation
Service-->>Controller: Result
Controller-->>Client: HTTP response
The pattern is consistent across the module. The controller receives req.body, the validator normalizes and checks it, and the service receives only validated data.
Gotchas & edge cases
[!WARNING]
validateRegisterInputrejects public registration ifroleorsystemRoleis present. The error includesADMIN_ROLE_NOT_ALLOWEDand points to the offending field.
[!WARNING]
validateRegisterInputrequiresaltchaPayload. If it is empty, registration fails with a 400 error.
[!CAUTION]
usernameis optional, but when present it must match the allowed pattern and length limits. The validator also strips leading@characters before checking it.
[!CAUTION]
normalizeDeletionGracePreferencesignores every key except the three allowed boolean preferences. Extra client-provided keys do not survive normalization.
[!WARNING]
validateDeleteAccountInputrequires the confirmation string to be exactlyELIMINAR. Any other value is rejected.
[!WARNING]
validateRestoreAccountInputrequires bothtokenandaltchaPayload. Missing either value produces a 400 error.
[!WARNING] The phone validators require E.164-compatible input after normalization. Invalid phone formatting fails before any downstream operation can continue.
Related
This file is tightly connected to the rest of the auth module.
services/api/src/modules/auth/auth.controller.jsconsumes these validators before calling service methods. fileciteturn0file4turn0file5services/api/src/utils/apiError.jsprovides the exception type used for all validation failures.services/api/src/utils/phone.jssuppliesnormalizePhoneE164andisValidE164Phone, which underpin all phone-related validation.services/api/src/modules/auth/auth.routes.jsdefines the HTTP surface that reaches the controller and, indirectly, this validation layer. fileciteturn0file8turn0file9services/api/src/config/altcha.jsprovides the ALTCHA challenge and consumption flow used by registration and other sensitive operations. fileciteturn0file4turn0file5
[!IMPORTANT] This file is a pure validation boundary. It does not persist data, create sessions, verify tokens, or call external APIs directly.