Skip to content

fix: align session TTL with refresh token lifetime and prevent silent…#947

Merged
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
anumukul:fix/session-role-alignment
Jun 29, 2026
Merged

fix: align session TTL with refresh token lifetime and prevent silent…#947
RUKAYAT-CODER merged 1 commit into
rinafcode:mainfrom
anumukul:fix/session-role-alignment

Conversation

@anumukul

Copy link
Copy Markdown

closes #819
closes #823

Summary

Two fixes addressing authentication state inconsistencies and a silent permissions downgrade bug.


Issue 1: Session TTL vs Refresh Token Lifetime Alignment

Problem: src/session/session.service.ts defaults session TTL to 604800s (7 days) from AUTH_SESSION_TTL_SECONDS, while the JWT refresh token expires based on JWT_REFRESH_EXPIRES_IN. These values were configured independently with no enforcement of their relationship, allowing sessions to outlive refresh tokens or vice versa.

Fix:

  • SessionService constructor now parses JWT_REFRESH_EXPIRES_IN and compares it against sessionTtlSeconds
  • Logs a warning at startup when session TTL < refresh token lifetime
  • Added SessionService.parseDurationToSeconds() utility supporting 7d, 1h, 60m, 3600s, and numeric formats
  • Documented alignment notes in .env.example for SESSION_TTL_SECONDS, AUTH_SESSION_TTL_SECONDS, and JWT_REFRESH_EXPIRES_IN

Acceptance Criteria:

  • Startup logs a warning when session TTL < refresh token lifetime
  • Both values documented in .env.example with a note about alignment

Issue 2: User Role Getter Silent STUDENT Fallback

Problem: User.role getter in src/users/entities/user.entity.ts returned UserRole.STUDENT when this.roles was undefined or empty. TypeORM does not eagerly load relations, so any query without relations: ['roles'] silently granted incorrect (downgraded) permissions.

Fix:

  • get role() now throws Error('User.roles relation not loaded. Include relations: ["roles"] in the query.') when this.roles === undefined
  • Updated repository queries to include relations: ['roles']:
    • auth.controller.ts: login user lookup
    • auth.service.ts: refresh token user lookup
  • Updated courses.service.ts and enrollments.service.ts role checks to use user.roles (string array from JWT payload) instead of relying on the entity getter, fixing role resolution for JWT-authenticated contexts
  • Added unit tests in user.entity.spec.ts covering:
    • Throws when roles is undefined
    • Returns correct role when roles are loaded
    • Returns STUDENT when roles array is empty
    • Handles null roles gracefully

Acceptance Criteria:

  • Accessing user.role on an entity loaded without the relation throws an error
  • All guards and permission checks load the roles relation
  • Unit tests cover both loaded and unloaded scenarios

Files Changed

File Change
src/session/session.service.ts Added startup TTL validation + parseDurationToSeconds()
.env.example Documented alignment between session TTL and refresh token lifetime
src/users/entities/user.entity.ts get role() throws when roles is undefined
src/auth/auth.controller.ts Login query now includes relations: ['roles']
src/auth/auth.service.ts Refresh token query now includes relations: ['roles']
src/courses/courses.service.ts Role checks use user.roles array instead of entity getter
src/courses/enrollments.service.ts Role check uses user.roles array instead of entity getter
src/users/entities/user.entity.spec.ts New unit tests for the role getter
src/session/session.service.spec.ts Tests for parseDurationToSeconds() and constructor validation
src/auth/auth.service.spec.ts Updated mocks for findOne with relations

… role fallback

- SessionService now validates that session TTL >= JWT refresh token lifetime
  at startup, logging a warning when misaligned. Added parseDurationToSeconds
  utility to interpret JWT_REFRESH_EXPIRES_IN formats (e.g. '7d', '1h').

- User.role getter now throws when the roles relation is not loaded,
  preventing silent STUDENT fallback. Added unit tests for both loaded
  and unloaded scenarios.

- Updated auth.controller.ts and auth.service.ts to load the roles
  relation when querying users for login and refresh token operations.

- Updated courses.service.ts and enrollments.service.ts to check
  user.roles (string array from JWT) instead of relying on the entity
  role getter, fixing role checks for JWT-authenticated users.

- Documented the SESSION_TTL / JWT_REFRESH_EXPIRES_IN alignment
  requirement in .env.example.
@drips-wave

drips-wave Bot commented Jun 29, 2026

Copy link
Copy Markdown

@anumukul Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RUKAYAT-CODER RUKAYAT-CODER 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.

Thank you for contributing to the project.

@RUKAYAT-CODER RUKAYAT-CODER merged commit b0352d8 into rinafcode:main Jun 29, 2026
1 check passed
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.

Session TTL is not synchronized with JWT access token lifetime User entity role getter silently returns STUDENT when roles relation is not loaded

2 participants