-
Notifications
You must be signed in to change notification settings - Fork 714
Apps #9215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apps #9215
Conversation
- Added expiresIn option to sign() method in getJwtAccessToken() - Access tokens now expire after JWT_TOKEN_EXPIRATION_TIME (default: 1 day) - This aligns with the behavior of generateToken() method - Fixes issue where access tokens from /auth/login and /auth/refresh-token never expired
…tion fix: add expiration time to JWT access token in getJwtAccessToken()
* feat(desktop-ui-lib): add token refresh service and interceptors; update auth services * chore(desktop): wire auth/interceptors in app module and component * feat(auth): add refresh token system Implement a comprehensive refresh token system for seamless authentication and extended user sessions. This change introduces: - `RefreshTokenInterceptor`: Automatically handles 401 responses by refreshing expired access tokens, queuing concurrent requests, and retrying original requests. - `TokenRefreshService`: Proactively refreshes tokens 5 minutes before expiration to prevent user-facing 401s. - Updates to `AuthStrategyService` and `StoreService`: Manage token rotation, securely store refresh tokens, and track JWT expiry timestamps. - Refined `UnauthorizedInterceptor`: Delegates 401 handling to the refresh interceptor, ensuring proper logout only when the refresh token is invalid or unavailable. - Integration into desktop and agent applications: The new interceptors and services are registered and initialized. This system provides a production-ready solution for managing token lifecycles, enhancing security, and improving user experience by minimizing login interruptions. Extensive documentation for the implementation is included in `REFRESH_TOKEN_QUICKSTART.md`, `REFRESH_TOKEN_SUMMARY.md`, and `packages/desktop-ui-lib/REFRESH_TOKEN_IMPLEMENTATION.md`. * refactor(token-refresh): simplify token refresh service API Rename `startTokenRefreshTimer` to `start` and `stopTokenRefreshTimer` to `stop` in the `TokenRefreshService`. This change improves method brevity and consistency. Update all call sites in `AppComponent` for both agent and desktop applications to reflect the new method names. Remove `REFRESH_TOKEN_QUICKSTART.md`, `REFRESH_TOKEN_SUMMARY.md`, and `packages/desktop-ui-lib/REFRESH_TOKEN_IMPLEMENTATION.md`. These documentation files are no longer current or have been consolidated into other documentation. * feat(auth): implement token refresh mechanism Integrates TokenRefreshService to automatically renew authentication tokens. Starts the refresh timer on application initialization for existing sessions and after successful social login. Registers RefreshTokenInterceptor to handle token refreshing for outgoing HTTP requests, ensuring continuous user session without manual re-authentication. * refactor(interceptors): centralize auth endpoint patterns Extract authentication endpoint paths into a dedicated `AUTH_ENDPOINTS` constant. This change reduces duplication and improves maintainability by providing a single source of truth for endpoints that should be excluded from automatic token refresh and unauthorized redirect logic. Updated `RefreshTokenInterceptor` and `UnauthorizedInterceptor` to utilize this new constant. Includes minor formatting improvements in `app.module.ts`. * refactor(interceptors): reorder RefreshTokenInterceptor Move RefreshTokenInterceptor after UnauthorizedInterceptor. This ensures that unauthorized responses are processed by the UnauthorizedInterceptor first, allowing for subsequent token refresh attempts in the correct order within the HTTP interceptor chain.
* feat: add automatic token refresh interceptor for web app - Created AuthRefreshInterceptor to handle 401 errors and automatically refresh access tokens - Prevents users from being logged out when access tokens expire (after 1 day) - Uses BehaviorSubject pattern to coordinate multiple simultaneous requests during token refresh - Registered interceptor in Gauzy web app module - Skips refresh for auth endpoints to avoid infinite loops - Falls back to error if refresh token is missing or refresh fails This interceptor is necessary now that access tokens have expiration time. Without it, users would be disconnected every 24 hours when tokens expire. * remove unmecessary comment * fix authendpoints * feat: add automatic token refresh interceptor for web app - Created AuthRefreshInterceptor to handle 401 errors and automatically refresh access tokens - Prevents users from being logged out when access tokens expire - Uses BehaviorSubject pattern to coordinate multiple simultaneous requests during token refresh - Registered interceptor in Gauzy web app module - Excludes only public auth endpoints to avoid infinite loops - Protected endpoints like /auth/authenticated, /auth/role, /auth/permissions will trigger refresh - Clears tokens on refresh failure to allow proper logout flow This interceptor is necessary now that access tokens have expiration time. Without it, users would be disconnected every 24 hours when tokens expire.
* fix: agent refresh token handled * fix: remove unnecessary async * fix: wrong type of refreshtoken
* fix: reset refreshTokenSubject on token refresh failure - Added refreshTokenSubject.next(null) in catchError block when refresh fails - Ensures pending requests are unblocked and notified of refresh failure - Prevents requests from waiting indefinitely when refresh token is invalid - Maintains proper cleanup sequence: clear tokens -> reset subject -> throw error * fix: properly handle token refresh failure with error emission and login redirect - Changed from BehaviorSubject<string | null> to Subject<string> to properly handle errors - Emit error through subject on refresh failure to unblock all waiting requests - Added automatic redirect to login page when refresh token fails - Create new Subject after error to allow future refresh attempts - Removed filter for non-null values that was causing requests to hang indefinitely This fixes the critical bug where pending requests would hang forever when refresh token is invalid or expired. Now all waiting requests are properly notified of the failure and users are redirected to the login page. * refactor: use inject() function for dependency injection in AuthRefreshInterceptor - Replaced constructor injection with modern inject() function pattern - Removed Injector dependency in favor of direct inject() calls - Simplified refreshAccessToken() to use injected authService directly - Aligns with Angular best practices and modern dependency injection patterns
[FIX] remove `strict` and use only `lax` to avoid the oauth flow to be restrictive(Avoiding crsf issue)
feat: migrate GitHub workflows to Ubicloud/Warp runners
Bumps [min-document](https://github.com/Raynos/min-document) from 2.19.0 to 2.19.2. - [Commits](Raynos/min-document@v2.19.0...v2.19.2) --- updated-dependencies: - dependency-name: min-document dependency-version: 2.19.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]>
…ument-2.19.2 chore(deps): bump min-document from 2.19.0 to 2.19.2
* feat(redis): improve Redis connection configuration - Add pingInterval (30s) to prevent LB/firewall disconnections - Add keepAlive (10s) for TCP keepalive - Add reconnectStrategy with exponential backoff (1s-5s) - Add connectTimeout (10s) for connection stability - Apply improvements to session store, cache store, and health checks - Optimize for DigitalOcean Valkey 8 compatibility Resolves #9162 (step 4) * fix typos * fix(redis): implement true exponential backoff in reconnectStrategy - Replace linear progression (1000 + retries * 200) with exponential backoff (1000 * 2^retries) - Retry delays now: 1000ms, 2000ms, 4000ms, 5000ms (capped) - Previous formula was arithmetic, not exponential as claimed in comment - Apply fix to session store, cache store, and health checks Resolves #9162 (reconnectStrategy fix) * feat: migrate from cache-manager-redis-yet to @keyv/redis with production-ready options - Replace outdated cache-manager-redis-yet with modern @keyv/redis adapter - Create new CacheModule with Keyv Redis integration - Add production-ready Redis connection options: - keepAlive: 10_000ms for TCP keepalive - reconnectStrategy with exponential backoff (1s-5s) - connectTimeout: 10_000ms for connection stability - pingInterval: 30_000ms to prevent LB/firewall disconnections - isolationPoolOptions with connection pooling (min: 1, max: 100) - TLS support for secure connections - Configure Keyv options for optimal performance: - namespace: 'gauzy-cache' for key organization - useUnlink: true for better performance (UNLINK vs DEL) - clearBatchSize: 1000 for batch operations - throwOnConnectError: true for error handling - Create CacheService with get/set/delete/clear methods - Update app.module.ts to use new CacheModule - Remove cache-manager-redis-yet dependency from package.json * fix cspell * fix redis config * fix ttl config * fix ttl config * remove unused import * chore: sanitize .env.sample - remove sensitive credentials * Refactor cache service * refactor(cache): migrate from cache-manager-redis-yet to cacheable with 2-layer non-blocking cache - Replace cache-manager-redis-yet with cacheable package - Implement 2-layer caching (in-memory L1 + Redis L2) - Configure non-blocking mode for Redis operations - Add Redis URL validation to prevent invalid URLs - Remove custom cache module in favor of NestJS cache-manager integration - Add fallback to in-memory cache if Redis connection fails Follows NestJS caching documentation and cacheable best practices: https://docs.nestjs.com/techniques/caching https://cacheable.org/docs/cacheable/#non-blocking-with-keyvredis * refactor(cache): simplify cache configuration using createKeyvNonBlocking helper - Use createKeyvNonBlocking() helper function from @keyv/redis for non-blocking Redis setup - Remove manual Redis client configuration (disableOfflineQueue, reconnectStrategy, event listeners) - Remove explicit primary cache configuration (Cacheable manages in-memory LRU by default) - Replace 'as any' type cast with factory function for type safety - Simplify code from ~120 lines to ~40 lines while maintaining same functionality Benefits: - Automatic non-blocking configuration (disableOfflineQueue: true, reconnectStrategy: false, throwOnConnectError: false) - Type-safe cache-manager integration without 'as any' cast - Cleaner, more maintainable code following cacheable best practices - Default in-memory LRU cache managed by Cacheable (Layer 1) - Non-blocking Redis cache for distributed persistence (Layer 2) Follows cacheable documentation: https://cacheable.org/docs/cacheable/#non-blocking-with-keyvredis * fix(cache): improve security and error handling Security improvements: - Remove credential logging: Replace plain-text Redis URL logging with sanitized connection info - Log only host, port, and protocol (rediss/redis) without username/password - Prevent credential exposure in application logs (compliance & security best practice) Error handling improvements: - Fix invalid fallback: Replace 'store: undefined' with proper in-memory config - Add try-catch around Redis initialization to handle connection failures gracefully - Return '{ isGlobal: true }' for in-memory fallback instead of broken 'store: undefined' - Ensure cache operations continue to work even when Redis is unavailable Before (security risk): console.log('REDIS_URL: ', url); // Logs: redis://user:password@host:6379 After (secure): console.log('Redis Cache: Connecting to redis://host:6379'); // No credentials Before (broken fallback): return { store: undefined }; // Cache operations will fail After (working fallback): return { isGlobal: true }; // In-memory cache works correctly * fix 2layer cache * add primary store * fix config * fix return * fix deeepscan * feat: add comprehensive Redis configuration properties for cache consistency - Parse Redis URL to extract username, password, host, port - Add conditional socket configuration based on TLS/TCP mode - TLS mode: tls, passphrase, rejectUnauthorized, connectTimeout - TCP mode: keepAlive, keepAliveInitialDelay, connectTimeout - Add pingInterval for connection keep-alive - Maintain consistency with RedisHealthIndicator configuration - Respect non-blocking mode constraints (reconnectStrategy omitted) * fix parse * fix parse * add default port * improve parse int * fix comment
* fix: remove and use only lax to avoid the oauth flow to be restrictive * feat: migrate GitHub workflows to Ubicloud/Warp runners and normalize YAML quoting * chore(deps): bump min-document from 2.19.0 to 2.19.2 Bumps [min-document](https://github.com/Raynos/min-document) from 2.19.0 to 2.19.2. - [Commits](Raynos/min-document@v2.19.0...v2.19.2) --- updated-dependencies: - dependency-name: min-document dependency-version: 2.19.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]> * feat(redis): migrate from cache-manager-redis-yet to @keyv/redis (#9172) * feat(redis): improve Redis connection configuration - Add pingInterval (30s) to prevent LB/firewall disconnections - Add keepAlive (10s) for TCP keepalive - Add reconnectStrategy with exponential backoff (1s-5s) - Add connectTimeout (10s) for connection stability - Apply improvements to session store, cache store, and health checks - Optimize for DigitalOcean Valkey 8 compatibility Resolves #9162 (step 4) * fix typos * fix(redis): implement true exponential backoff in reconnectStrategy - Replace linear progression (1000 + retries * 200) with exponential backoff (1000 * 2^retries) - Retry delays now: 1000ms, 2000ms, 4000ms, 5000ms (capped) - Previous formula was arithmetic, not exponential as claimed in comment - Apply fix to session store, cache store, and health checks Resolves #9162 (reconnectStrategy fix) * feat: migrate from cache-manager-redis-yet to @keyv/redis with production-ready options - Replace outdated cache-manager-redis-yet with modern @keyv/redis adapter - Create new CacheModule with Keyv Redis integration - Add production-ready Redis connection options: - keepAlive: 10_000ms for TCP keepalive - reconnectStrategy with exponential backoff (1s-5s) - connectTimeout: 10_000ms for connection stability - pingInterval: 30_000ms to prevent LB/firewall disconnections - isolationPoolOptions with connection pooling (min: 1, max: 100) - TLS support for secure connections - Configure Keyv options for optimal performance: - namespace: 'gauzy-cache' for key organization - useUnlink: true for better performance (UNLINK vs DEL) - clearBatchSize: 1000 for batch operations - throwOnConnectError: true for error handling - Create CacheService with get/set/delete/clear methods - Update app.module.ts to use new CacheModule - Remove cache-manager-redis-yet dependency from package.json * fix cspell * fix redis config * fix ttl config * fix ttl config * remove unused import * chore: sanitize .env.sample - remove sensitive credentials * Refactor cache service * refactor(cache): migrate from cache-manager-redis-yet to cacheable with 2-layer non-blocking cache - Replace cache-manager-redis-yet with cacheable package - Implement 2-layer caching (in-memory L1 + Redis L2) - Configure non-blocking mode for Redis operations - Add Redis URL validation to prevent invalid URLs - Remove custom cache module in favor of NestJS cache-manager integration - Add fallback to in-memory cache if Redis connection fails Follows NestJS caching documentation and cacheable best practices: https://docs.nestjs.com/techniques/caching https://cacheable.org/docs/cacheable/#non-blocking-with-keyvredis * refactor(cache): simplify cache configuration using createKeyvNonBlocking helper - Use createKeyvNonBlocking() helper function from @keyv/redis for non-blocking Redis setup - Remove manual Redis client configuration (disableOfflineQueue, reconnectStrategy, event listeners) - Remove explicit primary cache configuration (Cacheable manages in-memory LRU by default) - Replace 'as any' type cast with factory function for type safety - Simplify code from ~120 lines to ~40 lines while maintaining same functionality Benefits: - Automatic non-blocking configuration (disableOfflineQueue: true, reconnectStrategy: false, throwOnConnectError: false) - Type-safe cache-manager integration without 'as any' cast - Cleaner, more maintainable code following cacheable best practices - Default in-memory LRU cache managed by Cacheable (Layer 1) - Non-blocking Redis cache for distributed persistence (Layer 2) Follows cacheable documentation: https://cacheable.org/docs/cacheable/#non-blocking-with-keyvredis * fix(cache): improve security and error handling Security improvements: - Remove credential logging: Replace plain-text Redis URL logging with sanitized connection info - Log only host, port, and protocol (rediss/redis) without username/password - Prevent credential exposure in application logs (compliance & security best practice) Error handling improvements: - Fix invalid fallback: Replace 'store: undefined' with proper in-memory config - Add try-catch around Redis initialization to handle connection failures gracefully - Return '{ isGlobal: true }' for in-memory fallback instead of broken 'store: undefined' - Ensure cache operations continue to work even when Redis is unavailable Before (security risk): console.log('REDIS_URL: ', url); // Logs: redis://user:password@host:6379 After (secure): console.log('Redis Cache: Connecting to redis://host:6379'); // No credentials Before (broken fallback): return { store: undefined }; // Cache operations will fail After (working fallback): return { isGlobal: true }; // In-memory cache works correctly * fix 2layer cache * add primary store * fix config * fix return * fix deeepscan * feat: add comprehensive Redis configuration properties for cache consistency - Parse Redis URL to extract username, password, host, port - Add conditional socket configuration based on TLS/TCP mode - TLS mode: tls, passphrase, rejectUnauthorized, connectTimeout - TCP mode: keepAlive, keepAliveInitialDelay, connectTimeout - Add pingInterval for connection keep-alive - Maintain consistency with RedisHealthIndicator configuration - Respect non-blocking mode constraints (reconnectStrategy omitted) * fix parse * fix parse * add default port * improve parse int * fix comment --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: rolandm99 <[email protected]> Co-authored-by: Paradoxe Ngwasi <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: samuel mbabhazi <[email protected]>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
Greptile OverviewGreptile SummaryThis PR implements comprehensive token refresh functionality across web and desktop applications, and migrates Redis caching from Key Changes
Technical ImplementationThe authentication flow now supports both proactive (desktop-only, timer-based) and reactive (all apps, 401-based) token refresh strategies with proper request queueing to prevent duplicate refresh attempts. Confidence Score: 4/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User
participant App
participant TokenInterceptor
participant RefreshInterceptor
participant AuthService
participant Store
participant API
Note over User,API: Initial Login Flow
User->>App: Login with credentials
App->>AuthService: authenticate(email, password)
AuthService->>API: POST /auth/login
API-->>AuthService: {token, refresh_token, user}
AuthService->>Store: Save token, refresh_token, tokenExpiresAt
AuthService-->>App: Success
Note over User,API: Token Refresh - Proactive (Desktop Apps)
loop Every 1 minute
Store->>Store: Check isTokenExpired()
alt Token expiring within 5 min
Store->>AuthService: refreshToken()
AuthService->>API: POST /auth/refresh-token
API-->>AuthService: {token}
AuthService->>Store: Update token, tokenExpiresAt
end
end
Note over User,API: Token Refresh - Reactive (401 Handling)
User->>App: Make API request
App->>TokenInterceptor: Add Bearer token
TokenInterceptor->>API: Request with token
API-->>RefreshInterceptor: 401 Unauthorized
RefreshInterceptor->>Store: Get refresh_token
RefreshInterceptor->>AuthService: refreshToken()
AuthService->>API: POST /auth/refresh-token
API-->>AuthService: {token}
AuthService->>Store: Update token
RefreshInterceptor->>API: Retry original request
API-->>User: Success response
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
89 files reviewed, 1 comment
| console.warn('Failed to parse token expiry, using default 1 hour:', error); | ||
| // Default to 1 hour |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: atob may not be available in all Electron/Node.js contexts
| console.warn('Failed to parse token expiry, using default 1 hour:', error); | |
| // Default to 1 hour | |
| const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/desktop-ui-lib/src/lib/auth/services/auth-strategy.service.ts
Line: 262:263
Comment:
**syntax:** `atob` may not be available in all Electron/Node.js contexts
```suggestion
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
```
How can I resolve this? If you propose a fix, please make it concise.
PR
Please note: we will close your PR without comment if you do not check the boxes above and provide ALL requested information.