Skip to content

feat(auth): migrate authentication to better-auth#51

Merged
iamkabelomoobi merged 1 commit into
mainfrom
feat/migrate-to-better-auth
Jun 6, 2026
Merged

feat(auth): migrate authentication to better-auth#51
iamkabelomoobi merged 1 commit into
mainfrom
feat/migrate-to-better-auth

Conversation

@iamkabelomoobi

@iamkabelomoobi iamkabelomoobi commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Migrate authentication to Better Auth with session-based authorization, update user and profile models to centralize identity data, and add end-to-end coverage for the new auth flows.

New Features:

  • Expose Better Auth-based auth routes under /api/auth with email/password sign-up, sign-in, email verification, and password reset flows.
  • Add centralized user identity fields (name, image, emailVerified) and role-aware profile creation hooks backed by Prisma and Better Auth.
  • Introduce end-to-end auth tests that exercise sign-up, verification, sign-in, role-based access, and password reset using Maildev and a dedicated e2e server harness.

Bug Fixes:

  • Fix employer, admin, and candidate services and repositories to handle optional user IDs, default pagination, and consistent cache key usage.
  • Correct various DTOs, templates, and controllers to use the unified user name/image fields and to validate route params more defensively.

Enhancements:

  • Enforce required and optional environment variables via config helpers and add stronger port parsing/validation.
  • Simplify registration and profile validation schemas to use a shared base schema with unified name/password/phone rules and role-specific extensions.
  • Refine application, job, and user repositories and services to remove nullable returns where inappropriate and to align with the new user model.
  • Add richer authentication email templates for verification, welcome, and password update notifications.
  • Replace legacy JWT/password utilities and middleware with Better Auth session checks in the authorization middleware and app wiring.

Build:

  • Add Better Auth and ESLint configuration, wire auth middleware into the Express app, and configure TypeScript and Jest options for the new setup.

CI:

  • Enable Nx analytics and record the package manager version to support the updated toolchain.
  • Add a migration lock file for Prisma to stabilize database migrations across environments.

Deployment:

  • Add Prisma migrations for Better Auth tables and the new user identity fields, and adjust docker-compose Elasticsearch/Kibana container names for this project.

Documentation:

  • Document the Better Auth API surface, example request bodies, and auth behavior in the README, and update repository URLs and badges to the new GitHub org.

Tests:

  • Update existing validator tests to match the new registration and candidate/employer schemas and add Jest e2e configuration for the auth test harness.

Chores:

  • Remove obsolete authentication controllers, routes, utilities, middleware, and tests now replaced by Better Auth.

@sourcery-ai

sourcery-ai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Migrates authentication to Better Auth, replacing custom JWT/password flows, wiring Better Auth into the Express app and Prisma schema, and aligning models/DTOs/validators/tests with the new user/profile model while adding e2e coverage and tightening configuration validation and caching behavior.

Sequence diagram for Better Auth–backed role-protected route

sequenceDiagram
  actor User
  participant Browser
  participant ExpressApp
  participant AuthorizationMiddleware
  participant BetterAuth as BetterAuth_auth.api
  participant Prisma

  User->>Browser: Clicks /api/admins/me
  Browser->>ExpressApp: GET /api/admins/me (cookies)
  ExpressApp->>AuthorizationMiddleware: isAuthorized(['admin'])
  AuthorizationMiddleware->>BetterAuth: getSession(headers: fromNodeHeaders(req.headers))
  BetterAuth->>Prisma: Load session and user via prismaAdapter
  Prisma-->>BetterAuth: Session + user { role }
  BetterAuth-->>AuthorizationMiddleware: session | null
  alt session not found
    AuthorizationMiddleware-->>ExpressApp: sendError(401, 'Authentication required')
    ExpressApp-->>Browser: 401 Unauthorized
  else session found
    alt user.role not in allowedRoles
      AuthorizationMiddleware-->>ExpressApp: sendError(403, 'Access denied: insufficient permissions')
      ExpressApp-->>Browser: 403 Forbidden
    else role allowed
      AuthorizationMiddleware->>ExpressApp: set app.locals { userId, session, user }
      ExpressApp-->>Browser: 200 OK (admin profile)
    end
  end
Loading

Entity relationship diagram for Better Auth migration in Prisma

erDiagram
  Users {
    UUID id PK
    TEXT name
    TEXT email
    BOOLEAN emailVerified
    TEXT image
    TEXT phone
    TEXT password
    TEXT role
    BOOLEAN isVerified
    BOOLEAN isActive
    BOOLEAN isLocked
  }

  session {
    TEXT id PK
    TIMESTAMP expiresAt
    TEXT token
    TIMESTAMP createdAt
    TIMESTAMP updatedAt
    TEXT ipAddress
    TEXT userAgent
    UUID userId FK
  }

  account {
    TEXT id PK
    TEXT accountId
    TEXT providerId
    UUID userId FK
    TEXT accessToken
    TEXT refreshToken
    TEXT idToken
    TIMESTAMP accessTokenExpiresAt
    TIMESTAMP refreshTokenExpiresAt
    TEXT scope
    TEXT password
    TIMESTAMP createdAt
    TIMESTAMP updatedAt
  }

  verification {
    TEXT id PK
    TEXT identifier
    TEXT value
    TIMESTAMP expiresAt
    TIMESTAMP createdAt
    TIMESTAMP updatedAt
  }

  Users ||--o{ session : "has sessions"
  Users ||--o{ account : "has accounts"
  verification }o--|| Users : "indirectly references via identifier (e.g. reset-password:, email)"
Loading

File-Level Changes

Change Details Files
Integrate Better Auth as the authentication system and expose its HTTP API under /api/auth.
  • Add Better Auth configuration using Prisma adapter with extended user fields, email verification, password reset, and role-based profile creation hooks
  • Wire Better Auth request handler into Express via app.middleware and remove legacy authentication routes, middleware, and services
  • Add detailed Auth API documentation to README including endpoints, payload examples, and session/social route descriptions
src/libs/auth.ts
src/middlewares/app.middleware.ts
src/routes/index.ts
src/controllers/index.ts
src/services/index.ts
src/middlewares/authentication.middleware.ts
src/routes/authentication.route.ts
src/services/authentication.service.ts
src/controllers/authentication.controller.ts
src/utils/jwt.util.ts
src/utils/password.util.ts
src/utils/index.ts
README.md
Adapt authorization and user-facing services/controllers to Better Auth sessions and remove app-managed password update flows.
  • Update authorization middleware to derive session and role from Better Auth instead of access_token cookies/JWT utilities and to attach user/session to app.locals
  • Remove user password update endpoint and service method, delegating password changes to Better Auth, and adjust admin/candidate/employer routes and Swagger docs accordingly
  • Expand authentication email templates to support verification, welcome, and password-update notifications and reuse them in Better Auth hooks
src/middlewares/authorization.middleware.ts
src/services/user.service.ts
src/interfaces/services/user.ts
src/controllers/user.controller.ts
src/routes/admin.route.ts
src/routes/candidate.route.ts
src/routes/employer.route.ts
src/templates/authentication.template.ts
Update persistence layer to support Better Auth’s schema (sessions, accounts, verifications) and normalized user identity fields, and propagate those changes through DTOs, models, queries, and services.
  • Extend Users table with name/image/emailVerified and drop avatarUrl plus profile-level name columns; add Better Auth tables (session/account/verification) via Prisma migrations
  • Change user, admin, candidate, employer models/interfaces/DTOs to use user.name/image/emailVerified instead of profile-specific names and avatarUrl; update related queries, job/employer application DTOs, templates, and queue payloads
  • Adjust repositories (user/admin/candidate/employer/job/application) to use the new relations/selects, throw RepositoryError instead of returning null, and adapt job/employer search to the new model
prisma/schema.prisma
prisma/migrations/20260606110417_remove_profile_name_columns/migration.sql
prisma/migrations/20260606120000_remove_profile_name_columns/migration.sql
prisma/migrations/migration_lock.toml
src/interfaces/models/user.model.ts
src/interfaces/models/admin.model.ts
src/interfaces/models/candidate.model.ts
src/interfaces/models/employer.model.ts
src/interfaces/models/job.model.ts
src/interfaces/models/index.ts
src/interfaces/queries/admin.query.ts
src/interfaces/queries/candidate.query.ts
src/interfaces/queries/employer.query.ts
src/dtos/user.dto.ts
src/dtos/admin.dto.ts
src/dtos/candidate.dto.ts
src/dtos/employer.dto.ts
src/dtos/job.dto.ts
src/repositories/prisma.repository.ts
src/repositories/user.repository.ts
src/repositories/admin.repository.ts
src/repositories/candidate.repository.ts
src/repositories/employer.repository.ts
src/repositories/job.repository.ts
src/repositories/application.repository.ts
src/queues/workers/application.worker.ts
src/templates/application/application-created.ejs
src/templates/application/application-rejected.ejs
src/templates/application/application-updated.ejs
src/services/application.service.ts
Align registration and profile validation schemas/tests with Better Auth’s base registration shape (name/email/phone/password) and simplified profile fields.
  • Introduce required name field at base registration level and remove firstName/lastName/company name from candidate/admin/employer schemas and related query filters
  • Adjust candidate/admin/employer validators and unit tests to the new payloads, including title/industry-only profile-specific fields and updated error messages
  • Update pagination query docs and minor type expectations in tests (e.g., job validator, candidate/employer validators)
src/validators/schemas/base-register.schema.ts
src/validators/schemas/candidate-register.schema.ts
src/validators/schemas/admin-register.schema.ts
src/validators/schemas/employer-register.schema.ts
src/validators/schemas/candidate.schema.ts
src/validators/schemas/admin.schema.ts
src/validators/schemas/employer.schema.ts
src/validators/candidate-register.validator.ts
src/__tests__/validators/base-register.spec.ts
src/__tests__/validators/candidate-register.spec.ts
src/__tests__/validators/admin-register.spec.ts
src/__tests__/validators/candidate.spec.ts
src/__tests__/validators/employer.validator.spec.ts
src/__tests__/validators/job.validator.spec.ts
src/interfaces/services/authentication.ts
src/interfaces/queries/pagination.query.ts
Add end-to-end auth test suite plus Jest/E2E harness improvements and local auth test environment setup/teardown.
  • Implement e2e auth spec that exercises sign-up, email verification, role-based protected route access, and password reset/session revocation using axios, Maildev, and Prisma
  • Replace trivial global Jest setup/teardown with scripts that provision test Postgres/Redis connections, run Prisma migrations, launch Maildev and the app, and persist PIDs for cleanup
  • Disable ts-jest diagnostics in e2e Jest config and add small robustness tweaks (e.g., getLocationFromIp error path, Application/Job controller param type checks)
e2e/src/auth/auth.e2e.spec.ts
e2e/src/support/global-setup.ts
e2e/src/support/global-teardown.ts
e2e/jest.config.ts
e2e/src/support/test-setup.ts
src/controllers/application.controller.ts
src/controllers/job.controller.ts
src/utils/location.util.ts
Improve configuration, caching, and base infrastructure/tooling.
  • Enforce required/optional environment variables and safe port parsing in config, ensuring missing critical secrets/URLs fail fast while allowing optional logger/SMTP fields
  • Guard cache key computation in admin/candidate/employer/job/application services and default pagination options when none are provided
  • Modernize tooling: replace .eslintignore with explicit eslint.config.cjs, set TypeScript ignoreDeprecations flag, add Better Auth dependency, tweak docker-compose service names, main.ts env loading, nx analytics, and package metadata/scripts
src/configs/config.ts
src/services/admin.service.ts
src/services/candidate.service.ts
src/services/employer.service.ts
src/services/job.service.ts
src/services/application.service.ts
src/services/base.service.ts
docker-compose.yml
src/main.ts
tsconfig.json
eslint.config.cjs
nx.json
package.json
package-lock.json
Repository/metadata/documentation updates and cleanup of obsolete code.
  • Update README badges, clone URLs, Docker image name, and contributing links to new GitHub org/user
  • Delete unused unit tests and authentication-related utilities that are replaced by Better Auth, plus adjust imports to use local notification lib paths
  • Add Prisma migration lock file and Better Auth-related tables; clean up duplicate exports and outdated comments
README.md
src/utils/notification.util.ts
src/templates/authentication.template.ts
src/__tests__/utils/getUserRole.test.ts
src/__tests__/validators/admin.spec.ts
e2e/src/server/server.spec.ts
.eslintignore
src/interfaces/index.ts
src/middlewares/index.ts
src/routes/index.ts
src/services/index.ts
src/controllers/index.ts
prisma/migrations/migration_lock.toml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@iamkabelomoobi iamkabelomoobi merged commit cb85f56 into main Jun 6, 2026
1 of 2 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The Users migration adds a non-null name column without a default and notes that this is not possible on non-empty tables; consider either backfilling existing rows in the migration or introducing the column as nullable and tightening it in a follow-up step to avoid production deployment failures.
  • The new requireEnv/parsePort usage in config.ts now hard-requires values like NODEMAILER_HOST and NODEMAILER_PORT, which previously could be omitted; if email is optional in some environments, you may want to treat these as optional or gate their validation behind a feature flag to prevent the app from crashing when mail isn’t configured.
  • In libs/auth.ts, createUserbyRole throws when role === admin, while the README’s Auth API examples show an admin sign-up payload using role: "admin"; it would be good to align the runtime behavior and the documented API by either allowing controlled admin sign-up or clearly documenting that admins cannot be created via this endpoint.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `Users` migration adds a non-null `name` column without a default and notes that this is not possible on non-empty tables; consider either backfilling existing rows in the migration or introducing the column as nullable and tightening it in a follow-up step to avoid production deployment failures.
- The new `requireEnv`/`parsePort` usage in `config.ts` now hard-requires values like `NODEMAILER_HOST` and `NODEMAILER_PORT`, which previously could be omitted; if email is optional in some environments, you may want to treat these as optional or gate their validation behind a feature flag to prevent the app from crashing when mail isn’t configured.
- In `libs/auth.ts`, `createUserbyRole` throws when `role === admin`, while the README’s Auth API examples show an admin sign-up payload using `role: "admin"`; it would be good to align the runtime behavior and the documented API by either allowing controlled admin sign-up or clearly documenting that admins cannot be created via this endpoint.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant