-
Notifications
You must be signed in to change notification settings - Fork 0
Conversation
…#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: 
📝 WalkthroughWalkthroughThe changes introduce an OAuth login flow for GitHub integration with OAuthLoginView handling authentication, add centralized error rendering helpers, extend GitHubInstallation to manage state across pipeline steps, validate user-installation matching, and update get_pipeline_views to include the new OAuth view. Supporting tests validate the OAuth flow, edge cases, and repository retrieval functionality. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Sentry as Sentry<br/>(OAuthLoginView)
participant GitHub as GitHub<br/>(OAuth)
participant Sentry2 as Sentry<br/>(Installation Flow)
Client->>Sentry: Initiate GitHub setup
Sentry->>Sentry: Get client credentials<br/>from GitHubIdentityProvider
Sentry->>Client: Redirect to GitHub auth<br/>with state parameter
Client->>GitHub: User authorizes
GitHub->>Client: Redirect with code & state
Client->>Sentry: Access callback with code & state
Sentry->>GitHub: Exchange code for<br/>access token
GitHub->>Sentry: Return access token
Sentry->>GitHub: Fetch authenticated<br/>user login
GitHub->>Sentry: Return user login
Sentry->>Sentry: Store in pipeline state
Sentry->>Sentry2: Pass to installation flow
Sentry2->>Sentry2: Validate authenticated user<br/>matches installation sender
Sentry2->>Sentry2: Store installation_id<br/>in state
Sentry2->>Client: Complete setup flow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/sentry/integrations/github/integration.py`:
- Around line 493-505: The code currently assumes
integration.metadata["sender"]["login"] exists and will KeyError for legacy
integrations; update the check in the handler that compares
pipeline.fetch_state("github_authenticated_user") to
integration.metadata["sender"]["login"] to first safely access metadata (e.g.,
validate that integration.metadata is a dict and contains "sender" with a
"login" key or use nested .get calls) and if the sender/login is missing call
error(request, self.active_organization) so the code fails cleanly; reference
the Integration instance (variable integration), pipeline.fetch_state(...) and
the error(...) return path when implementing the guard.
| try: | ||
| integration = Integration.objects.get( | ||
| external_id=installation_id, status=ObjectStatus.ACTIVE | ||
| ) | ||
| except Integration.DoesNotExist: | ||
| return error(request, self.active_organization) | ||
|
|
||
| # Check that the authenticated GitHub user is the same as who installed the app. | ||
| if ( | ||
| pipeline.fetch_state("github_authenticated_user") | ||
| != integration.metadata["sender"]["login"] | ||
| ): | ||
| return error(request, self.active_organization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard missing sender metadata to avoid runtime KeyError.
If legacy integrations lack metadata["sender"], the direct lookup will raise and surface a 500 instead of a clean failure.
🛠️ Suggested fix
- # Check that the authenticated GitHub user is the same as who installed the app.
- if (
- pipeline.fetch_state("github_authenticated_user")
- != integration.metadata["sender"]["login"]
- ):
- return error(request, self.active_organization)
+ sender_login = (integration.metadata.get("sender") or {}).get("login")
+ if not sender_login:
+ return error(request, self.active_organization)
+
+ # Check that the authenticated GitHub user is the same as who installed the app.
+ if pipeline.fetch_state("github_authenticated_user") != sender_login:
+ return error(request, self.active_organization)📝 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.
| try: | |
| integration = Integration.objects.get( | |
| external_id=installation_id, status=ObjectStatus.ACTIVE | |
| ) | |
| except Integration.DoesNotExist: | |
| return error(request, self.active_organization) | |
| # Check that the authenticated GitHub user is the same as who installed the app. | |
| if ( | |
| pipeline.fetch_state("github_authenticated_user") | |
| != integration.metadata["sender"]["login"] | |
| ): | |
| return error(request, self.active_organization) | |
| try: | |
| integration = Integration.objects.get( | |
| external_id=installation_id, status=ObjectStatus.ACTIVE | |
| ) | |
| except Integration.DoesNotExist: | |
| return error(request, self.active_organization) | |
| sender_login = (integration.metadata.get("sender") or {}).get("login") | |
| if not sender_login: | |
| return error(request, self.active_organization) | |
| # Check that the authenticated GitHub user is the same as who installed the app. | |
| if pipeline.fetch_state("github_authenticated_user") != sender_login: | |
| return error(request, self.active_organization) |
🤖 Prompt for AI Agents
In `@src/sentry/integrations/github/integration.py` around lines 493 - 505, The
code currently assumes integration.metadata["sender"]["login"] exists and will
KeyError for legacy integrations; update the check in the handler that compares
pipeline.fetch_state("github_authenticated_user") to
integration.metadata["sender"]["login"] to first safely access metadata (e.g.,
validate that integration.metadata is a dict and contains "sender" with a
"login" key or use nested .get calls) and if the sender/login is missing call
error(request, self.active_organization) so the code fails cleanly; reference
the Integration instance (variable integration), pipeline.fetch_state(...) and
the error(...) return path when implementing the guard.
This pull request was automatically created by
@coderabbitai/e2e-reviewer.Batch created pull request.
Summary by CodeRabbit
New Features
Bug Fixes