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: add encryption for api keys #331

Merged
merged 6 commits into from
Sep 26, 2024
Merged

feat: add encryption for api keys #331

merged 6 commits into from
Sep 26, 2024

Conversation

Harish-osmosys
Copy link
Contributor

@Harish-osmosys Harish-osmosys commented Sep 24, 2024

API PR Checklist

Pre-requisites

  • I have gone through the Contributing guidelines for Submitting a Pull Request (PR) and ensured that this is not a duplicate PR.
  • I have performed unit testing for the new feature added or updated to ensure the new features added are working as expected.
  • I have added/updated test cases to the test suite as applicable.
  • I have performed preliminary testing using the test suite to ensure that any existing features are not impacted and any new features are working as expected as a whole.
  • I have added/updated the required api docs as applicable.
  • I have added/updated the .env.example file with the required values as applicable.

PR Details

PR details have been updated as per the given format (see below)

  • PR title adheres to the format specified in guidelines (e.g., feat: add admin login endpoint)
  • Description has been added
  • Related changes have been added (optional)
  • Screenshots have been added (optional)
  • Query request and response examples have been added (as applicable, in case added or updated)
  • Documentation changes have been listed (as applicable)
  • Test suite/unit testing output is added (as applicable)
  • Pending actions have been added (optional)
  • Any other additional notes have been added (optional)

Additional Information

  • Appropriate label(s) have been added (ready for review should be added if the PR is ready to be reviewed)
  • Assignee(s) and reviewer(s) have been added (optional)

Note: Reviewer should ensure that the checklist and description have been populated and followed correctly, and the PR should be merged only after resolving all conversations and verifying that CI checks pass.


Description:

This Pr does the following things

  • Add a new graph api to generate new server api keys
  • Add encryption to the api key
  • Add a new column MaskedKey
  • update the logic to verify the key

Related changes:

  • Add new migration to add a masked Key column in server-api table

Screenshots:
// api

image

//db
image

Query request and response:

  • cURL to add new server api key
curl --location 'http://localhost:3000/graphql' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkFkbWluIiwidXNlcklkIjoxLCJyb2xlIjoxLCJpYXQiOjE3MjcxNzQ5OTMsImV4cCI6MTcyOTc2Njk5M30.a6g-OIdUcAZiyNcJ_AxRgyIWEbGnVnWRK5EVOsuJEWQ' \
--data '{"query":"mutation GenerateApiKey($applicationId: Int!) {\n  generateApiKey(applicationId: $applicationId)\n}","variables":{"applicationId":1}}'

Documentation changes:

NA
Test suite/unit testing output:

NA
Pending actions:

NA
Additional notes:

NA

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new method for generating random API keys for applications, enhancing user access to API functionality.
    • Added a masked API key feature for improved security in the API key management process.
  • Improvements

    • Enhanced API key validation logic to support multiple keys for flexibility.
    • Updated password and API key handling for better security and configurability.
  • Bug Fixes

    • Adjusted the return type of API key retrieval methods to ensure accurate data handling.

These updates aim to improve security, flexibility, and user experience within the application.

Copy link

coderabbitai bot commented Sep 24, 2024

Walkthrough

The changes in this pull request enhance the API key management system by modifying the ApiKeyGuard to validate multiple API keys associated with an application ID. Additionally, the bcrypt utility has been updated to allow configurable salt rounds and includes new methods for hashing and comparing API keys. The ServerApiKeysService has been adjusted to return an array of API keys and includes a new method for generating random API keys. These updates collectively improve the flexibility and security of API key handling.

Changes

File Path Change Summary
apps/api/src/common/guards/api-key/api-key.guard.ts Modified ApiKeyGuard to validate multiple API keys associated with an application ID instead of a single entry.
apps/api/src/common/utils/bcrypt.ts Updated encodePassword to use configurable salt rounds; added hashApiKey and compareApiKeys functions for enhanced security.
apps/api/src/modules/server-api-keys/server-api-keys.service.ts Updated findByRelatedApplicationId to return an array of ServerApiKey objects; added generateApiKey method for creating new API keys.

Possibly related PRs

  • feat: update auth guard code #330: The changes in this PR involve a transition from using server API keys to JWT for authentication, which is directly related to the modifications made in the main PR regarding API key validation logic.

Suggested reviewers

  • xixas

Poem

🐰 In the garden of code, changes bloom bright,
API keys dancing, a secure delight.
With masks for protection, they hop and they play,
Generating new paths in a safe, clever way.
So let’s celebrate this code, oh so fine,
A leap towards safety, in each little line! 🌼


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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

@Harish-osmosys Harish-osmosys marked this pull request as ready for review September 24, 2024 12:37
Copy link

@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: 7

Outside diff range and nitpick comments (11)
apps/api/src/database/migrations/1727156770079-AddMaskedKeyColumn.ts (2)

4-14: LGTM: 'up' method correctly adds the new column.

The method properly adds a 'masked_api_key' column to the 'notify_server_api_keys' table. The column definition is appropriate for storing a masked API key.

Consider adding a comment explaining the purpose of the 'masked_api_key' column for better code documentation. For example:

// Add 'masked_api_key' column to store partially obscured API keys for display purposes
await queryRunner.addColumn(
  // ... (rest of the code remains the same)
);

1-18: LGTM: Migration successfully implements the required changes for API key encryption.

This migration file correctly adds a 'masked_api_key' column to the 'notify_server_api_keys' table, which aligns with the PR objective of implementing encryption for API keys. The migration is well-structured, follows TypeORM conventions, and is reversible.

As this migration is part of a larger feature to encrypt API keys, ensure that:

  1. The actual encryption of API keys is implemented in the application logic.
  2. The 'masked_api_key' column is populated with partially visible versions of the encrypted keys.
  3. Any code that previously accessed the plain API keys is updated to use the encrypted versions.
  4. The API documentation is updated to reflect these changes in key handling.
apps/api/src/modules/server-api-keys/server-api-keys.resolver.ts (2)

7-10: LGTM: Class definition is well-structured and secure.

The ServerApiKeysResolver class is correctly decorated and follows best practices:

  • @Resolver(() => ServerApiKey) properly associates the resolver with the ServerApiKey entity.
  • @UseGuards(GqlAuthGuard) ensures that all resolver methods are protected by authentication.
  • Constructor injection of ServerApiKeysService follows dependency injection principles.

Consider adding error handling for cases where ServerApiKeysService initialization fails. For example:

constructor(private serverApiKeysService: ServerApiKeysService) {
  if (!serverApiKeysService) {
    throw new Error('ServerApiKeysService is not initialized');
  }
}

12-17: LGTM: Mutation method is well-defined, but could benefit from additional safeguards.

The generateApiKey mutation is correctly implemented:

  • It's properly decorated with @Mutation(() => String).
  • The applicationId argument is correctly typed and decorated.
  • The method correctly delegates to the ServerApiKeysService.

Consider adding input validation and error handling:

  1. Validate applicationId:
if (applicationId <= 0) {
  throw new Error('Invalid applicationId');
}
  1. Add try-catch for error handling:
@Mutation(() => String)
async generateApiKey(
  @Args('applicationId', { type: () => Int }) applicationId: number,
): Promise<string> {
  if (applicationId <= 0) {
    throw new Error('Invalid applicationId');
  }
  try {
    return await this.serverApiKeysService.generateApiKey(applicationId);
  } catch (error) {
    // Log the error
    console.error('Failed to generate API key:', error);
    throw new Error('Failed to generate API key');
  }
}

These additions will improve the robustness of the mutation.

apps/api/src/common/utils/bcrypt.ts (3)

11-12: Approved: Good use of configurable SALT_ROUNDS.

The change to use SALT_ROUNDS from configuration improves flexibility. However, consider making this function asynchronous for better performance in high-load scenarios.

Here's a suggested asynchronous version:

export async function encodePassword(rawPassword: string): Promise<string> {
  const salt = await bcrypt.genSalt(this.SALT_ROUNDS);
  return bcrypt.hash(rawPassword, salt);
}

This change would make the function consistent with the newly added asynchronous API key functions.


19-25: Approved: Well-implemented API key encryption function.

The new encryptApiKey function is well-implemented, using async/await for better performance and combining the API key with a SECRET for enhanced security. The use of configurable SALT_ROUNDS is consistent with the password encoding function.

Consider adding error handling to make the function more robust:

export const encryptApiKey = async (originalApiKey: string): Promise<string> => {
  try {
    const keyToHash = `${originalApiKey}${this.SECRET}`;
    const salt = await bcrypt.genSalt(this.SALT_ROUNDS);
    const encryptedApiKey = await bcrypt.hash(keyToHash, salt);
    return encryptedApiKey;
  } catch (error) {
    // Log the error or handle it as appropriate for your application
    throw new Error('Failed to encrypt API key');
  }
};

This change would help catch and handle any unexpected errors during the encryption process.


27-35: Approved: Well-implemented API key comparison function.

The new compareApiKeys function is well-implemented, using async/await for better performance and combining the API key with the SECRET before comparison, which is consistent with the encryption function. The use of bcrypt.compare is the correct approach for comparing hashed values.

Consider adding error handling to make the function more robust:

export const compareApiKeys = async (
  originalApiKey: string,
  encryptedApiKey: string,
): Promise<boolean> => {
  try {
    const keyToCompare = `${originalApiKey}${this.SECRET}`;
    const isMatch = await bcrypt.compare(keyToCompare, encryptedApiKey);
    return isMatch;
  } catch (error) {
    // Log the error or handle it as appropriate for your application
    throw new Error('Failed to compare API keys');
  }
};

This change would help catch and handle any unexpected errors during the comparison process.

apps/api/src/modules/server-api-keys/server-api-keys.service.ts (3)

8-9: Remove unnecessary bcrypt import after replacing API key generation.

Since you're replacing bcrypt.genSalt() with crypto.randomBytes() for API key generation, the import of bcrypt is no longer needed. Removing unused imports helps keep the codebase clean.

Apply this diff to remove the unused import:

-import * as bcrypt from 'bcrypt';

36-36: Review the masking strategy to prevent information leakage.

Revealing the first and last 4 characters of the API key may expose too much information, potentially aiding attackers in compromising the key. Consider reducing the number of exposed characters or modifying the masking pattern.

You could modify the masking to reveal fewer characters:

-    const maskedApiKey = `${originalApiKey.slice(0, 4)}****${originalApiKey.slice(-4)}`;
+    const maskedApiKey = `${originalApiKey.slice(0, 2)}****${originalApiKey.slice(-2)}`;

44-44: Add error handling when saving the API key to the repository.

If save(serverApiKey) fails due to a database error or other issue, the error will propagate unhandled. Consider adding error handling to manage potential exceptions and provide meaningful feedback.

Example of adding error handling:

try {
  await this.serverApiKeyRepository.save(serverApiKey);
} catch (error) {
  // Handle the error appropriately, e.g., log the error and throw a custom exception
}
apps/api/src/common/guards/api-key/api-key.guard.ts (1)

98-109: Potential security risk if API keys are not handled properly

Storing and handling API keys requires careful attention to security best practices to prevent leaks or unauthorized access.

Ensure that:

  • API keys are stored securely, preferably hashed and salted.
  • Access to API keys is restricted and audited.
  • Logs do not contain sensitive information.

Review the overall API key management strategy to align with security standards.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between e531301 and 56181a7.

Files selected for processing (7)
  • apps/api/src/common/guards/api-key/api-key.guard.ts (2 hunks)
  • apps/api/src/common/utils/bcrypt.ts (1 hunks)
  • apps/api/src/database/migrations/1727156770079-AddMaskedKeyColumn.ts (1 hunks)
  • apps/api/src/modules/server-api-keys/entities/server-api-key.entity.ts (1 hunks)
  • apps/api/src/modules/server-api-keys/server-api-keys.module.ts (1 hunks)
  • apps/api/src/modules/server-api-keys/server-api-keys.resolver.ts (1 hunks)
  • apps/api/src/modules/server-api-keys/server-api-keys.service.ts (2 hunks)
Additional comments not posted (9)
apps/api/src/modules/server-api-keys/server-api-keys.module.ts (2)

5-5: LGTM! Changes align with NestJS best practices.

The addition of ServerApiKeysResolver to both the imports and providers is correct. This change makes the resolver available for dependency injection within the module, which is necessary for handling GraphQL operations related to server API keys.

Also applies to: 9-9


5-5: Verify encryption implementation for API keys.

While these changes are correct, they don't directly address the PR's main objective of implementing encryption for API keys. Could you please clarify where the encryption logic is implemented? Is it within the ServerApiKeysResolver, or is it handled elsewhere in the codebase?

To help verify the implementation, you can run the following script:

This script will help us locate where the encryption logic is implemented within the server-api-keys module.

Also applies to: 9-9

Verification successful

Encryption for API keys is correctly implemented within the ServerApiKeysService.

  • The encryptApiKey function from src/common/utils/bcrypt is used to encrypt API keys before storage.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for encryption-related code in the ServerApiKeysResolver and related files

# Check ServerApiKeysResolver for encryption logic
echo "Checking ServerApiKeysResolver for encryption logic:"
rg --type typescript -i "encrypt|cipher|crypto" apps/api/src/modules/server-api-keys/server-api-keys.resolver.ts

# Check ServerApiKeysService for encryption logic
echo "Checking ServerApiKeysService for encryption logic:"
rg --type typescript -i "encrypt|cipher|crypto" apps/api/src/modules/server-api-keys/server-api-keys.service.ts

# Check for any new files related to encryption in the server-api-keys module
echo "Checking for new encryption-related files:"
fd -e ts -d 1 encrypt apps/api/src/modules/server-api-keys

Length of output: 682


Script:

#!/bin/bash
# Description: Search for encryption-related code in the ServerApiKeysResolver and related files without specifying file type

# Check ServerApiKeysResolver for encryption logic
echo "Checking ServerApiKeysResolver for encryption logic:"
rg -i "encrypt|cipher|crypto" apps/api/src/modules/server-api-keys/server-api-keys.resolver.ts

# Check ServerApiKeysService for encryption logic
echo "Checking ServerApiKeysService for encryption logic:"
rg -i "encrypt|cipher|crypto" apps/api/src/modules/server-api-keys/server-api-keys.service.ts

# Check for any encryption-related code within all .ts files in the server-api-keys module
echo "Searching all TypeScript files in server-api-keys for encryption logic:"
rg -i "encrypt|cipher|crypto" apps/api/src/modules/server-api-keys/**/*.ts

Length of output: 829

apps/api/src/database/migrations/1727156770079-AddMaskedKeyColumn.ts (3)

1-1: LGTM: Imports are correct and necessary.

The imports from 'typeorm' are appropriate for creating a database migration using TypeORM.


3-3: LGTM: Class definition follows TypeORM migration conventions.

The class name AddMaskedKeyColumn1727156770079 includes a timestamp and implements MigrationInterface, which is the correct approach for TypeORM migrations.


15-17: LGTM: 'down' method correctly reverts the migration.

The method properly drops the 'masked_api_key' column from the 'notify_server_api_keys' table, effectively reverting the changes made in the 'up' method.

apps/api/src/modules/server-api-keys/server-api-keys.resolver.ts (1)

1-5: LGTM: Imports are appropriate and aligned with the resolver's functionality.

The imports cover all necessary decorators, guards, and services required for implementing the API key generation resolver. They align well with the PR objective of adding encryption for API keys.

apps/api/src/common/utils/bcrypt.ts (1)

1-35: Overall: Good implementation of API key encryption, aligning with PR objectives.

The changes in this file successfully implement the encryption and comparison of API keys, which aligns well with the PR objective of "feat: add encryption for api keys". The use of ConfigService for retrieving configuration values enhances flexibility and security. The new functions for API key handling are well-implemented and consistent with existing patterns.

Some suggestions for further improvement:

  1. Consider injecting ConfigService instead of instantiating it directly.
  2. Add error handling to the new API key functions.
  3. Consider making the encodePassword function asynchronous for consistency and potential performance benefits.

These changes have significantly enhanced the security of API key handling in the application.

apps/api/src/modules/server-api-keys/entities/server-api-key.entity.ts (1)

26-28: Approve addition of maskedApiKey property with suggestions for improvement

The addition of the maskedApiKey property aligns well with the PR objective of implementing encryption for API keys. The property is correctly annotated for both database storage and GraphQL schema.

However, I have a few suggestions and questions:

  1. Could you clarify the encryption process and the relationship between apiKey and maskedApiKey? It would be helpful to understand how these two properties interact.

  2. Consider adding validation for the maskedApiKey format. For example, you might want to ensure it follows a specific pattern or length.

  3. Remember to update any related documentation to reflect this new property.

  4. As mentioned in the PR objectives, ensure that the .env.example file is updated if any new environment variables are required for this feature.

To verify the implementation of the masking process, please run the following script:

apps/api/src/modules/server-api-keys/server-api-keys.service.ts (1)

24-31: Ensure all usages of findByRelatedApplicationId handle the array return type.

The method findByRelatedApplicationId now returns a Promise<ServerApiKey[]> instead of Promise<ServerApiKey | undefined>. Verify that all callers of this method are updated to handle an array of ServerApiKey objects to prevent runtime errors.

Run the following script to find all usages of findByRelatedApplicationId and review how the return value is handled:

Verification successful

All usages of findByRelatedApplicationId handle the array return type appropriately.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of `findByRelatedApplicationId` to ensure callers handle the array return type.

# Search for usages of the method in TypeScript files.
rg --type ts 'findByRelatedApplicationId\(' -A 5

Length of output: 1183

Copy link
Collaborator

@xixas xixas left a comment

Choose a reason for hiding this comment

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

Check comments from code rabbit too.

apps/api/src/common/utils/bcrypt.ts Show resolved Hide resolved
@xixas xixas merged commit 9a3f7c1 into main Sep 26, 2024
6 checks passed
@xixas xixas deleted the feat/encrypt-api-keys branch September 26, 2024 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants