Skip to content

feat: implement authentication module using clean architecture with Login and Register use cases - #1730

Open
TanCodeX wants to merge 3 commits into
Canopus-Labs:mainfrom
TanCodeX:feature/hexagonal-backend-architecture
Open

feat: implement authentication module using clean architecture with Login and Register use cases#1730
TanCodeX wants to merge 3 commits into
Canopus-Labs:mainfrom
TanCodeX:feature/hexagonal-backend-architecture

Conversation

@TanCodeX

@TanCodeX TanCodeX commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

📝 Pull Request Description

Related Issue

Closes #1608

Summary

Migrates the authentication domain toward a Hexagonal Architecture (Ports and Adapters) by separating business logic from Express and Mongoose dependencies.

This first phase introduces a clean domain/application/infrastructure/presentation structure under backend/src/, while keeping the existing authentication routes backward compatible.

Key changes include:

  • Extracted authentication business logic into RegisterUser and LoginUser use cases.
  • Added a pure UserEntity without Mongoose dependencies.
  • Introduced repository and email service ports.
  • Added Mongoose, Nodemailer, and JWT infrastructure adapters.
  • Added a new HTTP authentication controller and composition root.
  • Updated the existing /register and /login routes to use the new hexagonal implementation.
  • Added isolated Vitest unit tests using in-memory mocks, removing the need for MongoDB during use-case testing.

This establishes a reusable blueprint for migrating additional authentication endpoints and future domains to Hexagonal Architecture.


Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature
  • ♻️ Refactoring
  • 📝 Documentation update
  • 🎨 UI/UX improvement
  • 🔥 Other (please describe) — Architecture migration

How Has This Been Tested?

Describe the testing steps performed.

  • Added isolated unit tests for RegisterUser and LoginUser using Vitest.
  • Used in-memory repository mocks to verify application logic independently of MongoDB.
  • Verified the new hexagonal authentication controller integrates correctly with the existing Express routes.
  • Verified existing validation middleware and response shapes remain compatible.
  • Ran the complete backend test suite.
  • Validation result: 136 backend tests passing.
  • Confirmed the /register and /login endpoints continue to work through the existing authentication router.

Screenshots (if applicable)

Not applicable — this PR contains backend architecture and testing changes.


Checklist

  • My code follows the project's guidelines
  • I have tested my changes
  • I have updated documentation where necessary
  • I have linked the related issue
  • My changes do not introduce new warnings or errors

Looks good to me. Ready to merge.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds a hexagonal authentication module. It separates domain entities, application use cases, ports, infrastructure adapters, and HTTP presentation. Registration and login now use the new controller and dependency wiring.

Changes

Hexagonal authentication

Layer / File(s) Summary
Domain entities and ports
backend/src/auth/domain/entities/UserEntity.js, backend/src/shared/domain/BaseError.js, backend/src/auth/application/ports/*
Adds UserEntity, shared domain errors, and repository and email-service ports.
Registration and login use cases
backend/src/auth/application/useCases/*
Adds registration and login workflows with validation, verification handling, password checks, token persistence, and unit tests.
Persistence and service adapters
backend/src/auth/infrastructure/...
Adds MongoDB user persistence, verification email delivery, and access and refresh token adapters.
HTTP presentation and module wiring
backend/src/auth/presentation/..., backend/src/auth/index.js, backend/routes/authRoutes.js
Adds the controller and validated routes, wires dependencies, and routes /register and /login through the new controller.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AuthController
  participant RegisterUser
  participant UserRepositoryMongoImpl
  participant EmailServiceImpl

  Client->>AuthController: POST /register
  AuthController->>RegisterUser: execute({name, email, password, frontendUrl})
  RegisterUser->>UserRepositoryMongoImpl: findByEmail(email)
  UserRepositoryMongoImpl-->>RegisterUser: UserEntity or no user
  RegisterUser->>UserRepositoryMongoImpl: save(UserEntity)
  RegisterUser->>EmailServiceImpl: sendVerificationEmail(to, verificationUrl)
  AuthController-->>Client: 201 registration response
Loading

Possibly related PRs

Suggested labels: level:advanced, type:refactor

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the authentication refactor and identifies the Login and Register use cases.
Linked Issues check ✅ Passed The changes implement the hexagonal architecture refactor in issue [#1608] through ports, adapters, use cases, and isolated tests.
Out of Scope Changes check ✅ Passed The changes are related to the authentication architecture refactor and its required use cases, adapters, routes, errors, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/auth/application/useCases/RegisterUser.js`:
- Around line 34-39: Update both email-delivery catch blocks in RegisterUser.js
at lines 34-39 and 71-76: do not suppress sendVerificationEmail failures after
persisting a verification token. Either enqueue durable retry work for the
replacement and initial verification emails, or propagate a retriable error so
registration is not reported as complete without a usable delivery path.
- Around line 23-24: Update the registration flow around RegisterUser to
eliminate the check-then-save race: retain the lookup only as an early
optimization, but catch the User model’s duplicate-email constraint error from
the save operation and return the existing generic registration response;
alternatively, use an atomic repository operation that handles lookup and
creation together.

In `@backend/src/auth/domain/entities/UserEntity.js`:
- Around line 49-62: Update the profileDetails initialization in the UserEntity
constructor so the top-level profileDetails values are spread before assigning
socials. Then construct socials by merging the default social fields with
profileDetails.socials, preserving defaults when persisted data provides only
partial socials.

In `@backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js`:
- Around line 44-46: Update the existing-user branch in UserRepositoryMongoImpl
to remove upsert from findByIdAndUpdate options, then detect a null update
result and throw the repository’s established not-found or concurrency error
before returning. Preserve the normal updated-document flow for existing users
found successfully.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a61f931-25ba-41ad-b4ee-bcb0ceca548f

📥 Commits

Reviewing files that changed from the base of the PR and between a8a7be0 and b3db17c.

📒 Files selected for processing (15)
  • backend/routes/authRoutes.js
  • backend/src/auth/application/ports/IEmailService.js
  • backend/src/auth/application/ports/IUserRepository.js
  • backend/src/auth/application/useCases/LoginUser.js
  • backend/src/auth/application/useCases/LoginUser.unit.test.js
  • backend/src/auth/application/useCases/RegisterUser.js
  • backend/src/auth/application/useCases/RegisterUser.unit.test.js
  • backend/src/auth/domain/entities/UserEntity.js
  • backend/src/auth/index.js
  • backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js
  • backend/src/auth/infrastructure/services/EmailServiceImpl.js
  • backend/src/auth/infrastructure/services/TokenServiceImpl.js
  • backend/src/auth/presentation/controllers/AuthController.js
  • backend/src/auth/presentation/routes/authRoutesHex.js
  • backend/src/shared/domain/BaseError.js

Comment thread backend/src/auth/application/useCases/RegisterUser.js
Comment thread backend/src/auth/application/useCases/RegisterUser.js Outdated
Comment thread backend/src/auth/domain/entities/UserEntity.js
Comment thread backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js Outdated
@github-actions github-actions Bot added the merge ready PR is mergeable and has no conflicts label Aug 9, 2026
@KaranUnique

Copy link
Copy Markdown
Contributor

@TanCodeX address the coderabbit suggestions

@TanCodeX

Copy link
Copy Markdown
Contributor Author

@TanCodeX address the coderabbit suggestions

@KaranUnique Done, please review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/auth/application/useCases/RegisterUser.js (1)

27-35: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Prevent concurrent verification refreshes from invalidating tokens.

When two requests find the same unverified user, each request saves a different token hash and sends its matching raw token. The later save can complete before the earlier email send. The stored hash can then differ from the token in the delivered email, so the user cannot verify the account.

Serialize token refresh and delivery, or use an outbox/versioned operation that never sends a superseded token.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/auth/application/useCases/RegisterUser.js` around lines 27 - 35,
Update the unverified-user branch in RegisterUser so concurrent registration
requests cannot overwrite a verification token while another request is
delivering its email. Serialize token generation, persistence, and
sendVerificationEmail for each user, or implement an outbox/version check that
prevents delivery of superseded tokens while preserving matching stored hashes
and raw tokens.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/auth/application/useCases/RegisterUser.js`:
- Around line 68-73: Update the ConflictError handling in RegisterUser so it
returns alreadyRegistered only when the underlying duplicate-key conflict
targets the email field. Inspect the error’s duplicate-key details or preserved
field metadata, and rethrow conflicts involving prepPilotId or any other field.

---

Outside diff comments:
In `@backend/src/auth/application/useCases/RegisterUser.js`:
- Around line 27-35: Update the unverified-user branch in RegisterUser so
concurrent registration requests cannot overwrite a verification token while
another request is delivering its email. Serialize token generation,
persistence, and sendVerificationEmail for each user, or implement an
outbox/version check that prevents delivery of superseded tokens while
preserving matching stored hashes and raw tokens.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 00cabd39-64b9-4f6a-ae56-2790a66ef781

📥 Commits

Reviewing files that changed from the base of the PR and between b3db17c and 24b24e6.

📒 Files selected for processing (3)
  • backend/src/auth/application/useCases/RegisterUser.js
  • backend/src/auth/domain/entities/UserEntity.js
  • backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js
  • backend/src/auth/domain/entities/UserEntity.js

Comment thread backend/src/auth/application/useCases/RegisterUser.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/auth/application/useCases/RegisterUser.js`:
- Around line 33-40: Make token freshness and verification-email delivery atomic
in the registration flow around the user repository save and
emailService.sendVerificationEmail call. Replace the separate findByEmail check
with a repository/email-port operation that conditionally delivers only when the
saved token version remains current, or preserve every unexpired verification
token until expiry; add a concurrency test covering a token replacement between
the freshness check and delivery.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66f6a3ce-f189-4eb2-90b3-d7c813617952

📥 Commits

Reviewing files that changed from the base of the PR and between 24b24e6 and 92350fe.

📒 Files selected for processing (3)
  • backend/src/auth/application/useCases/RegisterUser.js
  • backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js
  • backend/src/shared/domain/BaseError.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/shared/domain/BaseError.js
  • backend/src/auth/infrastructure/database/UserRepositoryMongoImpl.js

Comment on lines +33 to +40
await this.userRepository.save(userExists);

// Outbox/version check to prevent delivery of superseded tokens
const latestUser = await this.userRepository.findByEmail(cleanEmail);
if (latestUser && latestUser.emailVerificationToken === hashedToken) {
const verificationUrl = `${frontendUrl}/verify-email?token=${rawToken}`;
await this.emailService.sendVerificationEmail(userExists.email, verificationUrl);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift

Make token freshness atomic with email delivery.

The save at Line 33, re-read at Line 36, and email send at Line 39 are separate operations. A concurrent request can replace the token after Line 36 returns and before Line 39 runs. The first request can then send a token that verification no longer accepts.

Use a repository/email port operation that couples token versioning to delivery, or keep each unexpired verification token valid until expiry. Add a concurrency test for this interleaving.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/auth/application/useCases/RegisterUser.js` around lines 33 - 40,
Make token freshness and verification-email delivery atomic in the registration
flow around the user repository save and emailService.sendVerificationEmail
call. Replace the separate findByEmail check with a repository/email-port
operation that conditionally delivers only when the saved token version remains
current, or preserve every unexpired verification token until expiry; add a
concurrency test covering a token replacement between the freshness check and
delivery.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge ready PR is mergeable and has no conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Architecture: Hexagonal Architecture Refactoring

2 participants