Skip to content
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

feat(services-bff): Refresh token with polling #16872

Merged
merged 14 commits into from
Nov 29, 2024

Conversation

snaerseljan
Copy link
Contributor

@snaerseljan snaerseljan commented Nov 14, 2024

Refresh token with polling

What

This feature prevents performance issues that could occur when multiple requests simultaneously attempt to refresh an expired token.
The main logic is in the new token service which manages token refresh operations with built-in concurrency protection.
I have also refactored a bit the enhancedFetch since it was handling error logic which was never reached.

Key features:

  • Prevents multiple simultaneous refresh attempts for the same session
  • Implements a polling mechanism to handle concurrent requests efficiently
  • Provides cache-based coordination between multiple requests
  • Includes automatic retry logic if polling times out
  • Maintains thread safety for token refresh operations

The service ensures that when multiple requests need a token refresh:

  1. Only one actual refresh operation is executed
  2. Other requests wait... for the ongoing refresh to complete
  3. All requests receive the same updated cached response
  4. Failed refresh attempts are properly logged and handled

Why

This prevents multiple consecutive requests from triggering redundant refresh token requests.

NOTE

  • Comments generated mostly with AI

  • TODO tests

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • Formatting passes locally with my changes
  • I have rebased against main before asking for a review

Summary by CodeRabbit

  • New Features

    • Introduced TokenRefreshService for managing token refresh operations with enhanced error handling.
    • Added ErrorService for centralized management of authorization errors.
    • New utility function getCookieOptions for flexible cookie configuration.
  • Improvements

    • Streamlined error handling in login, callbackLogin, and postRequest methods.
    • Updated method signatures in ProxyService and UserService for better clarity and maintainability.
    • Simplified response handling in IdsService by returning raw data instead of structured responses.
    • Enhanced clarity in UserController and UserService with updated method signatures.
    • Improved logging and error management throughout the authentication module.
    • Enhanced test coverage for TokenRefreshService and UserController to ensure robust handling of various scenarios.
  • Bug Fixes

    • Removed unnecessary checks and improved response handling in various services.

@snaerseljan snaerseljan requested a review from a team as a code owner November 14, 2024 14:46
Copy link
Contributor

coderabbitai bot commented Nov 14, 2024

Walkthrough

The pull request introduces significant changes across multiple service files in the authentication module. Key modifications include the removal of the getCookieOptions method from AuthService, replaced by a utility function. The addition of the TokenRefreshService manages token refresh operations, while error handling has been streamlined in several services. The IdsService now returns raw data instead of encapsulated responses. New services, ErrorService, and updated method signatures enhance flexibility and maintainability throughout the module. Overall, the changes focus on simplifying error handling and improving token management.

Changes

File Change Summary
apps/services/bff/src/app/modules/auth/auth.service.ts Removed getCookieOptions method; updated callbackLogin and logout methods to use getCookieOptions from utils. Streamlined error handling in login and callbackLogin methods.
apps/services/bff/src/app/modules/auth/token-refresh.service.ts Added TokenRefreshService class with methods for managing token refresh operations, including polling and cache management.
apps/services/bff/src/app/modules/ids/ids.service.ts Updated postRequest method to return Promise<T> instead of Promise<ApiResponse<T>>; removed error handling logic.
apps/services/bff/src/app/modules/ids/ids.types.ts Removed SuccessResponse, ErrorRes interfaces, and ApiResponse type.
apps/services/bff/src/app/modules/proxy/proxy.module.ts Updated providers array to include TokenRefreshService and ErrorService.
apps/services/bff/src/app/modules/proxy/proxy.service.ts Updated method signatures to accept { req, res } parameters; refactored error handling using ErrorService.
apps/services/bff/src/app/modules/user/user.controller.ts Updated getUser method to include @Res decorator, changing its signature.
apps/services/bff/src/app/modules/user/user.module.ts Added TokenRefreshService and ErrorService to providers array.
apps/services/bff/src/app/modules/user/user.service.ts Updated constructor to replace cryptoService and logger with errorService and tokenRefreshService; modified getUser method signature and error handling.
apps/services/bff/src/app/services/error.service.ts Introduced ErrorService class for handling authorization errors, including handleAuthorizedError method.
apps/services/bff/src/app/utils/get-cookie-options.ts Added getCookieOptions utility function for cookie configuration.

Possibly related PRs

Suggested labels

high priority

Suggested reviewers

  • eirikurn
  • rafnarnason

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

warning eslint@8.57.0: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning eslint > @humanwhocodes/config-array@0.11.14: Use @eslint/config-array instead
warning eslint > @humanwhocodes/config-array > @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
warning eslint > file-entry-cache > flat-cache > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob > inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning jest > @jest/core > jest-config > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > @jest/reporters > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > jest-runtime > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning jest > @jest/core > @jest/transform > babel-plugin-istanbul > test-exclude > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning storybook > @storybook/cli > puppeteer-core > rimraf@2.7.1: Rimraf versions prior to v4 are no longer supported
warning storybook > @storybook/cli > puppeteer-core > rimraf > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning storybook > @storybook/cli > tempy > del > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning storybook > @storybook/cli > jscodeshift > temp > rimraf@2.6.3: Rimraf versions prior to v4 are no longer supported
warning storybook > @storybook/cli > jscodeshift > temp > rimraf > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning storybook > @storybook/cli > jscodeshift > @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
warning storybook > @storybook/cli > jscodeshift > @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
warning storybook > @storybook/cli > jscodeshift > @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
warning next-auth > querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning next-auth > jose@1.28.2: this version is no longer supported
warning next-auth > @next-auth/typeorm-legacy-adapter > typeorm > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning @nx/next > @nx/webpack > stylus > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning @nx/next > @nx/webpack > webpack-dev-server > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning @nx/next > @nx/webpack > fork-ts-checker-webpack-plugin > memfs@3.6.0: this will be v4
warning @nx/next > @nx/webpack > webpack-dev-server > webpack-dev-middleware > memfs@3.6.0: this will be v4
warning workspace-aggregator-e222a6c0-b8fc-411c-a05e-69569ff3751d > eslint@8.57.0: This version is no longer supported. Please see https://eslint.org/version-support for other options.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb31ac and bfa90bc.

📒 Files selected for processing (1)
  • apps/services/bff/src/app/modules/auth/auth.controller.spec.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/services/bff/src/app/modules/auth/auth.controller.spec.ts

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (11)
apps/services/bff/src/app/modules/proxy/proxy.module.ts (1)

13-19: Consider organizing providers by dependency order.

The providers array correctly includes all necessary services for the token refresh feature. However, consider organizing them based on their dependency relationships for better maintainability.

Consider this organization:

  providers: [
    ProxyService,
-   CryptoService,
-   TokenRefreshService,
-   ErrorService,
-   IdsService,
+   ErrorService,      // Base service
+   CryptoService,     // Base service
+   IdsService,        // Depends on base services
+   TokenRefreshService, // Depends on other services
  ],
apps/services/bff/src/app/modules/user/user.controller.ts (2)

31-35: Consider adding type safety for service parameters

The object parameter passed to userService.getUser would benefit from an explicit interface definition.

Consider adding this interface:

interface GetUserParams {
  req: Request;
  res: Response;
  refresh: boolean;
}

Then update the method call:

-    return this.userService.getUser({
+    const params: GetUserParams = {
       req,
       res,
       refresh: query.refresh === 'true',
-    })
+    };
+    return this.userService.getUser(params);

Line range hint 23-36: Architecture aligns with NestJS best practices

The controller properly:

  1. Delegates business logic to the service layer
  2. Maintains single responsibility principle
  3. Uses dependency injection for service integration

The token refresh polling mechanism is correctly abstracted away from the controller layer.

apps/services/bff/src/app/modules/auth/auth.service.ts (1)

Line range hint 1-507: Consider implementing token refresh polling as a separate service.

Given the PR objectives focus on token refresh with polling, consider:

  1. Creating a dedicated TokenRefreshService to handle polling logic
  2. Implementing a caching mechanism for refresh operations
  3. Adding retry logic with exponential backoff
  4. Including proper logging for refresh operations

This would maintain single responsibility principle and make the auth flow more maintainable.

apps/services/bff/src/app/modules/user/user.service.ts (2)

1-2: Consider abstracting Express-specific types from the service layer

Passing Request and Response types from Express into service methods couples the service layer to the HTTP framework, which can reduce modularity and make unit testing more complex. Consider extracting only the necessary data from the Request object in the controller and refactoring the service methods to be independent of Express types.


Line range hint 34-87: Add or update unit tests for the modified getUser method

With significant changes to the getUser method, including updated parameters and token refresh logic, it's important to add or update unit tests to ensure all new scenarios are properly tested.

Do you want assistance in creating unit tests for the updated method or opening a GitHub issue to track this task?

apps/services/bff/src/app/services/error.service.ts (1)

18-33: Consider reducing duplication between OAuth2ErrorCode and OAUTH2_ERROR_CODES

To ensure consistency and simplify maintenance, consider deriving the OAUTH2_ERROR_CODES array from the OAuth2ErrorCode type to avoid duplication.

Here's an example of how you might implement this:

export type OAuth2ErrorCode =
  | 'invalid_grant'
  | 'invalid_client'
  | 'invalid_request'
  | 'unauthorized_client'
  | 'unsupported_grant_type'
  | 'invalid_scope'

export const OAUTH2_ERROR_CODES: OAuth2ErrorCode[] = [
  'invalid_grant',
  'invalid_client',
  'invalid_request',
  'unauthorized_client',
  'unsupported_grant_type',
  'invalid_scope',
]

Alternatively, you can create the type from the array:

const errorCodes = [
  'invalid_grant',
  'invalid_client',
  'invalid_request',
  'unauthorized_client',
  'unsupported_grant_type',
  'invalid_scope',
] as const

export type OAuth2ErrorCode = typeof errorCodes[number]

export const OAUTH2_ERROR_CODES: OAuth2ErrorCode[] = [...errorCodes]
apps/services/bff/src/app/modules/ids/ids.service.ts (1)

Line range hint 66-74: Restrict createPARAuthorizationHeader to be a private method

The createPARAuthorizationHeader method is currently declared as public, but it appears to be used only within the IdsService. Making it private enhances encapsulation by hiding implementation details that are not intended for external use.

Apply this diff to make the method private:

- createPARAuthorizationHeader() {
+ private createPARAuthorizationHeader() {
    const { ids } = this.config
    const basicAuth = `${encodeURIComponent(ids.clientId)}:${encodeURIComponent(
      ids.secret,
    )}`
    const base64Auth = Buffer.from(basicAuth).toString('base64')

    return `Basic ${base64Auth}`
  }
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

14-15: Consider externalizing POLL_INTERVAL and MAX_POLL_TIME to configuration

Currently, the polling interval and maximum polling time are hardcoded as static properties. Externalizing these constants to a configuration file or environment variables would enhance flexibility and make it easier to adjust these values without changing the codebase.


196-198: Include error details in the log for better debugging

The current error log does not include the actual error information, which can make diagnosing issues more difficult. Including the error object provides valuable context when investigating failures.

Update the log statement to include the error details:

- this.logger.error(`Token refresh failed for sid: ${sid}`)
+ this.logger.error(`Token refresh failed for sid: ${sid}`, { error })
apps/services/bff/src/app/modules/proxy/proxy.service.ts (1)

238-238: Provide more descriptive error messages for better debugging

The error message 'Proxying url failed!' may not offer sufficient context for troubleshooting.

Consider including additional details in the exception message to aid debugging while avoiding exposure of sensitive information. For example:

throw new BadRequestException(`Invalid proxy URL provided.`);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 64ad820 and ce95573.

📒 Files selected for processing (11)
  • apps/services/bff/src/app/modules/auth/auth.service.ts (6 hunks)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
  • apps/services/bff/src/app/modules/ids/ids.service.ts (2 hunks)
  • apps/services/bff/src/app/modules/ids/ids.types.ts (0 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.module.ts (1 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.service.ts (7 hunks)
  • apps/services/bff/src/app/modules/user/user.controller.ts (2 hunks)
  • apps/services/bff/src/app/modules/user/user.module.ts (1 hunks)
  • apps/services/bff/src/app/modules/user/user.service.ts (2 hunks)
  • apps/services/bff/src/app/services/error.service.ts (1 hunks)
  • apps/services/bff/src/app/utils/get-cookie-options.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/services/bff/src/app/modules/ids/ids.types.ts
🧰 Additional context used
📓 Path-based instructions (10)
apps/services/bff/src/app/modules/auth/auth.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/ids/ids.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/proxy/proxy.module.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/proxy/proxy.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/user/user.controller.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/user/user.module.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/user/user.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/services/error.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/utils/get-cookie-options.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (23)
apps/services/bff/src/app/utils/get-cookie-options.ts (2)

1-2: LGTM! Clean and focused imports.

The imports are appropriate and well-scoped for the functionality.


4-13: Consider enhancing cookie security configuration.

The cookie options implementation follows security best practices with httpOnly, secure, and sameSite: 'lax'. However, consider these improvements:

  1. Add maxAge or expires to explicitly control cookie lifetime
  2. Validate environment.keyPath to ensure it's not undefined
  3. Consider memoizing the function since it returns the same configuration object

Here's a suggested implementation:

+ import { memoize } from 'lodash'
  import { CookieOptions } from 'express'
  import { environment } from '../../environment'

- export const getCookieOptions = (): CookieOptions => {
+ export const getCookieOptions = memoize((): CookieOptions => {
+   if (!environment.keyPath) {
+     throw new Error('Cookie path configuration is missing')
+   }
+
    return {
      httpOnly: true,
      secure: true,
      // The lax setting allows cookies to be sent on top-level navigations (such as redirects),
      // while still providing some protection against CSRF attacks.
      sameSite: 'lax',
      path: environment.keyPath,
+     maxAge: 24 * 60 * 60 * 1000, // 24 hours
    }
- }
+ })

Let's verify the environment configuration:

✅ Verification successful

Let me analyze the environment configuration based on the shell script results.

The environment.keyPath is properly configured in the BFF service:

  1. It's defined in the environment schema (apps/services/bff/src/environment/environment.schema.ts) with validation:

    • Required field with error message
    • Has validation to ensure it doesn't end with '/bff'
  2. It's used consistently across the BFF service:

    • As cookie path in get-cookie-options.ts
    • As global API prefix in main.ts
    • In auth service for client base URL construction
  3. The value comes from BFF_CLIENT_KEY_PATH environment variable with proper schema validation.

Cookie configuration is secure and well-implemented.

The current implementation follows security best practices and the environment configuration is properly validated. The suggestions for improvement can be revised:

  1. maxAge: Not strictly necessary as session cookies (without expiry) are often preferred for auth cookies
  2. environment.keyPath validation: Already handled by the environment schema
  3. Memoization: Not critical as the object is small and the function is likely not called frequently
  • The cookie path is properly validated at startup through Zod schema
  • The implementation is used consistently across the BFF service
  • The security settings (httpOnly, secure, sameSite) are appropriately configured
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if environment.keyPath is properly configured across the codebase

# Check for environment configuration files
echo "Searching for environment configuration files..."
fd -e ts -e js environment

# Check for keyPath usage
echo "Checking keyPath usage..."
rg "keyPath" -A 5 -B 5

Length of output: 17783

apps/services/bff/src/app/modules/user/user.module.ts (3)

2-3: LGTM! Import statements follow NestJS best practices

The new service imports maintain proper module boundaries and follow the expected NestJS architectural patterns.

Also applies to: 5-5


13-19: LGTM! Module configuration follows NestJS patterns

The providers are correctly registered in the module, maintaining proper dependency injection patterns and service encapsulation.


13-19: Verify circular dependencies with AuthModule

Since we're importing AuthModule and also providing TokenRefreshService (which comes from the auth module), we should verify there are no circular dependencies.

✅ Verification successful

Let me gather more information to verify the potential circular dependencies.


Let me check one more aspect to verify if there's a circular dependency through the services.


No circular dependency detected between UserModule and AuthModule

Based on the analysis:

  1. TokenRefreshService is defined in the auth module and is correctly imported by the user module
  2. UserService does depend on TokenRefreshService, but AuthService does not depend on any user module components
  3. The auth module exports its services properly, making them available for use in other modules

The current architecture follows a unidirectional dependency flow from user module to auth module, which is a correct design pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential circular dependencies between UserModule and AuthModule

# Search for UserModule imports in auth module
echo "Checking for UserModule imports in AuthModule:"
rg -l "import.*UserModule" "apps/services/bff/src/app/modules/auth/"

# Search for service dependencies
echo -e "\nChecking for service dependencies:"
ast-grep --pattern 'constructor($$$) {
  $$$
}' "apps/services/bff/src/app/modules/auth/"

Length of output: 346


Script:

#!/bin/bash
# Check imports and providers in both modules

# Check auth.module.ts content
echo "Content of auth.module.ts:"
fd -e ts "auth.module.ts" -x cat {}

echo -e "\nContent of user.module.ts:"
fd -e ts "user.module.ts" -x cat {}

# Check where TokenRefreshService is defined and its dependencies
echo -e "\nTokenRefreshService definition and dependencies:"
rg "class TokenRefreshService" -A 10
rg "import.*TokenRefreshService"

Length of output: 10362


Script:

#!/bin/bash
# Check dependencies in AuthService and UserService

echo "AuthService dependencies:"
rg "class AuthService" -A 10

echo -e "\nUserService dependencies:"
rg "class UserService" -A 10

echo -e "\nCheck if AuthService imports UserService:"
rg "import.*UserService" "apps/services/bff/src/app/modules/auth/"

Length of output: 7449

apps/services/bff/src/app/modules/user/user.controller.ts (2)

1-1: LGTM! Import changes follow NestJS conventions

The addition of Response type and Res decorator imports aligns with NestJS best practices for handling HTTP responses.

Also applies to: 4-11


27-27: Ensure test coverage for response handling

The addition of @Res({ passthrough: true }) requires careful testing to verify:

  1. Error scenarios are properly handled
  2. Response headers and cookies are correctly set
  3. Framework features like interceptors still work as expected
apps/services/bff/src/app/modules/auth/auth.service.ts (5)

29-29: LGTM! Good refactoring of cookie options to utils.

Moving getCookieOptions to a shared utility follows the DRY principle and improves code maintainability.


286-286: LGTM! Appropriate error handling approach.

The simplified error handling is acceptable as any errors from updateTokenCache will be caught by the surrounding try-catch block.


299-299: LGTM! Consistent use of shared cookie options.

The use of the shared getCookieOptions utility maintains consistency across the codebase.


407-407: LGTM! Consistent cookie clearing implementation.

The use of getCookieOptions for clearing cookies maintains consistency with other cookie operations in the service.


206-208: Verify error handling for PAR response.

The removal of error type checking could potentially lead to runtime errors if parResponse is undefined or doesn't contain request_uri.

apps/services/bff/src/app/modules/user/user.service.ts (3)

16-17: Dependencies injected according to NestJS best practices

Adding TokenRefreshService and ErrorService via dependency injection aligns with NestJS patterns for service encapsulation and promotes modularity.


21-23: Good documentation added for mapToBffUser method

Including JSDoc comments enhances code readability and maintainability by clearly explaining the purpose of the method.


31-33: Well-documented method description for getUser

Adding comments provides clarity on the method's functionality, improving understanding for future maintainers.

apps/services/bff/src/app/services/error.service.ts (3)

1-12: Imports are well-organized and follow best practices

The import statements are correctly grouped and use TypeScript's import type where appropriate.


44-50: Type guard isKnownOAuth2ErrorCode is effectively implemented

The use of a type guard enhances type safety when validating error codes.


79-82: Confirm the logic when handling missing sid or UnauthorizedException

Ensure that throwing an UnauthorizedException when sid is missing or the error is an UnauthorizedException aligns with the desired authentication flow.

If this logic is intentional, no action is needed. Otherwise, consider revisiting the condition.

apps/services/bff/src/app/modules/ids/ids.service.ts (1)

36-57: Confirm adherence to NestJS architecture and practices

The implementation of the IdsService follows NestJS best practices by utilizing dependency injection for configurations and services such as BffConfig, EnhancedFetchAPI, and CryptoService. The service methods are well-structured and encapsulated, promoting maintainability and testability.

apps/services/bff/src/app/modules/proxy/proxy.service.ts (4)

12-12: Good use of type-only imports for Request and Response

Using import type helps optimize the compiled output by ensuring only types are imported, which can reduce bundle size.


21-21: Consistent use of SESSION_COOKIE_NAME constant

Replacing hardcoded cookie names with the SESSION_COOKIE_NAME constant improves maintainability and reduces the risk of typos.


70-71: Proper dependency injection of TokenRefreshService and ErrorService

Injecting TokenRefreshService and ErrorService follows NestJS dependency injection patterns and enhances modularity and testability of the service.


189-191: Using HttpStatus constants for clarity in error responses

Utilizing HttpStatus.INTERNAL_SERVER_ERROR instead of hardcoded status codes enhances code readability and ensures consistency across HTTP responses.

Copy link

codecov bot commented Nov 14, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 36.46%. Comparing base (64ad820) to head (6eeab1b).
Report is 27 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #16872      +/-   ##
==========================================
+ Coverage   36.44%   36.46%   +0.02%     
==========================================
  Files        6851     6903      +52     
  Lines      143567   144590    +1023     
  Branches    40980    41288     +308     
==========================================
+ Hits        52328    52730     +402     
- Misses      91239    91860     +621     
Flag Coverage Δ
air-discount-scheme-backend 54.08% <ø> (+0.02%) ⬆️
air-discount-scheme-web 0.00% <ø> (ø)
api 3.34% <ø> (ø)
api-domains-air-discount-scheme 37.08% <ø> (+0.06%) ⬆️
api-domains-assets 26.71% <ø> (ø)
api-domains-auth-admin 48.48% <ø> (ø)
api-domains-communications 39.59% <ø> (-0.04%) ⬇️
api-domains-criminal-record 47.56% <ø> (+0.07%) ⬆️
api-domains-driving-license 44.50% <ø> (+0.03%) ⬆️
api-domains-education 30.62% <ø> (+0.06%) ⬆️
api-domains-health-insurance 34.43% <ø> (+0.09%) ⬆️
api-domains-mortgage-certificate 34.74% <ø> (+0.05%) ⬆️
api-domains-payment-schedule 41.31% <ø> (+0.05%) ⬆️
application-api-files 62.52% <ø> (+0.06%) ⬆️
application-core 70.98% <ø> (+0.23%) ⬆️
application-system-api 41.03% <ø> (+0.03%) ⬆️
application-template-api-modules 27.69% <ø> (+0.04%) ⬆️
application-templates-accident-notification 28.98% <ø> (ø)
application-templates-car-recycling 3.12% <ø> (ø)
application-templates-criminal-record 25.87% <ø> (ø)
application-templates-driving-license 18.14% <ø> (-0.12%) ⬇️
application-templates-estate 12.14% <ø> (ø)
application-templates-example-payment 24.80% <ø> (ø)
application-templates-financial-aid 15.48% <ø> (ø)
application-templates-general-petition 23.07% <ø> (ø)
application-templates-inheritance-report 6.52% <ø> (ø)
application-templates-marriage-conditions 15.04% <ø> (ø)
application-templates-mortgage-certificate 43.36% <ø> (+0.02%) ⬆️
application-templates-parental-leave 29.86% <ø> (ø)
application-types 6.60% <ø> (ø)
application-ui-components 1.27% <ø> (ø)
application-ui-shell 20.83% <ø> (ø)
auth-nest-tools 31.10% <ø> (+0.17%) ⬆️
auth-react 21.85% <ø> (ø)
clients-charge-fjs-v2 24.11% <ø> (ø)
clients-driving-license 40.28% <ø> (+0.08%) ⬆️
clients-driving-license-book 43.50% <ø> (+0.05%) ⬆️
clients-financial-statements-inao 49.06% <ø> (+0.07%) ⬆️
clients-license-client 1.26% <ø> (ø)
clients-middlewares 73.59% <ø> (+0.61%) ⬆️
clients-regulations 42.36% <ø> (+0.08%) ⬆️
clients-rsk-company-registry 29.76% <ø> (ø)
clients-syslumenn 49.23% <ø> (+0.04%) ⬆️
cms 0.41% <ø> (-0.02%) ⬇️
cms-translations 38.91% <ø> (-0.03%) ⬇️
content-search-index-manager 95.65% <ø> (ø)
content-search-toolkit 8.14% <ø> (ø)
contentful-apps 4.69% <ø> (ø)
download-service 44.16% <ø> (+0.02%) ⬆️
financial-aid-backend 51.29% <ø> (+0.02%) ⬆️
financial-aid-shared 17.81% <ø> (ø)
icelandic-names-registry-backend 54.34% <ø> (ø)
island-ui-core 28.88% <ø> (ø)
judicial-system-api 19.86% <ø> (+0.09%) ⬆️
judicial-system-backend 55.53% <ø> (+0.39%) ⬆️
judicial-system-web 27.16% <ø> (ø)
license-api 42.60% <ø> (+0.04%) ⬆️
localization 10.15% <ø> (ø)
nest-audit 68.20% <ø> (ø)
nest-core 43.54% <ø> (ø)
nest-feature-flags 51.09% <ø> (+0.12%) ⬆️
nest-problem 45.82% <ø> (ø)
nest-swagger 51.71% <ø> (ø)
portals-admin-regulations-admin 1.85% <ø> (ø)
portals-core 15.89% <ø> (ø)
reference-backend 49.74% <ø> (ø)
services-auth-admin-api 52.51% <ø> (+0.01%) ⬆️
services-auth-delegation-api 57.51% <ø> (-0.77%) ⬇️
services-auth-ids-api 52.09% <ø> (-0.01%) ⬇️
services-auth-personal-representative 45.63% <ø> (+0.01%) ⬆️
services-auth-personal-representative-public 41.80% <ø> (+0.01%) ⬆️
services-auth-public-api 48.91% <ø> (-0.68%) ⬇️
services-documents 60.81% <ø> (ø)
services-endorsements-api 53.26% <ø> (+0.02%) ⬆️
services-sessions 65.46% <ø> (+<0.01%) ⬆️
services-university-gateway 49.34% <ø> (+0.14%) ⬆️
services-user-notification 46.77% <ø> (-0.15%) ⬇️
services-user-profile 61.89% <ø> (+0.01%) ⬆️
shared-components 26.90% <ø> (ø)
shared-form-fields 31.26% <ø> (ø)
shared-problem 87.50% <ø> (ø)
shared-utils 27.69% <ø> (ø)
skilavottord-ws 24.14% <ø> (ø)
web 1.77% <ø> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

see 137 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 64ad820...6eeab1b. Read the comment docs.

@datadog-island-is
Copy link

datadog-island-is bot commented Nov 14, 2024

Datadog Report

All test runs a253216 🔗

79 Total Test Services: 0 Failed, 77 Passed
🔻 Test Sessions change in coverage: 1 decreased (-0.1%), 2 increased, 197 no change

Test Services
This report shows up to 10 services
Service Name Failed Known Flaky New Flaky Passed Skipped Total Time Code Coverage Change Test Service View
air-discount-scheme-backend 0 0 0 82 0 25.9s N/A Link
air-discount-scheme-web 0 0 0 2 0 8.86s N/A Link
api 0 0 0 4 0 3s N/A Link
api-domains-air-discount-scheme 0 0 0 6 0 18.79s N/A Link
api-domains-assets 0 0 0 3 0 13.35s N/A Link
api-domains-auth-admin 0 0 0 18 0 14.27s N/A Link
api-domains-communications 0 0 0 5 0 36.82s 1 no change Link
api-domains-criminal-record 0 0 0 5 0 10.56s N/A Link
api-domains-driving-license 0 0 0 23 0 32.18s 1 no change Link
api-domains-education 0 0 0 8 0 23.67s N/A Link

🔻 Code Coverage Decreases vs Default Branch (1)

  • application-core - jest 78.59% (-0.1%) - Details

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ce95573 and 08aa8a2.

📒 Files selected for processing (1)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (3)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (3)

34-47: LGTM! Well-documented utility methods

The key creation methods are well-implemented with clear documentation and proper separation of concerns.


102-132: LGTM with previous review comment

The polling implementation has proper cleanup, but please address the previous review comment about using setTimeout instead of setInterval for better async handling.


150-201: 🛠️ Refactor suggestion

Add comprehensive unit tests for the token refresh flow

The refreshToken method handles critical authentication logic and should be thoroughly tested. Key test cases should include:

  • Successful token refresh
  • Concurrent refresh requests
  • Polling timeout and retry
  • Error handling scenarios

Additionally, consider adding integration tests to verify the interaction with Redis cache.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (6)
apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (6)

20-41: Consider moving mock data to separate test fixtures file

The mock data structures are well-defined but would be better organized in a separate test fixtures file for reusability and maintainability.


86-95: Add type annotations to mock implementations

The mock implementations for cache operations would benefit from explicit type annotations for better type safety.

-            save: jest.fn().mockImplementation(async ({ key, value }) => {
+            save: jest.fn().mockImplementation(async ({ key, value }: { key: string; value: unknown }) => {
               mockCacheStore.set(key, value)
             }),
-            get: jest
+            get: jest<unknown>
               .fn()
               .mockImplementation(async (key) => mockCacheStore.get(key)),
-            delete: jest
+            delete: jest<void>
               .fn()
               .mockImplementation(async (key) => mockCacheStore.delete(key)),

157-175: Enhance timeout test case clarity and coverage

The timeout test could be improved by:

  1. Making the timeout duration explicit in the test description
  2. Adding assertions to verify the timing
  3. Testing different timeout scenarios
-    it('should retry refresh if polling times out', async () => {
+    it('should retry refresh after 2000ms polling timeout', async () => {
       // Arrange
+      const startTime = Date.now()
       await cacheService.save({
         key: refreshInProgressKey,
         value: true,
         ttl: 3000,
       })

       // Act
       const result = await service.refreshToken({
         sid: testSid,
         encryptedRefreshToken: testRefreshToken,
       })

       // Assert
+      const duration = Date.now() - startTime
+      expect(duration).toBeGreaterThanOrEqual(2000)
       expect(mockLogger.error).toHaveBeenCalled()
       expect(idsService.refreshToken).toHaveBeenCalledWith(testRefreshToken)
       expect(result).toEqual(mockTokenResponse)
     })

177-193: Add more comprehensive error handling test cases

Consider adding test cases for:

  1. Network errors
  2. Invalid token errors
  3. Rate limiting errors
  4. Server errors with different status codes

Example addition:

it('should handle specific error types appropriately', async () => {
  const errorScenarios = [
    { error: new Error('Network Error'), expectedMessage: 'Network error during token refresh' },
    { error: new Error('Invalid Token'), expectedMessage: 'Invalid refresh token' },
    { error: new Error('Rate Limit Exceeded'), expectedMessage: 'Rate limit exceeded during token refresh' }
  ];

  for (const scenario of errorScenarios) {
    jest.spyOn(idsService, 'refreshToken').mockRejectedValueOnce(scenario.error);
    await expect(service.refreshToken({
      sid: testSid,
      encryptedRefreshToken: testRefreshToken,
    })).rejects.toThrow(scenario.error);
    expect(mockLogger.error).toHaveBeenCalledWith(
      expect.stringContaining(scenario.expectedMessage)
    );
  }
});

195-256: Strengthen concurrent request testing

While the test effectively verifies concurrent request handling, consider enhancing it with:

  1. Verification of request order
  2. Testing with varying delays between requests
  3. Assertion for maximum execution time
it('should maintain request order in concurrent scenarios', async () => {
  const requestOrder: number[] = [];
  const startTimes: number[] = [];
  
  jest.spyOn(idsService, 'refreshToken').mockImplementation(async () => {
    const requestIndex = startTimes.length;
    startTimes.push(Date.now());
    await delay(Math.random() * 50); // Random delay
    requestOrder.push(requestIndex);
    return mockTokenResponse;
  });

  const results = await Promise.all([
    service.refreshToken({ sid: testSid, encryptedRefreshToken: testRefreshToken }),
    service.refreshToken({ sid: testSid, encryptedRefreshToken: testRefreshToken }),
    service.refreshToken({ sid: testSid, encryptedRefreshToken: testRefreshToken })
  ]);

  expect(requestOrder).toEqual([0]); // Only first request should perform refresh
  expect(results).toHaveLength(3);
  expect(Math.max(...startTimes) - Math.min(...startTimes)).toBeLessThan(100); // All requests within 100ms
});

53-257: Consider adding integration and performance tests

While unit tests provide good coverage, consider adding:

  1. Integration tests with the actual authentication flow
  2. Performance benchmarks for the polling mechanism
  3. Load tests for concurrent request handling

This would help verify the service's behavior in real-world scenarios and ensure it meets performance requirements under load.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 08aa8a2 and 37c023d.

📒 Files selected for processing (1)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts (1)

Line range hint 1-234: Consider reorganizing tests for better coverage tracking

While the test structure is good, consider organizing the token refresh related tests into a separate describe block. This would:

  • Make it easier to track coverage of the new token refresh functionality
  • Provide a clear separation between proxy functionality and token management tests
  • Help identify gaps in testing the new polling and concurrency features

Suggested test organization:

describe('Token Refresh Handling', () => {
  // Group all token refresh related tests here
  // - Basic token refresh
  // - Concurrent refresh handling
  // - Polling timeout and retry
  // - Maximum retry attempts
})

describe('Proxy Endpoints', () => {
  // Keep existing proxy endpoint tests here
})
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

34-47: Consider enhancing type safety for cache keys.

The key creation methods are well-structured, but could benefit from type safety improvements.

Consider creating a type for session key types:

type SessionKeyType = 'current' | 'refresh_token_in_progress';

private createRefreshTokenKey(sid: string): `${SessionKeyType}:${string}` {
  return `refresh_token_in_progress:${sid}`;
}

150-209: Consider breaking down the complex refresh logic.

The refreshToken method handles multiple responsibilities. Consider extracting the retry logic and token response validation into separate methods.

Example structure:

private async retryRefreshAfterPollingTimeout(
  refreshTokenKey: string,
  encryptedRefreshToken: string,
): Promise<CachedTokenResponse> {
  // Retry logic here
}

private async validateTokenResponse(
  sid: string,
  tokenResponseKey: string,
): Promise<CachedTokenResponse> {
  // Validation logic here
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 37c023d and ad2ffa7.

📒 Files selected for processing (4)
  • apps/services/bff/src/app/modules/auth/auth.controller.spec.ts (2 hunks)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts (3 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.service.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/services/bff/src/app/modules/proxy/proxy.service.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/services/bff/src/app/modules/auth/auth.controller.spec.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (4)
apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts (1)

42-44: LGTM! Mock implementations are correctly simplified

The mock implementations for refreshToken, getTokens, and revokeToken are properly aligned with the new token refresh service requirements. The simplified return values improve test readability while maintaining functional correctness.

apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1)

1-8: LGTM! Dependencies are properly imported and organized.

The service follows NestJS best practices with proper imports and type definitions.

apps/services/bff/src/app/modules/auth/auth.controller.spec.ts (2)

9-12: LGTM! Import organization improvements

The reordering of imports and renaming of mockedTokensResponse to tokensResponse improves code organization and clarity.


61-63: Verify test coverage for token refresh polling scenarios

The simplified mock implementations align well with the new token refresh polling feature. However, given the PR objectives of preventing multiple simultaneous refresh attempts, we should ensure adequate test coverage.

Let's verify the test coverage:

✅ Verification successful

Test coverage for token refresh polling is comprehensive

The test coverage for token refresh polling scenarios is thorough and well-implemented. The token-refresh.service.spec.ts includes specific tests that verify:

  • Prevention of concurrent refresh token requests
  • Polling behavior and timeout handling
  • Waiting for ongoing refresh operations
  • Error handling during refresh operations
  • Cache management for refresh tokens

The implementation in token-refresh.service.ts aligns with the PR objectives of preventing multiple simultaneous refresh attempts, and the simplified mock implementations in the auth controller tests are appropriate given the comprehensive coverage in the dedicated token refresh service tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check test coverage for token refresh polling scenarios

# Look for test cases covering concurrent token refresh
rg -l "concurrent|simultaneous|polling" "apps/services/bff/src/**/*spec.ts"

# Check for timing-related test utilities
rg -l "fakeTimers|advanceTimersByTime|tick" "apps/services/bff/src/**/*spec.ts"

Length of output: 307


Script:

#!/bin/bash
# Let's try with correct path patterns and check test coverage

# Look for test cases covering concurrent token refresh
rg -l "concurrent|simultaneous|polling" --type spec

# Check for timing-related test utilities
rg -l "fakeTimers|advanceTimersByTime|tick" --type spec

# Get content of the auth controller spec file to analyze test coverage
rg -p "describe.*auth.*controller" --type spec -A 10

# Look for refresh token related tests
rg "refresh.*token" --type spec -A 5

Length of output: 801


Script:

#!/bin/bash
# Let's first find the test files and then analyze them

# Find all test files
fd -t f ".*spec.ts$"

# Find all test files specifically in the auth module
fd -t f "auth.*spec.ts$"

# Try to get content of the specific file we're reviewing
cat "apps/services/bff/src/app/modules/auth/auth.controller.spec.ts"

Length of output: 83649


Script:

#!/bin/bash
# Let's check for token refresh polling tests and implementation

# Find token refresh service tests
cat "apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts"

# Look for token refresh related code in the auth controller
rg "token.*refresh" "apps/services/bff/src/app/modules/auth/"

# Check for any concurrent or polling related tests
rg "concurrent|simultaneous|polling" "apps/services/bff/src/app/modules/auth/"

Length of output: 10798

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
apps/services/bff/src/app/modules/proxy/proxy.service.ts (3)

92-96: Consider caching the session key

The session key is constructed on every request. Consider moving this to a class property or memoizing it to avoid repeated string concatenation.

+  private createTokenResponseKey(sid: string): string {
+    return this.cacheService.createSessionKeyType('current', sid)
+  }

231-239: Add request validation for forwardGetApiRequest

Consider adding validation for the request body and headers before processing.

private validateRequest(req: Request): void {
  if (!req.headers['content-type']?.includes('application/json')) {
    throw new BadRequestException('Invalid content type')
  }
}

244-245: Enhance error message for invalid URL

The error message could be more descriptive to help clients understand the issue.

-      throw new BadRequestException('Proxying url failed!')
+      throw new BadRequestException(`URL '${url}' is not in the allowed external API URLs list`)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ad2ffa7 and d6782fd.

📒 Files selected for processing (1)
  • apps/services/bff/src/app/modules/proxy/proxy.service.ts (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/modules/proxy/proxy.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (2)
apps/services/bff/src/app/modules/proxy/proxy.service.ts (2)

70-71: LGTM: Dependencies properly injected

The addition of TokenRefreshService and ErrorService follows NestJS dependency injection patterns correctly.


108-112: LGTM: Token refresh implementation

The token refresh logic correctly implements the polling mechanism as specified in the PR objectives, preventing multiple simultaneous refresh attempts.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (3)
apps/services/bff/src/app/modules/user/user.controller.spec.ts (3)

18-24: Consider using a more robust caching mechanism for tests

The current implementation uses a simple Map for mocking cache operations. While this works, it might not accurately represent thread-safety concerns that the actual cache manager handles.

Consider using a more sophisticated mock that can simulate concurrent operations:

-const mockCacheStore = new Map()
+class ThreadSafeMockCache {
+  private store = new Map()
+  async set(key: string, value: unknown): Promise<void> {
+    this.store.set(key, value)
+  }
+  async get(key: string): Promise<unknown> {
+    return this.store.get(key)
+  }
+  async del(key: string): Promise<void> {
+    this.store.delete(key)
+  }
+}
+const mockCacheStore = new ThreadSafeMockCache()

54-62: Add type annotations for mock services

The mock service objects lack TypeScript interfaces, which could lead to type mismatches with the actual services.

+type TokenRefreshServiceMock = Partial<TokenRefreshService>
+type IdsServiceMock = Partial<IdsService>

-const mockTokenRefreshService = {
+const mockTokenRefreshService: TokenRefreshServiceMock = {
   refreshToken: jest.fn().mockResolvedValue(mockCachedTokenResponse),
 }

-const mockIdsService = {
+const mockIdsService: IdsServiceMock = {
   getTokens: jest.fn().mockResolvedValue(mockCachedTokenResponse),
   revokeToken: jest.fn().mockResolvedValue(undefined),
   getLoginSearchParams: jest.fn().mockImplementation(getLoginSearchParmsFn),
 }

96-100: Enhance error handling in afterAll

The app closure in afterAll should handle potential errors to ensure proper cleanup.

 afterAll(async () => {
   if (app) {
-    await app.close()
+    try {
+      await app.close()
+    } catch (error) {
+      console.error('Failed to close test app:', error)
+    }
   }
 })
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d6782fd and 32ca2e4.

📒 Files selected for processing (3)
  • apps/services/bff/src/app/modules/auth/auth.controller.spec.ts (3 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts (4 hunks)
  • apps/services/bff/src/app/modules/user/user.controller.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/services/bff/src/app/modules/auth/auth.controller.spec.ts
  • apps/services/bff/src/app/modules/proxy/proxy.controller.spec.ts
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/modules/user/user.controller.spec.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1)

63-92: Enhance error handling granularity

The error handling could be more specific to differentiate between cache operations and token refresh failures.

Consider implementing specific error types:

class TokenRefreshError extends Error {
  constructor(
    message: string,
    public readonly operation: 'CACHE_SAVE' | 'TOKEN_REFRESH' | 'CACHE_UPDATE',
    public readonly cause?: Error
  ) {
    super(message)
  }
}

// Usage:
try {
  await this.cacheService.save(...)
} catch (error) {
  throw new TokenRefreshError('Failed to set refresh flag', 'CACHE_SAVE', error)
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 32ca2e4 and 6eeab1b.

📒 Files selected for processing (2)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
  • apps/services/bff/src/app/modules/user/user.controller.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/services/bff/src/app/modules/user/user.controller.spec.ts
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (2)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

34-47: LGTM! Well-documented utility methods

The key generation methods are well-documented and follow single responsibility principle.


104-134: Refer to existing comment about setInterval usage

The implementation of polling mechanism needs improvement. Please refer to the previous comment about avoiding async functions within setInterval.

Copy link
Member

@eirikurn eirikurn left a comment

Choose a reason for hiding this comment

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

Let's be careful not to log errors for anything that will happen as part of normal operations.

Other then that, just have some code simplification suggestions.

@snaerseljan snaerseljan requested a review from eirikurn November 25, 2024 20:04
@snaerseljan
Copy link
Contributor Author

snaerseljan commented Nov 25, 2024

@eirikurn I simplified the control flow with logging/errors in mind. Can you take a second look 🙏

coderabbitai[bot]
coderabbitai bot previously requested changes Nov 25, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (12)
apps/services/bff/src/app/modules/user/user.service.ts (2)

1-17: Consider maintaining logging capability

The removal of the logger dependency might impact observability, especially for a critical authentication service. Consider retaining logging capabilities through the ErrorService or a dedicated logging service to maintain proper monitoring of authentication flows.


74-79: Enhance type safety in error handling

The error object passed to handleAuthorizedError lacks type information, which could make it harder to maintain and debug error handling logic.

Consider adding a custom error type:

type AuthError = UnauthorizedException | {
  message: string;
  code?: string;
  // Add other relevant error properties
};

// Then update the error handling:
} catch (error: unknown) {
  return this.errorService.handleAuthorizedError({
    error: error as AuthError,
    res,
    tokenResponseKey,
    operation: `${UserService.name}.getUser`,
  });
}
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (3)

14-25: LGTM! Consider environment-specific configuration

The service follows NestJS best practices with proper dependency injection. However, the timing constants should be configurable via environment variables for different deployment environments.

Consider using NestJS ConfigService to make POLL_INTERVAL and MAX_POLL_TIME configurable.


85-115: Consider enhancing polling logs

The polling implementation is solid, but could benefit from more detailed logging to track the polling progress.

Consider adding debug logs for each polling attempt:

 while (attempts < maxAttempts) {
+  this.logger.debug(`Polling attempt ${attempts + 1}/${maxAttempts} for session ${sid}`)
   const refreshTokenInProgress = await this.cacheService.get<boolean>(

161-202: Add comprehensive test coverage

While the implementation is solid, it requires thorough testing to ensure reliability of the concurrent refresh mechanism.

Would you like assistance in generating unit tests for the following scenarios?

  • Concurrent refresh requests
  • Polling timeout and retry
  • Cache miss scenarios
  • Token expiration handling
apps/services/bff/src/app/modules/proxy/proxy.service.ts (2)

79-127: Consider adding retry mechanism for token refresh.

While the token refresh logic is well-structured, consider adding a retry mechanism with exponential backoff for cases where the refresh operation fails due to temporary network issues.

 if (
   cachedTokenResponse &&
   hasTimestampExpiredInMS(cachedTokenResponse.accessTokenExp)
 ) {
+  const maxRetries = 3;
+  let retryCount = 0;
+  while (retryCount < maxRetries) {
+    try {
       cachedTokenResponse = await this.tokenRefreshService.refreshToken({
         sid,
         encryptedRefreshToken: cachedTokenResponse.encryptedRefreshToken,
       })
+      break;
+    } catch (error) {
+      if (retryCount === maxRetries - 1) throw error;
+      retryCount++;
+      await new Promise(resolve => setTimeout(resolve, Math.pow(2, retryCount) * 1000));
+    }
+  }
 }

247-247: Enhance error message for invalid URL.

The current error message 'Proxying url failed!' doesn't provide enough context about why the URL was rejected. Consider including the validation rules or allowed domains in the error message.

-      throw new BadRequestException('Proxying url failed!')
+      throw new BadRequestException(
+        `URL '${url}' is not in the allowed domains. Allowed domains: ${this.config.allowedExternalApiUrls.join(', ')}`
+      )
apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (5)

10-10: Consider moving utility functions to a separate test utilities file

The delay function is a generic test utility that could be reused across different test files.

Consider moving it to a shared test utilities file, e.g., test/utils.ts:

// test/utils.ts
export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

23-42: Extract mock data to test fixtures file

The mock data objects are well-structured but could be moved to a separate fixtures file to improve maintainability and reusability.

Consider creating a test fixtures file, e.g., test/fixtures/auth.fixtures.ts:

// test/fixtures/auth.fixtures.ts
export const mockTokenResponse: CachedTokenResponse = {
  // ... current mock data
};

export const mockIdsTokenResponse: TokenResponse = {
  // ... current mock data
};

Also applies to: 44-52


113-127: Consider adding edge cases for token refresh

While the happy path is well tested, consider adding tests for:

  • Refresh with expired tokens
  • Refresh with invalid tokens
  • Rate limiting scenarios

178-194: Enhance error handling test coverage

The error handling test could be more specific about different types of errors:

  • Network errors
  • Invalid token errors
  • Server errors

Example:

it('should handle specific refresh token errors', async () => {
  const cases = [
    { error: new NetworkError('Connection failed'), expectedLog: 'Network error during token refresh' },
    { error: new InvalidTokenError('Token expired'), expectedLog: 'Invalid refresh token' },
    { error: new ServerError('Internal error'), expectedLog: 'Server error during refresh' }
  ];

  for (const { error, expectedLog } of cases) {
    jest.spyOn(idsService, 'refreshToken').mockRejectedValueOnce(error);
    
    const result = await service.refreshToken({
      sid: testSid,
      encryptedRefreshToken: testRefreshToken,
    });

    expect(result).toBe(null);
    expect(mockLogger.warn).toHaveBeenCalledWith(
      expect.stringContaining(expectedLog),
    );
  }
});

196-256: Refactor concurrent requests test for better readability

The concurrent requests test is comprehensive but could be more maintainable:

  1. Extract the mock implementations to separate helper functions
  2. Use more descriptive variable names
  3. Add comments explaining the test flow

Example refactor:

it('should prevent concurrent refresh token requests', async () => {
  const CONCURRENT_REQUESTS = 5;
  const LOCK_DELAY_MS = 50;
  
  // Setup mock behavior
  const setupCacheMocks = () => {
    let isRefreshInProgress = false;
    
    jest.spyOn(cacheService, 'get')
      .mockImplementation(async (key) => 
        key.includes(refreshInProgressPrefix) ? isRefreshInProgress : mockTokenResponse
      );
      
    jest.spyOn(cacheService, 'save')
      .mockImplementation(async ({ key }) => {
        if (key.includes(refreshInProgressPrefix)) {
          isRefreshInProgress = true;
          await delay(LOCK_DELAY_MS);
        }
      });
      
    jest.spyOn(cacheService, 'delete')
      .mockImplementation(async (key) => {
        if (key.includes(refreshInProgressPrefix)) {
          isRefreshInProgress = false;
        }
      });
  };

  // Setup mocks
  setupCacheMocks();

  // Execute concurrent requests
  const refreshPromises = Array.from({ length: CONCURRENT_REQUESTS }, (_, index) => 
    service.refreshToken({
      sid: testSid,
      encryptedRefreshToken: testRefreshToken,
    })
  );

  const results = await Promise.all(refreshPromises);

  // Verify results
  expect(idsService.refreshToken).toHaveBeenCalledTimes(1);
  results.forEach(result => expect(result).toEqual(mockTokenResponse));
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6eeab1b and 835e96f.

📒 Files selected for processing (6)
  • apps/services/bff/src/app/modules/auth/auth.service.ts (6 hunks)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (1 hunks)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
  • apps/services/bff/src/app/modules/proxy/proxy.service.ts (7 hunks)
  • apps/services/bff/src/app/modules/user/user.service.ts (1 hunks)
  • apps/services/bff/src/app/services/error.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/services/bff/src/app/modules/auth/auth.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/proxy/proxy.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/user/user.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/services/error.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (12)
apps/services/bff/src/app/modules/user/user.service.ts (1)

16-16: Verify TokenRefreshService implementation

The TokenRefreshService is crucial for preventing multiple simultaneous refresh attempts. Let's verify its implementation.

✅ Verification successful

TokenRefreshService implementation verified successfully

The TokenRefreshService implementation in apps/services/bff/src/app/modules/auth/token-refresh.service.ts has been verified and includes:

  • Proper polling mechanism with configurable intervals (POLL_INTERVAL = 200ms) and timeout (MAX_POLL_TIME = 3s)
  • Centralized token refresh handling to prevent multiple simultaneous refresh attempts
  • Cache-based synchronization using CacheService
  • Comprehensive error handling and logging
  • Well-tested implementation with thorough test coverage in token-refresh.service.spec.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for TokenRefreshService implementation to verify polling and concurrency handling
ast-grep --pattern 'class TokenRefreshService {
  $$$
  refreshToken($$$) {
    $$$
  }
  $$$
}'

# Search for tests to verify proper handling of concurrent requests
rg -l "TokenRefreshService.*spec" 

Length of output: 134


Script:

#!/bin/bash
# Let's try a broader search for TokenRefreshService implementation
rg -A 10 "class TokenRefreshService"

# Search for files containing TokenRefreshService
fd -t f "TokenRefreshService"

# Search for any refresh token related implementations
rg -A 5 "refreshToken"

# Look for any test files containing token refresh
rg -l "token.*refresh.*test|token.*refresh.*spec"

Length of output: 73134

apps/services/bff/src/app/modules/auth/token-refresh.service.ts (3)

27-45: LGTM! Well-documented helper methods

The helper methods are focused, well-documented, and follow consistent patterns for key generation.


51-79: LGTM! Robust error handling with proper cleanup

The implementation includes proper error handling with cleanup in the finally block and appropriate warning logs.


121-142: LGTM! Comprehensive token validation

The cache operations include proper null checks and token expiration validation with appropriate warning logs.

apps/services/bff/src/app/modules/proxy/proxy.service.ts (1)

5-5: LGTM! Clean dependency management and service organization.

The changes follow NestJS best practices with proper dependency injection and service organization. The type imports and new service dependencies improve code maintainability and separation of concerns.

Also applies to: 12-12, 21-22, 26-26, 70-71

apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts (1)

108-111: LGTM! Proper test cleanup implemented

The cleanup properly resets both the mock cache store and Jest mocks after each test, preventing test pollution.

apps/services/bff/src/app/modules/auth/auth.service.ts (4)

286-286: Consider potential race conditions in token update

The updateTokenCache operation might be susceptible to race conditions if multiple requests attempt to update the cache simultaneously. Consider using the new TokenRefreshService mentioned in the PR objectives for better concurrency control.

Let's verify the TokenRefreshService implementation:


206-208: Consider security implications of exposing client_id in URL parameters

While the implementation is functionally correct, exposing the client_id in URL parameters might make it visible in server logs and browser history. Consider if this poses any security risks in your context.

Let's check if this is a common pattern in the codebase:


295-298: Ensure atomic cookie operations

The sequence of clearing and setting cookies should be atomic to prevent potential edge cases where requests might be processed between these operations.

Let's check for similar patterns in the codebase:


Line range hint 1-524: Consider architectural improvements for better maintainability

The AuthService has grown quite complex with multiple responsibilities. Consider:

  1. Extracting token management into a separate service (as planned with TokenRefreshService)
  2. Creating a dedicated CookieService for consistent cookie handling
  3. Implementing a more robust error handling strategy with custom error types

This would improve maintainability and make the code more testable.

Let's analyze the current architecture:

apps/services/bff/src/app/services/error.service.ts (2)

81-81: Enhance robustness when extracting errorCode from error

To prevent potential runtime errors, add type checks to ensure error has the expected structure before accessing body.error.


95-95: Avoid replacing errors; consider rethrowing the original error

As previously suggested, rather than throwing a new InternalServerErrorException, rethrow the original error to preserve the stack trace and allow proper handling by the exception middleware.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
apps/services/bff/src/app/services/error.service.ts (1)

47-58: Update JSDoc comments to reflect actual parameters

The JSDoc comment references params.sid, but sid is not a parameter of the handleAuthorizedError method. Please update the documentation to accurately reflect the method's parameters.

Apply this diff to correct the JSDoc comment:

 * @param params Object containing error handling parameters
 * @param params.res - Express Response object for clearing cookies
-* @param params.sid - Session ID of the user
 * @param params.operation - Name of the operation that failed (e.g., 'get user', 'refresh token')
 * @param params.error - The error that was caught
 * @param params.tokenResponseKey - Redis key for the token that needs to be cleaned up
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 835e96f and 001bf0e.

📒 Files selected for processing (1)
  • apps/services/bff/src/app/services/error.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
apps/services/bff/src/app/services/error.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (4)
apps/services/bff/src/app/services/error.service.ts (4)

1-28: Imports and OAuth2 error code definitions are appropriate

The imports are well-organized, and the OAuth2 error codes are accurately defined, aligning with the OAuth2 specification.


30-38: Dependency injection is properly implemented

The ErrorService class correctly uses dependency injection for Logger and CacheService, adhering to NestJS best practices.


43-45: Helper method isKnownOAuth2ErrorCode is correctly implemented

The isKnownOAuth2ErrorCode function accurately checks for known OAuth2 error codes, enhancing error handling efficiency.


59-89: handleAuthorizedError method effectively handles OAuth2 errors

The handleAuthorizedError method systematically manages OAuth2 errors by logging the error, clearing the session cookie, and deleting the cached token. It correctly throws an UnauthorizedException after cleanup, adhering to OAuth2 and NestJS error-handling practices.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
apps/services/bff/src/app/modules/user/user.service.ts (2)

54-65: Enhance type safety for token response handling

Consider using a more explicit type for the cached token response variable initialization.

-      let cachedTokenResponse: CachedTokenResponse | null =
+      let cachedTokenResponse: Readonly<CachedTokenResponse> | null =

74-79: Consider adding specific error types for better error handling

The error handling could be more specific about the types of errors that can occur during token refresh operations.

-    } catch (error) {
+    } catch (error: unknown) {
       return this.errorService.handleAuthorizedError({
         error,
         res,
         tokenResponseKey,
         operation: `${UserService.name}.getUser`,
       })
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

49-51: Consider decoupling key generation from CacheService

The createTokenResponseKey method directly depends on CacheService's implementation details. Consider moving the key format logic to a shared constant or configuration to reduce coupling.

-  private createTokenResponseKey(sid: string): string {
-    return this.cacheService.createSessionKeyType('current', sid)
+  private static readonly TOKEN_KEY_PREFIX = 'current'
+  private createTokenResponseKey(sid: string): string {
+    return `${TokenRefreshService.TOKEN_KEY_PREFIX}:${sid}`
   }

110-125: Consider implementing exponential backoff for polling

The current polling implementation uses a fixed interval. Consider implementing exponential backoff to reduce load on the system during high concurrency scenarios.

+  private calculateBackoff(attempt: number): number {
+    return Math.min(
+      TokenRefreshService.POLL_INTERVAL * Math.pow(2, attempt),
+      TokenRefreshService.MAX_POLL_TIME
+    )
+  }

   while (attempts < maxAttempts) {
     // ... existing polling logic ...

-    await this.delay(TokenRefreshService.POLL_INTERVAL)
+    await this.delay(this.calculateBackoff(attempts))

     attempts++
   }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 001bf0e and 2cb31ac.

📒 Files selected for processing (2)
  • apps/services/bff/src/app/modules/auth/token-refresh.service.ts (1 hunks)
  • apps/services/bff/src/app/modules/user/user.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
apps/services/bff/src/app/modules/auth/token-refresh.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
apps/services/bff/src/app/modules/user/user.service.ts (2)

Pattern apps/services/**/*: "Confirm that the code adheres to the following:

  • NestJS architecture, including modules, services, and controllers.
  • Dependency injection patterns and service encapsulation.
  • Integration and unit testing coverage and practices."

Pattern apps/**/*: "Confirm that the code adheres to the following:

  • NextJS best practices, including file structure, API routes, and static generation methods.
  • Efficient state management and server-side rendering techniques.
  • Optimal use of TypeScript for component and utility type safety."
🔇 Additional comments (5)
apps/services/bff/src/app/modules/user/user.service.ts (2)

1-2: LGTM! Constructor changes align with service responsibilities

The updated dependencies properly support the token refresh functionality while maintaining NestJS architectural patterns.

Also applies to: 7-7, 9-9, 16-17


33-41: Verify integration testing for concurrent token refresh scenarios

The token refresh implementation looks good, but we should ensure proper integration testing for concurrent scenarios.

Also applies to: 61-65

✅ Verification successful

Integration testing for concurrent token refresh is properly implemented

The codebase already includes comprehensive testing for concurrent token refresh scenarios:

  • In token-refresh.service.spec.ts:

    • Tests preventing concurrent refresh token requests with multiple simultaneous calls
    • Tests waiting for ongoing refresh and returning cached results
    • Tests retry mechanism for polling timeouts
    • Tests error handling for refresh token failures
  • In user.controller.spec.ts:

    • Tests token refresh behavior when access token is expired
    • Tests scenarios with refresh enabled and disabled

The implementation is well-tested for concurrent scenarios and meets the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for integration tests covering concurrent token refresh scenarios
rg -t typescript -l "describe.*token.*refresh" "apps/services/bff/src/**/*.(spec|test).ts"

Length of output: 127


Script:

#!/bin/bash
# Search for test files related to token refresh without file type restriction
rg -l "describe.*token.*refresh" "apps/services/bff/src/**/*.(spec|test).ts"

# Also search for test files containing user service tests
rg -l "describe.*UserService" "apps/services/bff/src/**/*.(spec|test).ts"

# Search for any test files that might test concurrent scenarios
rg -l "concurrent|simultaneous|race.condition" "apps/services/bff/src/**/*.(spec|test).ts"

Length of output: 493


Script:

#!/bin/bash
# First, let's find the correct test file paths
fd ".(spec|test).ts$" apps/services/bff/src/

# Then search within those files for token refresh tests
fd ".(spec|test).ts$" apps/services/bff/src/ -x rg -l "describe.*token.*refresh" {}

# Look for user service tests
fd ".(spec|test).ts$" apps/services/bff/src/ -x rg -l "describe.*UserService" {}

# Search for concurrent testing patterns
fd ".(spec|test).ts$" apps/services/bff/src/ -x rg -l "concurrent|simultaneous|race.condition" {}

Length of output: 817


Script:

#!/bin/bash
# Let's examine the token refresh service tests to see what scenarios are covered
rg -A 10 "describe|it\(" apps/services/bff/src/app/modules/auth/token-refresh.service.spec.ts

# Also check the user controller tests for any token refresh related tests
rg -A 10 "describe|it\(" apps/services/bff/src/app/modules/user/user.controller.spec.ts

Length of output: 4855

apps/services/bff/src/app/modules/auth/token-refresh.service.ts (3)

14-25: LGTM! Service setup follows NestJS best practices

The dependency injection is properly implemented with clear separation of concerns.


141-162: LGTM! Cache operations are well implemented

The token validation and cache operations are properly implemented with appropriate error handling and logging.


181-222: LGTM! Public interface is well designed

The method properly handles concurrent requests and implements appropriate retry logic with clear documentation.

Copy link
Member

@eirikurn eirikurn left a comment

Choose a reason for hiding this comment

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

Beautiful 🤩

@snaerseljan snaerseljan added the automerge Merge this PR as soon as all checks pass label Nov 29, 2024
@kodiakhq kodiakhq bot merged commit 918cf3b into main Nov 29, 2024
27 checks passed
@kodiakhq kodiakhq bot deleted the feat/bff-token-refresh-polling branch November 29, 2024 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge Merge this PR as soon as all checks pass
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants