[WEB-7778] fix(security): reject unverified OAuth provider emails to prevent ATO (Cluster E)#9289
[WEB-7778] fix(security): reject unverified OAuth provider emails to prevent ATO (Cluster E)#9289mguptahub wants to merge 2 commits into
Conversation
…prevent ATO An attacker controlling a self-hosted OAuth provider (Gitea, GitLab) could assert any email address in the OAuth response and be matched to an existing Plane account, bypassing authentication entirely. - Add OAUTH_PROVIDER_UNVERIFIED_EMAIL (5124) error code - GitHub: require both primary=True AND verified=True on email (was primary-only) - Google: check verified_email=False field in userinfo response - GitLab: check confirmed_at is non-null before accepting email - Gitea __get_email: remove unverified fallbacks (primary-unverified, any-unverified) - Gitea set_user_data: remove fast-path using .email from user object (no verification flag); always go through __get_email() which enforces verified Fixes GHSA-7j95-vh8g-f365 (critical ATO). Note: GHSA-cv9p-325g-wmv5 and GHSA-hx79-5pj5-qh42 (avatar SSRF) were already fixed in PR #9163. Co-authored-by: Plane AI <noreply@plane.so>
|
Linked to Plane Work Item(s) This comment was auto-generated by Plane |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds ChangesOAuth Unverified Email Rejection
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/plane/authentication/provider/oauth/gitea.py`:
- Around line 158-161: The __get_email() method calls the Gitea API endpoint GET
/api/v1/user/emails, which requires the read:user OAuth2 scope. The current
scope configuration in the Gitea OAuth provider (around line 22) only includes
standard OIDC scopes (openid email profile) and lacks the necessary read:user
scope. Update the scope variable in the Gitea OAuth configuration to include the
read:user scope by adding it to the existing scope string, changing it from
"openid email profile" to "openid email profile read:user", so that the
__get_email() method can successfully authenticate and retrieve verified email
information.
In `@apps/api/plane/authentication/provider/oauth/google.py`:
- Around line 105-108: The email verification check in the Google OAuth provider
is using a default value of True when the verified_email field is missing, which
treats absent verification claims as trusted and violates the fail-closed
security principle. Change the default parameter in the
user_info_response.get("verified_email", True) call from True to False so that
when Google omits the verified_email field, the email is properly treated as
unverified and rejected, requiring explicit verification signals to proceed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c5a09811-d7c3-49cf-aae3-80f2fe6b0daf
📒 Files selected for processing (5)
apps/api/plane/authentication/adapter/error.pyapps/api/plane/authentication/provider/oauth/gitea.pyapps/api/plane/authentication/provider/oauth/github.pyapps/api/plane/authentication/provider/oauth/gitlab.pyapps/api/plane/authentication/provider/oauth/google.py
…ogle verified_email Gitea's /api/v1/user/emails endpoint requires the read:user granular scope — openid+email+profile alone is insufficient and __get_email() would return a 401/403. Add read:user to the scope string. Google: change default from True to fail-closed (is not True) so a userinfo response that omits verified_email is rejected rather than trusted. The service-account justification was incorrect — service accounts do not go through the interactive OAuth2 callback flow. Co-authored-by: Plane AI <noreply@plane.so>
Summary
OAuth providers can return unverified email addresses. The current code accepted them and used them to look up existing Plane accounts — allowing an attacker who controls a self-hosted Gitea or GitLab instance to assert any email and gain access to another user's account (account takeover).
GHSA-cv9p-325g-wmv5 and GHSA-hx79-5pj5-qh42 (avatar SSRF) were already fixed in PR #9163 and are not part of this change.
Changes
adapter/error.pyOAUTH_PROVIDER_UNVERIFIED_EMAILerror code (5124)provider/oauth/github.pyprimary=Trueandverified=True(wasprimary-only)provider/oauth/google.pyverified_emailfield in userinfo responseprovider/oauth/gitlab.pyconfirmed_atis non-null before accepting emailprovider/oauth/gitea.py__get_email(); remove fast path inset_user_data()that bypassed verificationAll providers now fail closed — if no verified email is available,
AuthenticationException(OAUTH_PROVIDER_UNVERIFIED_EMAIL)is raised and login is rejected.Test plan
OAUTH_PROVIDER_UNVERIFIED_EMAILverified_email: true— succeedsverified_email: false(mocked) — rejectedconfirmed_atset — succeedsconfirmed_at: null(mocked self-hosted) — rejectedOAUTH_PROVIDER_UNVERIFIED_EMAILFixes GHSA-7j95-vh8g-f365 (critical).
Co-authored-by: Plane AI noreply@plane.so
Summary by CodeRabbit