Skip to content

Implement GitHub app authentication#1932

Merged
arkid15r merged 4 commits intomainfrom
ark/extend-github-auth
Jul 30, 2025
Merged

Implement GitHub app authentication#1932
arkid15r merged 4 commits intomainfrom
ark/extend-github-auth

Conversation

@arkid15r
Copy link
Copy Markdown
Collaborator

Proposed change

Add GitHub application authentication in addition to PAT.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jul 30, 2025

Summary by CodeRabbit

  • New Features

    • Introduced GitHub App authentication, supporting both app-based and personal access token authentication for improved integration.
    • Added a management command to retrieve GitHub App installation IDs.
  • Refactor

    • Centralized GitHub authentication logic, updating multiple commands and tests to use the new authentication approach.
    • Consolidated and streamlined secret file handling in deployment scripts and Docker configuration.
  • Bug Fixes

    • Improved error handling and validation for missing or invalid GitHub credentials.
  • Tests

    • Added comprehensive tests for GitHub authentication and installation ID retrieval.
    • Updated existing tests to align with the new authentication mechanism.

Walkthrough

This update introduces centralized GitHub App authentication for backend operations, replacing direct token usage with a new authentication module. The deployment and CI/CD configurations are updated to handle GitHub App credentials and private key file management. Related management commands and their tests are refactored to use the new authentication logic. Additional tests and configuration changes support these updates.

Changes

Cohort / File(s) Change Summary
Docker and Compose Configuration
.dockerignore, docker-compose/production.yaml
.dockerignore updated to exclude *.pem files from build context. Production Docker Compose mounts the GitHub App private key (.github.pem) into the backend container.
Ansible and CI/CD Deployment
.github/ansible/production/nest.yaml, .github/ansible/staging/nest.yaml, .github/workflows/run-ci-cd.yaml
Ansible playbooks consolidate secret file copy tasks into loops and add cleanup tasks; production playbook copies .github.pem. CI/CD workflow removes NEST_GITHUB_TOKEN usage for production, adds GitHub App secrets, and writes the private key file .github.pem with correct permissions for production deployment.
Backend GitHub Authentication Module
backend/apps/github/auth.py
New module implements GitHubAppAuth class and get_github_client() function to provide centralized GitHub App and personal access token (PAT) authentication logic with configuration validation and fallback.
Backend Management Commands (GitHub-related)
backend/apps/github/management/commands/github_add_related_repositories.py, backend/apps/github/management/commands/github_update_owasp_organization.py, backend/apps/github/management/commands/github_update_related_organizations.py, backend/apps/owasp/management/commands/owasp_scrape_projects.py
Refactored to replace direct GitHub client instantiation and token environment variable usage with calls to the new get_github_client() function; removed explicit invalid token error handling where applicable.
New Management Command for Installation ID
backend/apps/github/management/commands/github_get_installation_id.py
Added new Django management command to retrieve and display GitHub App installation IDs, supporting command-line arguments and error handling.
Production Settings
backend/settings/production.py
Added secret configuration variables GITHUB_APP_ID and GITHUB_APP_INSTALLATION_ID to the production settings class.
Backend Authentication Tests
backend/tests/apps/github/auth_test.py
New test module with unit tests covering GitHub App authentication scenarios, private key loading, and client creation for both app and PAT authentication.
Management Command Tests (GitHub-related)
backend/tests/apps/github/management/commands/github_add_related_repositories_test.py, backend/tests/apps/github/management/commands/github_get_installation_id_test.py, backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py, backend/tests/apps/github/management/commands/github_update_related_organizations_test.py
Tests updated to patch get_github_client() instead of github.Github; removed tests for invalid token handling where no longer applicable; added new tests for installation ID command.
OWASP Project Scraping Command Test
backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py
Updated test to patch get_github_client() instead of github.Github.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

backend, ci, deploy, docker, backend-tests

Suggested reviewers

  • kasya

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3131e9 and 80492cf.

📒 Files selected for processing (2)
  • .github/ansible/production/nest.yaml (1 hunks)
  • .github/ansible/staging/nest.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/ansible/staging/nest.yaml
  • .github/ansible/production/nest.yaml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ark/extend-github-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this 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
Copy Markdown
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

🧹 Nitpick comments (4)
backend/apps/github/management/commands/github_get_installation_id.py (1)

10-85: Consider using centralized authentication for consistency.

While the direct GitHub API usage works, this command doesn't leverage the centralized get_github_client function from apps.github.auth that other commands use. Consider refactoring to use the centralized authentication approach for consistency across the codebase.

However, the current implementation is acceptable since this command specifically needs to work with GitHub App authentication at the integration level rather than the client level.

backend/apps/github/auth.py (3)

40-46: Add type hint and consider IOError handling.

The method should include a return type hint for consistency with the rest of the codebase. Also consider catching IOError in addition to the current exceptions for broader error coverage.

-def _load_private_key(self):
+def _load_private_key(self) -> str | None:
     """Load the GitHub App private key from a local file."""
     try:
         with (Path(settings.BASE_DIR) / ".github.pem").open("r") as key_file:
             return key_file.read().strip()
-    except (FileNotFoundError, PermissionError):
+    except (FileNotFoundError, PermissionError, IOError):
         return None

63-74: Use logger.info instead of logger.warning for authentication method.

Using logger.warning for normal authentication method selection is misleading. This should be logger.info or logger.debug since it's expected behavior, not a warning condition.

 if self._is_app_configured():
-    logger.warning("Using GitHub App authentication")
+    logger.info("Using GitHub App authentication")
     return Github(

76-78: Use logger.info instead of logger.warning for PAT authentication.

Same issue as above - using PAT authentication is expected fallback behavior, not a warning condition.

 if self.pat_token:
-    logger.warning("Using GitHub PAT token")
+    logger.info("Using GitHub PAT token")
     return Github(self.pat_token, per_page=per_page)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bd70f1 and d8a0773.

📒 Files selected for processing (17)
  • .dockerignore (1 hunks)
  • .github/ansible/production/nest.yaml (1 hunks)
  • .github/workflows/run-ci-cd.yaml (2 hunks)
  • backend/apps/github/auth.py (1 hunks)
  • backend/apps/github/management/commands/github_add_related_repositories.py (2 hunks)
  • backend/apps/github/management/commands/github_get_installation_id.py (1 hunks)
  • backend/apps/github/management/commands/github_update_owasp_organization.py (2 hunks)
  • backend/apps/github/management/commands/github_update_related_organizations.py (2 hunks)
  • backend/apps/owasp/management/commands/owasp_scrape_projects.py (2 hunks)
  • backend/settings/production.py (1 hunks)
  • backend/tests/apps/github/auth_test.py (1 hunks)
  • backend/tests/apps/github/management/commands/github_add_related_repositories_test.py (2 hunks)
  • backend/tests/apps/github/management/commands/github_get_installation_id_test.py (1 hunks)
  • backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py (2 hunks)
  • backend/tests/apps/github/management/commands/github_update_related_organizations_test.py (2 hunks)
  • backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py (1 hunks)
  • docker-compose/production.yaml (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/module.py:28-29
Timestamp: 2025-07-16T13:49:58.648Z
Learning: In the OWASP Nest mentorship system, mentors can be created with only github_user initially (without nest_user) when assigned to modules. This allows mentors to be assigned before they've signed into the system. When these users eventually sign in, the nest_user is automatically linked to the existing github_user, creating a complete mentor profile. This design provides flexibility in mentor assignment workflows.
Learnt from: Rajgupta36
PR: OWASP/Nest#1634
File: backend/apps/nest/graphql/mutations/user.py:24-48
Timestamp: 2025-06-18T21:00:33.024Z
Learning: GitHub access tokens use specific prefixes to identify token types: OAuth tokens start with `gho_`, classic personal access tokens with `ghp_`, fine-grained personal access tokens with `github_pat_`, GitHub App user tokens with `ghu_`, GitHub App installation tokens with `ghs_`, and refresh tokens with `ghr_`. This is documented in GitHub's official authentication documentation and engineering blog.
docker-compose/production.yaml (2)

Learnt from: ahmedxgouda
PR: #1675
File: backend/apps/owasp/graphql/queries/project_health_metrics.py:13-21
Timestamp: 2025-07-03T03:47:05.436Z
Learning: The OWASP Nest project does not use async operations anywhere in the codebase and doesn't have the required database driver for async operations. All database methods and GraphQL resolvers should remain synchronous to maintain consistency with the existing architecture.

Learnt from: yashgoyal0110
PR: OWASP/Nest#0
File: :0-0
Timestamp: 2025-04-06T06:57:42.144Z
Learning: The OWASP/Nest project uses mypy for static type checking, configured in .pre-commit-config.yaml with settings to exclude tests and use backend/pyproject.toml as the config file.

backend/apps/github/auth.py (1)

Learnt from: Rajgupta36
PR: #1634
File: backend/apps/nest/graphql/mutations/user.py:24-48
Timestamp: 2025-06-18T21:00:33.024Z
Learning: GitHub access tokens use specific prefixes to identify token types: OAuth tokens start with gho_, classic personal access tokens with ghp_, fine-grained personal access tokens with github_pat_, GitHub App user tokens with ghu_, GitHub App installation tokens with ghs_, and refresh tokens with ghr_. This is documented in GitHub's official authentication documentation and engineering blog.

🧬 Code Graph Analysis (6)
backend/apps/github/management/commands/github_add_related_repositories.py (1)
backend/apps/github/auth.py (2)
  • get_github_client (48-80)
  • get_github_client (83-93)
backend/apps/github/management/commands/github_update_owasp_organization.py (1)
backend/apps/github/auth.py (2)
  • get_github_client (48-80)
  • get_github_client (83-93)
backend/apps/github/management/commands/github_update_related_organizations.py (1)
backend/apps/github/auth.py (2)
  • get_github_client (48-80)
  • get_github_client (83-93)
backend/tests/apps/github/auth_test.py (1)
backend/apps/github/auth.py (5)
  • GitHubAppAuth (16-80)
  • get_github_client (48-80)
  • get_github_client (83-93)
  • _is_app_configured (36-38)
  • _load_private_key (40-46)
backend/apps/owasp/management/commands/owasp_scrape_projects.py (1)
backend/apps/github/auth.py (2)
  • get_github_client (48-80)
  • get_github_client (83-93)
backend/tests/apps/github/management/commands/github_get_installation_id_test.py (1)
backend/apps/github/management/commands/github_get_installation_id.py (2)
  • Command (15-113)
  • handle (36-113)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (37)
.dockerignore (1)

18-18: LGTM! Good security practice.

Excluding .pem files from Docker build context prevents sensitive private key files from being accidentally included in Docker images. This aligns well with the GitHub App authentication implementation.

backend/settings/production.py (1)

50-52: LGTM! Proper secret handling for GitHub App credentials.

The addition of GITHUB_APP_ID and GITHUB_APP_INSTALLATION_ID as secret values follows the correct pattern for sensitive configuration and supports the new GitHub App authentication mechanism.

backend/tests/apps/owasp/management/commands/owasp_scrape_projects_test.py (1)

40-40: LGTM! Test correctly updated for centralized GitHub authentication.

The mock patch target has been appropriately updated to reflect the refactoring from direct github.Github instantiation to the centralized get_github_client function, maintaining test coverage.

docker-compose/production.yaml (1)

18-18: Security chain for GitHub App private key is fully configured

I’ve verified each step of the deployment pipeline:

  • Ansible (​.github/ansible/production/nest.yaml​) copies the key from the CI runner into ~/.github.pem on the remote host with mode: '0400'.
  • CI/CD workflow (​.github/workflows/run-ci-cd.yaml​) writes the NEST_GITHUB_APP_PRIVATE_KEY secret to .github.pem and sets chmod 600.
  • Backend (​backend/apps/github/auth.py​) correctly loads the private key from Path(settings.BASE_DIR) / ".github.pem" and falls back safely if missing.

No additional changes are needed—this read-only mount and file-permission setup follow best practices. 🚀

.github/ansible/production/nest.yaml (1)

55-60: LGTM! Secure GitHub App private key deployment.

The Ansible task correctly deploys the GitHub App private key with appropriate security measures:

  • Restrictive file permissions (0400) for owner-only read access
  • Clear task naming and proper placement in the deployment flow
  • Secure handling of sensitive credential files
backend/apps/github/management/commands/github_update_related_organizations.py (1)

8-8: LGTM: Clean refactor to centralized authentication

The import of get_github_client from apps.github.auth and its usage to replace direct GitHub client instantiation aligns perfectly with the GitHub App authentication implementation. This centralized approach provides better maintainability and supports both GitHub App and PAT authentication methods.

Also applies to: 43-43

backend/apps/github/management/commands/github_update_owasp_organization.py (1)

8-8: LGTM: Consistent authentication refactor

The implementation follows the same clean pattern as other management commands, replacing direct GitHub client instantiation with the centralized get_github_client() function. The usage is correct and maintains the existing functionality while enabling GitHub App authentication.

Also applies to: 48-49

backend/apps/github/management/commands/github_add_related_repositories.py (1)

8-8: LGTM: Consistent with authentication refactor pattern

The changes follow the established pattern across all GitHub management commands, replacing direct client instantiation with the centralized authentication approach. Implementation is clean and correct.

Also applies to: 40-40

.github/workflows/run-ci-cd.yaml (1)

654-655: LGTM: Proper GitHub App configuration

Adding the GitHub App ID and installation ID environment variables is correct for supporting the new authentication method.

backend/tests/apps/github/management/commands/github_update_owasp_organization_test.py (1)

75-75: LGTM: Test properly updated for authentication refactor

The test changes correctly adapt to the new centralized authentication approach:

  • Mocking get_github_client instead of github.Github
  • Updating parameter names and assertions accordingly
  • Maintaining the same test coverage and logic

The test will properly validate the command's functionality with the new authentication system.

Also applies to: 79-79, 86-86, 151-151

backend/apps/owasp/management/commands/owasp_scrape_projects.py (2)

9-9: LGTM! Centralized authentication import.

The import change to use get_github_client from the new authentication module aligns with the architectural improvement to centralize GitHub authentication logic.


39-39: LGTM! Simplified client instantiation.

The change from direct GitHub client instantiation to using the centralized get_github_client() function is correct and maintains the same functionality while benefiting from centralized authentication handling.

backend/tests/apps/github/management/commands/github_update_related_organizations_test.py (2)

26-30: LGTM! Updated fixture for centralized authentication.

The fixture correctly updates the mocking strategy to patch get_github_client instead of the github.Github class, aligning with the new centralized authentication approach.


97-97: LGTM! Consistent test mocking update.

The monkeypatch update correctly reflects the command's refactoring to use get_github_client, ensuring test coverage remains intact.

backend/tests/apps/github/management/commands/github_add_related_repositories_test.py (3)

34-34: LGTM! Updated mock patching for centralized authentication.

The mock patch correctly targets get_github_client instead of the github.Github class, aligning with the command's refactoring to use centralized authentication.


40-47: LGTM! Consistent test setup updates.

The test setup correctly updates the mock variable names and return value handling to match the new get_github_client function interface.


77-77: LGTM! Updated test assertion.

The assertion correctly verifies that get_github_client is called once without arguments, matching the expected behavior of the centralized authentication function.

backend/apps/github/management/commands/github_get_installation_id.py (2)

44-78: LGTM! Comprehensive error handling.

The error handling is thorough and user-friendly:

  • Validates app ID presence
  • Check file existence and readability
  • Handles empty private key files
  • Provides clear error messages with guidance

The use of sys.exit(1) is appropriate for management commands to indicate failure.


80-113: LGTM! Well-structured GitHub App integration.

The GitHub App authentication and installation retrieval logic is correct:

  • Proper use of Auth.AppAuth and GithubIntegration
  • Good output formatting with installation details
  • Appropriate exception handling with logging

The code follows GitHub API best practices.

backend/tests/apps/github/management/commands/github_get_installation_id_test.py (3)

10-55: LGTM! Comprehensive test for successful installation retrieval.

The test correctly mocks all GitHub API components and verifies:

  • Proper authentication setup with app ID and private key
  • GitHub integration instantiation and method calls
  • File reading operations

The test structure is well-organized and covers the happy path thoroughly.


79-96: LGTM! Good error condition testing.

The tests for missing app ID and private key file not found correctly use pytest.raises(SystemExit) to verify expected failure behavior. This ensures the command handles error conditions appropriately.


98-151: LGTM! Thorough edge case coverage.

The tests cover important edge cases:

  • Empty private key file handling
  • Custom private key file path support
  • Environment variable app ID sourcing

The mocking strategy is consistent and the assertions verify correct parameter passing to the GitHub API components.

backend/tests/apps/github/auth_test.py (10)

16-28: LGTM! Well-structured initialization test.

The test properly mocks the dependencies and verifies all the expected attributes are set correctly during GitHub App configuration initialization.


30-40: LGTM! Good PAT fallback test coverage.

The test correctly verifies the fallback behavior when GitHub App configuration is not available but a PAT token is provided via environment variable.


42-52: LGTM! Proper error handling test.

The test correctly verifies that a ValueError is raised when neither GitHub App configuration nor PAT token is available, which matches the expected behavior from the implementation.


110-147: LGTM! Comprehensive GitHub App authentication test.

The test properly mocks all the PyGithub authentication components and verifies the correct authentication flow is used when GitHub App credentials are configured. The assertions check that the correct parameters are passed to the authentication constructors.


149-163: LGTM! Good PAT fallback test.

The test correctly verifies that when GitHub App configuration is not available, the system falls back to PAT authentication and creates the client with the expected parameters.


165-179: LGTM! Custom pagination test.

The test properly verifies that custom per_page parameters are passed through correctly to the GitHub client constructor.


196-211: LGTM! Simple and effective convenience function test.

The test properly verifies that the module-level get_github_client function returns the expected GitHub client instance when PAT authentication is available.


68-79: Fix the test logic - redundant initialization.

Similar issue as the previous test - the test creates a GitHubAppAuth instance but then calls the private method directly instead of testing the integrated behavior.

 def test_load_private_key_file_not_found(self):
     """Test private key loading when file doesn't exist."""
     with (
         mock.patch.object(Path, "exists", return_value=False),
         mock.patch("apps.github.auth.settings") as mock_settings,
+        mock.patch.dict(os.environ, {"GITHUB_TOKEN": "fallback-token"}),
     ):
         mock_settings.BASE_DIR = "/test/path"
-        mock_settings.GITHUB_APP_ID = None
-        mock_settings.GITHUB_APP_INSTALLATION_ID = None
-        auth = GitHubAppAuth()
-        private_key = auth._load_private_key()
+        mock_settings.GITHUB_APP_ID = "12345"
+        mock_settings.GITHUB_APP_INSTALLATION_ID = "67890"
+        auth = GitHubAppAuth()
         assert private_key is None

Likely an incorrect or invalid review comment.


181-193: Fix the test logic - authentication error scenario.

The test mocks Github to raise BadCredentialsException, but this exception would be raised during client instantiation, not during the authentication process itself. The actual implementation doesn't catch this exception in get_github_client.

 def test_get_github_client_authentication_error(self):
-    """Test GitHub client creation with authentication error."""
+    """Test GitHub client creation with no valid credentials."""
     with (
         mock.patch("apps.github.auth.settings") as mock_settings,
-        mock.patch.dict(os.environ, {"GITHUB_TOKEN": "invalid-token"}),
-        mock.patch("apps.github.auth.Github") as mock_github,
+        mock.patch.dict(os.environ, {}, clear=True),
+        mock.patch.object(GitHubAppAuth, "_load_private_key", return_value=None),
     ):
         mock_settings.GITHUB_APP_ID = None
         mock_settings.GITHUB_APP_INSTALLATION_ID = None
         auth = GitHubAppAuth()
-        mock_github.side_effect = BadCredentialsException(401, "Invalid token", None)
         with pytest.raises(BadCredentialsException):
             auth.get_github_client()

Likely an incorrect or invalid review comment.


54-66: Fix the test logic - redundant initialization.

The test calls GitHubAppAuth() on line 64 but then calls auth._load_private_key() on line 65, which tests the method in isolation rather than the integrated behavior during initialization.

 def test_load_private_key_success(self):
     """Test successful private key loading from file."""
     with (
         mock.patch.object(Path, "exists", return_value=True),
         mock.patch.object(Path, "open", mock.mock_open(read_data="test-private-key")),
         mock.patch("apps.github.auth.settings") as mock_settings,
+        mock.patch.dict(os.environ, {"GITHUB_TOKEN": "fallback-token"}),
     ):
         mock_settings.BASE_DIR = "/test/path"
-        mock_settings.GITHUB_APP_ID = None
-        mock_settings.GITHUB_APP_INSTALLATION_ID = None
-        auth = GitHubAppAuth()
-        private_key = auth._load_private_key()
+        mock_settings.GITHUB_APP_ID = "12345"
+        mock_settings.GITHUB_APP_INSTALLATION_ID = "67890"
+        auth = GitHubAppAuth()
         assert private_key == "test-private-key"

Likely an incorrect or invalid review comment.

backend/apps/github/auth.py (5)

1-14: LGTM! Clean module structure and imports.

The module is well-organized with appropriate imports and logging setup. The docstring clearly describes the module's purpose.


19-34: LGTM! Comprehensive initialization with good error messaging.

The initialization properly loads configuration from Django settings and environment variables, with clear error messaging when neither authentication method is configured. The error message provides helpful guidance for resolving configuration issues.


36-38: LGTM! Clear configuration validation.

The method correctly checks that all required GitHub App configuration components are present and non-empty.


83-93: LGTM! Clean convenience function.

The module-level function provides a clean interface for consumers who don't need to manage the GitHubAppAuth instance directly. The docstring is comprehensive and the implementation is straightforward.


80-80: Add GitHub token prefix validation before authentication

Our search didn’t find any existing checks for known GitHub token prefixes (ghp_, github_pat_, gho_, ghu_, ghs_, ghr_). To surface malformed tokens earlier and provide clearer errors, add a prefix check in backend/apps/github/auth.py (around line 80) before invoking GitHub’s API:

--- a/backend/apps/github/auth.py
+++ b/backend/apps/github/auth.py
@@ -78,6 +78,14 @@ def authenticate_with_github(token: str) -> User:
     # existing setup code...
 
+    # Validate token format to catch invalid prefixes early
+    prefixes = ("ghp_", "github_pat_", "gho_", "ghu_", "ghs_", "ghr_")
+    if not any(token.startswith(p) for p in prefixes):
+        raise BadCredentialsException(
+            400,
+            f"Invalid GitHub token prefix. Expected one of: {', '.join(prefixes)}",
+            None,
+        )
+
     # call out to GitHub API…
     if response.status_code == 401:
         raise BadCredentialsException(401, "Invalid GitHub credentials", None)

• Location: backend/apps/github/auth.py around line 80
• Purpose: Ensure PATs use a valid prefix and improve error messaging

Comment thread .github/workflows/run-ci-cd.yaml
Comment thread backend/tests/apps/github/auth_test.py
Comment thread backend/tests/apps/github/auth_test.py
Copy link
Copy Markdown
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

🧹 Nitpick comments (1)
.github/ansible/staging/nest.yaml (1)

37-46: Add no_log + keep module naming consistent

  1. Secrets copy will expose filenames and a “changed” diff in the task output. Add no_log: true to suppress any accidental leakage in verbose logs.
  2. Elsewhere in this play you fully-qualify modules (ansible.builtin.copy). For consistency (and future‐proofing against collections shadowing), do the same here.
-      - name: Copy secrets
-        copy:
+      - name: Copy secrets
+        ansible.builtin.copy:
           src: '{{ github_workspace }}/{{ item }}'
           dest: ~/
           mode: '0400'
+          no_log: true
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8a0773 and f3131e9.

📒 Files selected for processing (3)
  • .github/ansible/production/nest.yaml (1 hunks)
  • .github/ansible/staging/nest.yaml (1 hunks)
  • backend/tests/apps/github/management/commands/github_get_installation_id_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/ansible/production/nest.yaml
  • backend/tests/apps/github/management/commands/github_get_installation_id_test.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/module.py:28-29
Timestamp: 2025-07-16T13:49:58.648Z
Learning: In the OWASP Nest mentorship system, mentors can be created with only github_user initially (without nest_user) when assigned to modules. This allows mentors to be assigned before they've signed into the system. When these users eventually sign in, the nest_user is automatically linked to the existing github_user, creating a complete mentor profile. This design provides flexibility in mentor assignment workflows.
Learnt from: Rajgupta36
PR: OWASP/Nest#1634
File: backend/apps/nest/graphql/mutations/user.py:24-48
Timestamp: 2025-06-18T21:00:33.024Z
Learning: GitHub access tokens use specific prefixes to identify token types: OAuth tokens start with `gho_`, classic personal access tokens with `ghp_`, fine-grained personal access tokens with `github_pat_`, GitHub App user tokens with `ghu_`, GitHub App installation tokens with `ghs_`, and refresh tokens with `ghr_`. This is documented in GitHub's official authentication documentation and engineering blog.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)

Comment thread .github/ansible/staging/nest.yaml
@sonarqubecloud
Copy link
Copy Markdown

@arkid15r arkid15r marked this pull request as ready for review July 30, 2025 22:09
@arkid15r arkid15r requested a review from kasya July 30, 2025 22:09
Copy link
Copy Markdown
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

Looks great! 🔥

@arkid15r arkid15r added this pull request to the merge queue Jul 30, 2025
Merged via the queue into main with commit 44153bf Jul 30, 2025
24 checks passed
@arkid15r arkid15r deleted the ark/extend-github-auth branch July 30, 2025 23:16
@coderabbitai coderabbitai bot mentioned this pull request Oct 14, 2025
4 tasks
@coderabbitai coderabbitai bot mentioned this pull request Feb 21, 2026
4 tasks
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