Skip to main content

Password

๐Ÿ“– Overviewโ€‹

The password service is responsible for both halves of the password lifecycle: issuing reset emails and accepting new credentials. It coordinates JWT token generation, email delivery via SendGrid, Wasabi-hosted assets, and database updates that invalidate stale sessions. Every branch is carefully guarded with badRequest/notFound errors so invalid tokens or stale requests stop early without revealing user state.

File Path: internal/api/v1/auth/services/password.service.js

๐Ÿ”„ Data Flowโ€‹

flowchart TD
A[๐Ÿ“ฅ Reset Request] --> B{Type}
B -->|email| C[Load account]
B -->|reset| C
C --> D[Find active user]
D -->|email| E[Generate JWT + store reset token]
E --> F[Build email payload]
F --> G[Send email via sendEmail]
D -->|reset| H[Validate stored reset token]
H --> I[Hash new password]
I --> J[Update user + clear token]
J --> K[Delete active API sessions]
K --> L[๐Ÿ“ค Return status]

๐Ÿ’พ Database Operationsโ€‹

Collections Usedโ€‹

  • _accounts โ€“ Confirms account status and supplies branding metadata for emails.
    • Operations: Read, Update
    • Model: shared/models/account.js
  • _users โ€“ Stores reset tokens and hashed passwords.
    • Operations: Read, Update
    • Model: shared/models/user.js
  • _api.sessions โ€“ Cleared after a password change/reset to force re-authentication.
    • Operations: Delete Many
    • Model: shared/models/api-session.js

๐Ÿ”ง Business Logic & Functionsโ€‹

Core Functionsโ€‹

reset({ type, email, account, password, token, auth })โ€‹

Purpose: Drives both the reset-email flow and the actual password change once the user submits a valid token.

Parameters:

  • type (String) โ€“ Either 'email' to send a reset email or 'reset' to submit a new password.
  • email (String) โ€“ User email required for the email request path.
  • account (String|ObjectId) โ€“ Account ID used to scope user lookup.
  • password (String) โ€“ New password submitted with the reset type.
  • token (String) โ€“ JWT token issued during the email step, required for reset.
  • auth (Object) โ€“ Current authenticated context, used to inherit branding colors when sending emails.

Returns: Promise<{ message: String, additional_info?: Object }>

  • message โ€“ Either PASSWORD_RESET_EMAIL_SENT or PASSWORD_RESET.
  • additional_info โ€“ Raw response from sendEmail (email path only).

Business Logic Flow:

  1. Token Validation (reset path)
    • Verifies the provided JWT using APP_SECRET, extracting account and email from the payload.
    • Rejects invalid or expired tokens with badRequest.
  2. Account Lookup
    • Fetches the target account with business branding to personalize the email.
    • Throws notFound if the account is inactive or missing.
  3. User Lookup
    • Finds an active user with matching account/email, selecting reset_token and verified fields.
    • Throws notFound when no active user is found.
  4. Email Path (type === 'email')
    • Signs a new JWT and stores it as reset_token on the user.
    • Resolves the account logo from Wasabi (if present) and builds a SendGrid template payload.
    • Sends the email using sendEmail; logs and throws badRequest if SendGrid returns an error.
  5. Reset Path (type === 'reset')
    • Confirms that the stored reset_token matches the submitted token.
    • Hashes the new password with a random salt using newHash.
    • Updates the user with the new hash, clears the reset token, and stamps verified if needed.
    • Deletes all ApiSession records for the user and clears pending Account.token values.

Error Handling:

  • Uses badRequest for invalid JWT tokens, mismatched reset tokens, or failed email dispatches.
  • Uses notFound when the account or user cannot be located, or when no reset token exists.

Dependencies:

  • sendEmail, getActiveDomain, Wasabi, and logger for outbound communications.
  • jwt for short-lived token creation/verification.

Example Usage:

await passwordService.reset({
type: 'email',
email: 'owner@example.com',
account: accountId,
auth: req.auth,
});
// -> { message: 'PASSWORD_RESET_EMAIL_SENT', additional_info: {...} }

Side Effects:

  • โš ๏ธ Writes reset_token to the user document.
  • โš ๏ธ Sends transactional email via SendGrid.
  • โš ๏ธ Clears API sessions and account tokens when passwords are changed.

change({ current_password, new_password, auth })โ€‹

Purpose: Allows an authenticated user to rotate their password from within the platform.

Parameters:

  • current_password (String) โ€“ Existing password provided by the user.
  • new_password (String) โ€“ Replacement password to _store.
  • auth (Object) โ€“ Injected authentication context containing the current user document.

Returns: Promise<void>

Business Logic Flow:

  1. Verification
    • Compares the supplied current_password against auth.user.password using verifyHash.
    • Throws badRequest if the hash comparison fails.
  2. Hashing
    • Generates a new salt and hashes the new_password with newHash.
  3. Persistence
    • Updates the user document with the new hash.
    • Purges every ApiSession linked to the user to enforce reauthentication on all devices.

Error Handling:

  • badRequest('The provided password was incorrect') when the current password hash check fails.

Dependencies:

  • verifyHash, newHash, and ApiSession.deleteMany.

Example Usage:

await passwordService.change({
current_password: 'oldPass123!',
new_password: 'StrongerPass456!',
auth: req.auth,
});

Side Effects:

  • โš ๏ธ Logs the user out everywhere by deleting their API sessions.

๐Ÿ”€ Integration Pointsโ€‹

Internal Dependenciesโ€‹

  • internal/api/v1/auth/controllers/password.controller.js โ€“ Calls reset and change directly after request validation.
  • internal/api/v1/auth/routes/password.routes.js โ€“ Applies Joi validation (schemas/password.js) and access guards before delegation.

External Servicesโ€‹

  • SendGrid โ€“ Sends password reset emails via sendEmail and template d-f506f1632d514fab84babdcfcc092150.
  • Wasabi โ€“ Supplies public URLs for stored branding assets.

๐ŸŽจ Data Transformation Pipelineโ€‹

graph LR
A[๐Ÿ“ฅ Inputs] --> B[Validate token or current password]
B --> C[Fetch account & user]
C --> D{Branch}
D -->|Email| E[Generate JWT reset token]
E --> F[Send transactional email]
D -->|Reset| G[Hash new password]
G --> H[Persist updates]
H --> I[Clear ApiSession documents]
I --> J[๐Ÿ“ค Final response]

๐Ÿงช Edge Cases & Special Handlingโ€‹

Case 1: Expired or tampered reset tokenโ€‹

Condition: JWT verification throws because the token is expired or modified. Handling: badRequest('The reset token provided is not valid.') is raised and no database changes occur.

Case 2: Account pending verification tokenโ€‹

Condition: Account document has a legacy token field from onboarding. Handling: When a password reset succeeds the service clears account.token and stamps verified to prevent future reuse.

Case 3: SendGrid outageโ€‹

Condition: sendEmail returns { error }. Handling: Logs the payload and error, then throws badRequest('The reset password email could not be sent.') so callers can retry later.

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ”’ Security: All passwords are rehashed with a fresh salt every time to mitigate rainbow-table attacks.
  • ๐Ÿšจ Session Hygiene: Both reset and change trigger ApiSession.deleteMany to avoid stale refresh tokens.
  • ๐Ÿ’ก Branding: Email theming respects auth.account.branding.colors.primary when available, maintaining white-label consistency.
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM