Skip to content

GitHub OAuth Security Enhancement#12

Open
akshayutture-augment wants to merge 1 commit into
oauth-state-vulnerablefrom
oauth-state-secure
Open

GitHub OAuth Security Enhancement#12
akshayutture-augment wants to merge 1 commit into
oauth-state-vulnerablefrom
oauth-state-secure

Conversation

@akshayutture-augment

@akshayutture-augment akshayutture-augment commented Nov 14, 2025

Copy link
Copy Markdown

Test 4

Summary by CodeRabbit

Release Notes

  • New Features

    • GitHub integration now includes an OAuth authorization step during setup
  • Bug Fixes

    • Prevents duplicate GitHub installations across organizations
    • Validates user authentication when detecting existing installations
    • Enhanced error messaging for installation failures with standardized error pages
  • Improvements

    • Better error page rendering with customer domain support

Replicated from ai-code-review-evaluation/sentry-coderabbit#4

…#67876)

We're adding one more step in the GitHub integration installation
pipeline, namely GitHub OAuth2 authorize. This is transparent from the
UX perspective as the data exchange happens without user interaction.

The pipeline will now fail in these cases:
- If there is a mismatch between currently authenticated GitHub user
(derived from OAuth2 authorize step) and the user who installed the
GitHub app (https://github.com/apps/sentry-io)
- If there is a mismatch between `state` parameter supplied by user and
pipeline signature
- If GitHub could not generate correct `access_token` from the `code`
(wrong or attempt of re-use of `code`).

In all those cases, this error is shown:

![image](https://github.com/getsentry/sentry/assets/1127549/18923861-2ead-4cf5-adda-7738aef801d7)
@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 18, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Nov 18, 2025

Copy link
Copy Markdown

Walkthrough

The changes introduce OAuth user authorization into the GitHub integration flow via a new OAuthLoginView, augment installation validation to verify authenticated user consistency, implement error handling for cross-organization installations, and simplify pipeline dispatcher logic by replacing constant-based routing with direct string comparison.

Changes

Cohort / File(s) Change Summary
GitHub Integration Core
src/sentry/integrations/github/integration.py
Introduces OAuthLoginView class to handle GitHub OAuth login flow; adds error() and get_document_origin() utilities for standardized error page rendering with document-origin awareness; adds ERR_INTEGRATION_INVALID_INSTALLATION_REQUEST constant; augments GitHubInstallation.dispatch() to validate installation authenticity by checking sender consistency across orgs; updates get_pipeline_views() in both GitHubIntegration and GitHubIntegrationProvider to include OAuthLoginView() in the pipeline sequence; imports token exchange, URL parsing, and GitHub identity helpers.
Pipeline Dispatcher Refactoring
src/sentry/web/frontend/pipeline_advancer.py
Removes FORWARD_INSTALL_FOR constant; replaces constant membership check with direct string comparison (provider_id == "github") for installation org picker redirect logic.
Integration Test Suite
tests/sentry/integrations/github/test_integration.py
Adds OAuth token and user endpoint stubs; extends setup flow assertions to validate redirect paths and query parameters; introduces additional setup-path invocations to simulate post-installation OAuth completion; adds test scenarios for user sender mismatch validation, repository retrieval with pagination, stacktrace link resolution edge cases, and pending installation re-flows.

Sequence Diagram

sequenceDiagram
    participant User
    participant Integration as GitHub Integration
    participant GitHubAPI as GitHub API
    participant Pipeline as Pipeline State
    
    rect rgb(220, 245, 220)
    Note over User,Pipeline: New OAuth Login Step
    User->>Integration: Start installation
    Integration->>GitHubAPI: Redirect to GitHub OAuth
    GitHubAPI->>User: Request user authorization
    User->>GitHubAPI: Authorize app
    GitHubAPI->>Integration: Redirect with code
    Integration->>GitHubAPI: Exchange code for token
    GitHubAPI->>Integration: Return access token
    Integration->>GitHubAPI: Fetch authenticated user info
    GitHubAPI->>Integration: Return user details
    Integration->>Pipeline: Store github_authenticated_user
    end
    
    rect rgb(245, 230, 220)
    Note over User,Pipeline: Installation Validation
    User->>Integration: Complete installation
    Integration->>Pipeline: Retrieve installation_id & sender
    Integration->>Integration: Compare auth user vs sender
    alt User mismatch
        Integration->>User: Error: sender mismatch
    else User valid
        Integration->>Pipeline: Proceed to complete install
    end
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Key areas requiring attention:
    • OAuthLoginView.dispatch() method: Contains critical token exchange and user info retrieval logic that must verify request authenticity and handle edge cases correctly.
    • GitHubInstallation.dispatch() augmentation: New installation validation logic comparing authenticated user with sender metadata; requires verification that sender comparison is robust and correctly prevents cross-org installation hijacking.
    • Error handling pathway: Confirm error() and get_document_origin() implementations correctly handle customer domain configurations and produce appropriate HTTP responses.
    • Pipeline state mutations: Verify that installation_id and github_authenticated_user are persisted and retrieved consistently throughout the flow.
    • Test coverage for user mismatch scenario: Ensure the new validation is thoroughly exercised by the added test cases.

Poem

🐰 A new auth dance begins, so fine—
OAuth tokens and users align!
The sender must match, or we say "nay,"
No hijacking org installations today!
Git-magic flows through the pipeline true,

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is vague and does not convey the specific changes. While it mentions 'OAuth Security Enhancement,' the actual changes involve adding OAuth login flow steps, installation validation, error handling, and related infrastructure—none of which are clearly captured by the generic title. Consider a more specific title like 'Add OAuth login flow and installation validation to GitHub integration' that better reflects the actual changeset and helps reviewers understand the primary changes at a glance.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch oauth-state-secure

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
src/sentry/integrations/github/integration.py (1)

425-432: Overly broad exception handling masks potential errors.

Catching all exceptions and setting an empty payload hides important error details that could help diagnose OAuth failures. Consider logging the exception or being more specific about which exceptions to catch.

Apply this diff to improve error handling:

+        import logging
+        logger = logging.getLogger(__name__)
+
         try:
             body = safe_urlread(req).decode("utf-8")
             payload = dict(parse_qsl(body))
-        except Exception:
+        except Exception as e:
+            logger.exception("Failed to parse OAuth token response")
             payload = {}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccd14e4 and 843d7c7.

📒 Files selected for processing (3)
  • src/sentry/integrations/github/integration.py (7 hunks)
  • src/sentry/web/frontend/pipeline_advancer.py (1 hunks)
  • tests/sentry/integrations/github/test_integration.py (9 hunks)
🔇 Additional comments (13)
src/sentry/web/frontend/pipeline_advancer.py (1)

37-48: LGTM - Cleaner direct comparison.

The refactor from a constant list to a direct string comparison simplifies the code without changing behavior. This makes the GitHub-specific handling more explicit.

src/sentry/integrations/github/integration.py (9)

7-7: LGTM - Necessary OAuth imports.

The new imports support the OAuth login flow, token exchange, and user information retrieval.

Also applies to: 10-10, 18-19, 42-42


116-118: LGTM - Clear error messaging.

The error constant provides a user-friendly message for installation authenticity failures.


129-153: LGTM - Centralized error handling with customer domain support.

These helper functions provide consistent error rendering and properly handle customer domain URLs for postMessage security.


401-409: LGTM - Proper OAuth state generation for CSRF protection.

The state parameter is correctly generated from pipeline.signature and included in the authorization URL. This protects against CSRF attacks.


411-413: Excellent - State validation prevents CSRF attacks.

The state validation ensures that the OAuth callback matches the original authorization request, which is critical for preventing CSRF attacks in the OAuth flow.


343-344: LGTM - OAuth login correctly added to pipeline.

The OAuth login view is properly positioned before the installation view in the pipeline sequence.


448-454: LGTM - Flexible installation ID retrieval and persistence.

The code correctly handles installation_id from either the request or pipeline state, ensuring it's available for subsequent pipeline steps.


423-423: Verify timeout configuration for OAuth token exchange.

The token exchange HTTP request should have a timeout to prevent hanging if GitHub's OAuth endpoint is slow or unresponsive.


501-505: Excellent security enhancement - prevents installation hijacking.

This validation ensures that the user completing the OAuth flow is the same user who initiated the GitHub App installation. This effectively prevents a scenario where User A installs the GitHub App and User B attempts to complete the installation in a different Sentry organization.

However, verify that integration.metadata["sender"] is always present to prevent KeyError exceptions:

tests/sentry/integrations/github/test_integration.py (3)

116-123: LGTM - Comprehensive OAuth endpoint stubs.

The stubs correctly simulate GitHub's OAuth token endpoint (form-urlencoded response) and user info endpoint, enabling proper testing of the OAuth flow.


226-261: LGTM - Comprehensive OAuth flow test coverage.

The updated test properly validates the complete OAuth flow including:

  1. Initial redirect to GitHub OAuth authorization
  2. OAuth callback with code and state parameters
  3. Subsequent redirect to GitHub App installation

398-455: Excellent security test - validates user mismatch detection.

This test comprehensively validates the security enhancement by:

  1. Simulating a GitHub App installation with sender="attacker" via webhook
  2. Having a different user authenticate via OAuth (returns "octocat")
  3. Verifying that the mismatch is detected and the installation is blocked

This directly tests the critical security check that prevents installation hijacking across organizations.

Comment on lines +415 to +423
# similar to OAuth2CallbackView.get_token_params
data = {
"code": request.GET.get("code"),
"client_id": github_client_id,
"client_secret": github_client_secret,
}

# similar to OAuth2CallbackView.exchange_token
req = safe_urlopen(url=ghip.get_oauth_access_token_url(), data=data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing redirect_uri in token exchange violates OAuth 2.0 specification.

According to the OAuth 2.0 specification (RFC 6749 Section 4.1.3), if redirect_uri was included in the authorization request (line 404-408), it must also be included in the token exchange request for security verification. GitHub's OAuth implementation may be lenient, but this violates the spec and could cause issues.

Apply this diff to include the redirect_uri:

         # similar to OAuth2CallbackView.get_token_params
+        redirect_uri = absolute_uri(
+            reverse("sentry-extension-setup", kwargs={"provider_id": "github"})
+        )
         data = {
             "code": request.GET.get("code"),
             "client_id": github_client_id,
             "client_secret": github_client_secret,
+            "redirect_uri": redirect_uri,
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# similar to OAuth2CallbackView.get_token_params
data = {
"code": request.GET.get("code"),
"client_id": github_client_id,
"client_secret": github_client_secret,
}
# similar to OAuth2CallbackView.exchange_token
req = safe_urlopen(url=ghip.get_oauth_access_token_url(), data=data)
# similar to OAuth2CallbackView.get_token_params
redirect_uri = absolute_uri(
reverse("sentry-extension-setup", kwargs={"provider_id": "github"})
)
data = {
"code": request.GET.get("code"),
"client_id": github_client_id,
"client_secret": github_client_secret,
"redirect_uri": redirect_uri,
}
# similar to OAuth2CallbackView.exchange_token
req = safe_urlopen(url=ghip.get_oauth_access_token_url(), data=data)
🤖 Prompt for AI Agents
In src/sentry/integrations/github/integration.py around lines 415 to 423, the
token exchange data dict omits redirect_uri which was sent in the authorization
request (lines ~404-408); add the same redirect_uri value to the data payload
used in safe_urlopen so the token request includes "redirect_uri": redirect_uri
(i.e., reuse the exact redirect_uri variable/build from the earlier auth
request), ensuring the token exchange matches the original authorization request
per RFC 6749.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants