Skip to content

feat: add scheduled refresh token and password reset cleanup job#114

Open
GitAddRemote wants to merge 11 commits intomainfrom
fix/ISSUE-98-token-cleanup-job
Open

feat: add scheduled refresh token and password reset cleanup job#114
GitAddRemote wants to merge 11 commits intomainfrom
fix/ISSUE-98-token-cleanup-job

Conversation

@GitAddRemote
Copy link
Copy Markdown
Owner

@GitAddRemote GitAddRemote commented Apr 11, 2026

Summary

Implements a scheduled token cleanup job that periodically purges expired and revoked refresh tokens and used/expired password reset tokens from the database.

  • Registers the cleanup cron via `OnApplicationBootstrap` + `SchedulerRegistry` (not `@Cron()`) so the cron expression can be driven by environment variables loaded at runtime
  • `SchedulerRegistry` is injected with `@Optional()` so the service constructs cleanly in test environments where `ScheduleModule` is excluded from `AppModule`
  • Cron expression is read from `REFRESH_TOKEN_CLEANUP_CRON` env var; invalid or blank values fall back to the default `0 3 * * *` (3 AM daily) with a logged warning
  • Skips cron registration and cleanup queries in test environments (`NODE_ENV === 'test'` or `JEST_WORKER_ID` set)
  • Adds migration for indexes supporting efficient cleanup DELETEs: partial indexes on `revoked`/`used` booleans and range indexes on `"expiresAt"` for both tables
  • Upgrades `Dockerfile` base image from `node:14` to `node:20-slim` (Debian/glibc — alpine was avoided because musl libc breaks bcrypt native prebuilds; Node 18 was EOL so bumped to 20 to match CI)

Test plan

  • Unit tests in `token-cleanup.service.spec.ts` (11 tests pass)
    • Early return when `NODE_ENV=test`
    • Early return when `JEST_WORKER_ID` set
    • Early return when `schedulerRegistry` is absent (`@Optional()` path)
    • Cron registration with default and custom expressions
    • Fallback to default on invalid cron expression
    • Blank/whitespace env var treated as unset
    • Cleanup deletes revoked/expired refresh tokens and used/expired password resets
    • No throw when a DB query fails
  • Migration has working `up()` and `down()` methods

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduled cleanup job in the auth module to purge revoked/expired refresh tokens and used/expired password reset records, preventing unbounded table growth and reducing retention of sensitive token data (Issue #98).

Changes:

  • Introduces TokenCleanupService with a Nest @Cron job to delete revoked/expired rows from refresh_tokens and password_resets.
  • Registers the cleanup service in AuthModule.
  • Documents REFRESH_TOKEN_CLEANUP_CRON in backend/.env.example.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
backend/src/modules/auth/token-cleanup.service.ts Adds the scheduled cleanup job implementation and logging.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService as an auth provider.
backend/.env.example Documents the cron env var used to configure the schedule.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
@GitAddRemote GitAddRemote self-assigned this Apr 12, 2026
Add TokenCleanupService to the auth module with a @Cron job that
runs daily at 3am (configurable via REFRESH_TOKEN_CLEANUP_CRON) and
deletes all refresh_tokens rows where revoked=true or expires_at < now,
and all password_resets rows where used=true or expires_at < now.

- Job skips early when NODE_ENV=test
- Logs row count and duration on success, error stack on failure
- Does not rethrow on failure so job errors cannot crash the process
- ScheduleModule is already conditionally excluded in test env (AppModule)

Closes #98
@GitAddRemote GitAddRemote force-pushed the fix/ISSUE-98-token-cleanup-job branch from 943ff4a to 0507031 Compare April 13, 2026 00:24
…ice, add tests

- Replace expires_at with "expiresAt" in both QueryBuilder WHERE clauses —
  TypeORM quotes identifiers so the DB column is case-sensitive "expiresAt",
  not expires_at, which would always error and silently skip cleanup
- Remove ConfigService import and constructor injection — it was never used;
  @Cron() is evaluated at module-load time before DI so ConfigService
  cannot supply the cron expression regardless
- Add TokenCleanupService unit tests covering early return in test env,
  correct WHERE clauses for both delete operations, and error handling
Copilot AI review requested due to automatic review settings April 13, 2026 14:42
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduled maintenance task in the backend auth module to periodically purge expired/revoked authentication artifacts from the database, addressing table growth and reducing retention of sensitive token records.

Changes:

  • Introduces TokenCleanupService with a configurable daily @Cron job to delete revoked/expired refresh_tokens and used/expired password_resets.
  • Wires the service into AuthModule providers.
  • Adds unit tests for early-return behavior, delete query conditions, and non-throwing failure handling; documents the cron env var in .env.example.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
backend/src/modules/auth/token-cleanup.service.ts New cron-driven cleanup logic for refresh tokens and password reset tokens.
backend/src/modules/auth/token-cleanup.service.spec.ts Unit tests covering skip behavior in tests, delete query clauses, and error handling.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in the auth module providers.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON default cron expression.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.spec.ts Outdated
…_WORKER_ID guard

@Cron() expressions are evaluated at module-load time in Node.js CJS, before
dotenv runs, so REFRESH_TOKEN_CLEANUP_CRON from .env was never read. Switching
to OnApplicationBootstrap + SchedulerRegistry means the cron is registered
after ConfigModule has fully loaded all env vars.

- Remove @Cron() decorator; implement OnApplicationBootstrap
- Register cron via SchedulerRegistry using ConfigService.get() so .env
  values are honoured; falls back to '0 3 * * *' if unset
- Add JEST_WORKER_ID guard to onApplicationBootstrap() so cron is never
  registered in Jest worker processes even if NODE_ENV is not 'test'
- Add cron as a direct dependency (was transitive via @nestjs/schedule)
- Update spec: explicit NODE_ENV mock in cleanupExpiredTokens early-return
  test for determinism; add onApplicationBootstrap() coverage
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an auth-module scheduled cleanup job intended to periodically purge revoked/expired refresh tokens (and used/expired password resets) to prevent unbounded table growth and reduce retention of sensitive token records.

Changes:

  • Introduces TokenCleanupService that registers a cron job on bootstrap and executes DB DELETEs for expired/revoked tokens.
  • Adds unit tests for cron registration guards and deletion query construction/error handling.
  • Adds REFRESH_TOKEN_CLEANUP_CRON to .env.example and adds cron as a direct dependency.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds cron dependency entry and updates lock metadata.
backend/package.json Adds cron dependency for runtime CronJob usage.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON with default schedule.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in Auth module providers.
backend/src/modules/auth/token-cleanup.service.ts Implements bootstrap-time cron registration and cleanup queries + logging.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit coverage for bootstrap guards and cleanup behavior.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.ts
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/package.json
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
- Add @optional() to SchedulerRegistry injection so service can be

  instantiated in test environments where ScheduleModule is excluded

- Validate cron expression at runtime; fall back to '0 3 * * *' on invalid value

- Treat blank/whitespace REFRESH_TOKEN_CLEANUP_CRON as unset (|| not ??)

- Upgrade Dockerfile base image from node:14 to node:18-alpine

- Add migration for token cleanup indexes (partial + range indexes on

  revoked, expiresAt for refresh_tokens and used, expiresAt for password_resets)

- Expand spec: test @optional() path, invalid cron fallback, blank env var
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an Auth-module scheduled maintenance job to remove expired/revoked refresh tokens and used/expired password reset tokens, plus supporting DB indexes and runtime configuration.

Changes:

  • Introduces TokenCleanupService that registers a cron job at bootstrap using SchedulerRegistry and a configurable cron expression.
  • Adds unit tests covering bootstrap registration and cleanup query behavior.
  • Adds a migration creating indexes to support efficient cleanup deletes; updates Docker base image and dependency set (adds cron).

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks cron dependency and updates lockfile metadata.
backend/src/modules/auth/token-cleanup.service.ts Implements bootstrap-registered cron + cleanup DELETE queries and logging.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit coverage for cron registration paths and cleanup execution.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService provider in AuthModule.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds indexes (partial + expiresAt) to speed up cleanup deletes.
backend/package.json Adds direct dependency on cron.
backend/Dockerfile Upgrades base image to Node 18 Alpine.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.ts
Comment thread backend/src/modules/auth/token-cleanup.service.ts
Comment thread backend/Dockerfile Outdated
Merge conflict resolutions:

- .env.example: keep both REFRESH_TOKEN_CLEANUP_CRON and ALLOWED_ORIGIN

- package.json: keep both cron and cookie-parser dependencies

- auth.module.ts: keep TokenCleanupService, drop RefreshTokenStrategy

  (superseded by custom cookie-reading RefreshTokenAuthGuard on main)

- pnpm-lock.yaml: regenerated with pnpm install

Review item fixes:

- Log correct (effective) expression after fallback, not the invalid one

- Extend cleanupExpiredTokens() guard to also skip when JEST_WORKER_ID set

- Switch Dockerfile from node:18-alpine to node:18-slim (musl/bcrypt compat)

- Add spec case for JEST_WORKER_ID guard in cleanupExpiredTokens

- Unset JEST_WORKER_ID in non-test-env describe block so DB tests can run
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an environment-configurable scheduled cleanup job to the backend auth module to purge expired/revoked refresh tokens and used/expired password reset tokens, along with supporting DB indexes and runtime/container updates.

Changes:

  • Introduces TokenCleanupService that registers a cron job at bootstrap via SchedulerRegistry, with runtime-configurable expression and test-environment guards.
  • Adds unit tests covering cron registration behavior and cleanup delete queries.
  • Adds a migration for cleanup-oriented indexes and updates backend runtime dependencies/container base image.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks the new cron dependency (and related lockfile metadata updates).
backend/src/modules/auth/token-cleanup.service.ts Implements scheduled token cleanup via OnApplicationBootstrap + SchedulerRegistry with env-driven cron expression and safeguards.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit tests for cron registration and cleanup delete behavior/guards.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService as an auth module provider.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds indexes (partial boolean + expiresAt) to support cleanup DELETE performance.
backend/package.json Adds direct cron dependency.
backend/Dockerfile Updates base image to Node 18 (slim) for dependency compatibility.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Outdated
Comment thread backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Outdated
The previous comments said the WHERE clause 'mirrors cleanup query', which

implied full coverage of the OR condition. Each partial index only covers

one predicate (revoked = true / used = true); the range index on expiresAt

handles the other side. Updated comments to describe what each index

actually covers.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduled maintenance job to the auth subsystem to periodically delete expired/revoked refresh tokens and used/expired password reset tokens, with runtime-configurable cron scheduling and supporting DB indexes.

Changes:

  • Introduces TokenCleanupService that registers a CronJob via SchedulerRegistry on bootstrap (env-driven schedule, safe no-op in test/Jest environments).
  • Adds unit tests covering cron registration behavior and cleanup query execution/guards.
  • Adds a migration creating indexes to support efficient cleanup deletes, plus wires the service into AuthModule and documents the env var in .env.example.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds the cron dependency to the lockfile (and related metadata updates).
backend/package.json Adds cron as a direct backend dependency.
backend/src/modules/auth/token-cleanup.service.ts Implements bootstrap-time cron registration and token cleanup delete logic.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit tests for cron registration guards/fallbacks and delete behavior.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in the auth module providers.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds partial/range indexes to support cleanup queries efficiently.
backend/Dockerfile Updates Node base image version for backend container builds.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/Dockerfile Outdated
Comment thread backend/Dockerfile Outdated
Node 18 reached EOL; CI already runs on Node 20. Switching to node:20-slim
(Debian/glibc) keeps the runtime in sync with CI and avoids the musl libc
issues that alpine would introduce for bcrypt's native prebuilds.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduled maintenance job to the auth subsystem that periodically deletes expired/revoked refresh tokens and used/expired password reset tokens, with configurable cron timing and supporting DB indexes.

Changes:

  • Introduces TokenCleanupService that registers a runtime-configured cron job via SchedulerRegistry and performs periodic cleanup DELETEs.
  • Adds unit tests for cron registration guards/fallback behavior and for the cleanup DELETE queries.
  • Adds DB indexes (including partial indexes) to support efficient cleanup, plus wiring/env/docs and container/runtime dependency updates.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new direct cron dependency and associated lockfile updates.
backend/src/modules/auth/token-cleanup.service.ts Implements runtime cron registration + token/password reset cleanup logic.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit coverage for bootstrap guards, cron expression fallback, and DELETE query behavior.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in the Auth module providers.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds indexes to support efficient cleanup DELETEs.
backend/package.json Adds cron as a direct dependency.
backend/Dockerfile Updates runtime base image to Node 20 slim.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.spec.ts
Comment thread backend/src/modules/auth/token-cleanup.service.spec.ts
Comment thread backend/src/modules/auth/token-cleanup.service.ts
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Service:
- Separate the schedulerRegistry guard from the test/JEST_WORKER_ID guard so
  a missing registry in a non-test environment emits a warn log rather than
  silently no-oping; production misconfiguration is now visible in logs
- Split the single try/catch around both DELETE queries into two independent
  blocks so a failure in the refresh token cleanup does not prevent the
  password reset cleanup from running; each table logs its own error

Spec:
- Extract restoreWorker() helper that calls delete instead of assignment when
  the original JEST_WORKER_ID value was undefined; assigning undefined to
  process.env coerces it to the string 'undefined', which would leave the
  guard permanently set and contaminate later tests
- Add test: 'should still clean up password resets when refresh token cleanup
  fails' to assert the new per-table resilience
Resolved conflict in backend/.env.example: kept main's sectioned format
and FRONTEND_URL; added REFRESH_TOKEN_CLEANUP_CRON under a new
'Token Cleanup' section; dropped the duplicate Application block from HEAD.
Copilot AI review requested due to automatic review settings April 16, 2026 02:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a scheduled maintenance job to the auth module that periodically deletes expired/revoked refresh tokens and used/expired password reset tokens, with supporting DB indexes and runtime-configurable scheduling.

Changes:

  • Introduces TokenCleanupService that registers a CronJob at bootstrap using SchedulerRegistry, with env-driven cron expression and safe fallbacks/guards for test environments.
  • Adds unit tests covering cron registration paths and cleanup delete behavior, including error handling.
  • Adds a migration to create indexes that support efficient cleanup deletes; updates Docker base image and env example; adds cron dependency.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks cron dependency version used by the backend.
backend/src/modules/auth/token-cleanup.service.ts Implements bootstrap-time cron registration and DB cleanup deletes.
backend/src/modules/auth/token-cleanup.service.spec.ts Unit tests for cron registration guards/fallbacks and cleanup query behavior.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in the auth module providers.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds indexes for revoked/used and expiresAt to speed up cleanup deletes.
backend/package.json Adds direct dependency on cron.
backend/Dockerfile Updates backend container base image to Node 20 slim.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
Comment thread backend/src/modules/auth/token-cleanup.service.ts Outdated
- Replace the inaccurate comment ('evaluates before dotenv runs') with the
  actual constraint: @Cron() decorator arguments are evaluated at class-
  definition time before DI runs, so ConfigService cannot be used in them
- Define DEFAULT_CRON = '0 3 * * *' once and reference it throughout,
  eliminating the duplicated string that could diverge on future edits
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an Auth-module-level scheduled cleanup job to periodically delete expired/revoked refresh tokens and used/expired password reset tokens, with supporting DB indexes and runtime-configurable cron scheduling.

Changes:

  • Introduces TokenCleanupService that registers a CronJob at bootstrap time via SchedulerRegistry, with runtime cron expression from REFRESH_TOKEN_CLEANUP_CRON and safe fallbacks/guards for test environments.
  • Adds unit tests covering cron registration behavior and the cleanup DELETE queries (including error isolation between the two tables).
  • Adds a migration creating indexes to support the cleanup queries, and updates runtime/build config (cron dependency, Docker Node image, .env.example).

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pnpm-lock.yaml Locks the newly-added direct cron dependency.
backend/src/modules/auth/token-cleanup.service.ts Implements bootstrap-time cron registration + cleanup DELETEs with test-env guards and fallback cron expression handling.
backend/src/modules/auth/token-cleanup.service.spec.ts Adds unit test coverage for cron registration and cleanup behavior.
backend/src/modules/auth/auth.module.ts Registers TokenCleanupService in the Auth module.
backend/src/migrations/1765038000000-AddTokenCleanupIndexes.ts Adds indexes (including partial indexes) to support efficient cleanup deletes.
backend/package.json Adds cron as a direct dependency.
backend/Dockerfile Bumps base image to node:20-slim.
backend/.env.example Documents REFRESH_TOKEN_CLEANUP_CRON configuration.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +52 to +56
const DEFAULT_CRON = '0 3 * * *';
const rawExpression = this.configService
.get<string>('REFRESH_TOKEN_CLEANUP_CRON')
?.trim();
const cronExpression = rawExpression || DEFAULT_CRON;
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.

2 participants