fix(security): block bot user logins#9368
Conversation
Bot service accounts (User.is_bot=True, e.g. the WORKSPACE_SEED bot) are internal identities meant to act only through API tokens. Nothing stopped one from being driven through the interactive login flow if its email was known, letting a human assume a service identity. Reject bot accounts at the shared login chokepoint, Adapter.complete_login_or_signup(), right beside the existing deactivated-account check. This covers every interactive provider in one place: email/password, magic code, and all OAuth providers (Google, GitHub, GitLab, Gitea) across both the app and space surfaces. Bot API-token access is left untouched, since that is how bots are meant to operate. Also add a defense-in-depth is_bot guard to InstanceAdminSignInEndpoint, which mints its own admin session outside the chokepoint (a bot is never an InstanceAdmin today, so this is not currently reachable, but it closes the path regardless). Surface the rejection with a new dedicated error code BOT_USER_LOGIN_FORBIDDEN (5017), plumbed into the app and space frontend error helpers as well as the shared @plane/constants and @plane/utils packages (message map + banner-alert list) so any consumer of the shared auth-error handler renders it correctly. The admin path reuses the existing ADMIN_AUTHENTICATION_FAILED code so it discloses no bot-specific error. Add contract regression tests: a bot blocked via password and via magic code, a bot blocked at the admin sign-in endpoint, and a non-bot control that still logs in.
|
React Doctor found no issues. 🎉
|
|
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 (8)
📝 WalkthroughWalkthroughAdds backend enforcement to reject bot service accounts from interactive login/signup and admin sign-in flows via a new BOT_USER_LOGIN_FORBIDDEN (5017) error code, contract tests validating the rejection, and matching frontend error code definitions, banner messages, and handler registrations in web, space, and shared packages. ChangesBot login block
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant AuthAdapter
participant AdminSignInEndpoint
Client->>AuthAdapter: complete_login_or_signup()
AuthAdapter->>AuthAdapter: check user.is_bot
AuthAdapter-->>Client: raise AuthenticationException(BOT_USER_LOGIN_FORBIDDEN)
Client->>AdminSignInEndpoint: post() sign-in request
AdminSignInEndpoint->>AdminSignInEndpoint: check user.is_bot
AdminSignInEndpoint-->>Client: redirect with ADMIN_AUTHENTICATION_FAILED
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: Stream initialization permanently failed: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) 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.
Pull request overview
This PR prevents bot/service accounts (User.is_bot=True) from authenticating through any interactive login mechanism, ensuring bots can only act via API tokens while normal user authentication remains unchanged.
Changes:
- Added a backend guard in
Adapter.complete_login_or_signup()to reject bot users with the newBOT_USER_LOGIN_FORBIDDEN (5017)error. - Added a defense-in-depth guard in the instance-admin sign-in endpoint to reject bots using the existing
ADMIN_AUTHENTICATION_FAILEDresponse. - Introduced and propagated the new error code (
5017) through shared constants/utils and bothapps/webandapps/space, plus added contract regression tests.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/utils/src/auth.ts | Adds messaging + banner handling for BOT_USER_LOGIN_FORBIDDEN in the shared auth error handler. |
| packages/constants/src/auth/index.ts | Adds EAuthErrorCodes.BOT_USER_LOGIN_FORBIDDEN = "5017". |
| apps/web/helpers/authentication.helper.tsx | Adds the new error code + UI message mapping for the web app auth flow. |
| apps/space/helpers/authentication.helper.tsx | Adds the new error code + UI message mapping for the space app auth flow. |
| apps/api/plane/tests/contract/app/test_authentication.py | Adds contract regression tests ensuring bot users can’t sign in (password/magic) nor via admin sign-in. |
| apps/api/plane/license/api/views/admin.py | Blocks bot users from instance-admin sign-in (defense-in-depth). |
| apps/api/plane/authentication/adapter/error.py | Adds backend numeric error code mapping for BOT_USER_LOGIN_FORBIDDEN: 5017. |
| apps/api/plane/authentication/adapter/base.py | Adds the shared chokepoint guard rejecting user.is_bot during interactive login/signup completion. |
Description
Bot service accounts (
User.is_bot=True, e.g. theWORKSPACE_SEEDbot created inworkspace_seed_task.py) are internal identities that are meant to act only through API tokens. Nothing previously stopped one from being driven through the interactive login flow if its email was known — letting a human assume a service identity.This PR blocks bot users from every interactive login path while leaving their API-token access completely untouched (that is how bots are meant to operate).
appandspacesurfaces) funnels throughAdapter.complete_login_or_signup(). A singleis_botguard is added there, right beside the existing deactivated-account check. A brand-new signup can never be a bot (bots are provisioned internally), so guarding on an existinguserrecord is sufficient.InstanceAdminSignInEndpointmints its own admin session viauser_login()outside the chokepoint. A bot is never registered as anInstanceAdminand has a random autoset password, so this is not reachable today, but a guard is added there too to close the path. It reuses the existingADMIN_AUTHENTICATION_FAILEDcode so no bot-specific error is disclosed.BOT_USER_LOGIN_FORBIDDEN(5017) is added to the backendAUTHENTICATION_ERROR_CODESand plumbed end-to-end: theapps/webandapps/spaceauth helpers, plus the shared@plane/constants(EAuthErrorCodes) and@plane/utils(authErrorHandlermessage map + banner-alert list) packages, so any consumer of the shared handler renders it correctly rather than falling back to a generic error.Bot API-token authentication (
APIKeyAuthentication) does not pass throughcomplete_login_or_signup()and is verified unaffected.Type of Change
Screenshots and Media (if applicable)
Test Scenarios
Added contract regression tests in
apps/api/plane/tests/contract/app/test_authentication.py:BOT_USER_LOGIN_FORBIDDENand no session is created./api/instances/admins/sign-in/withADMIN_AUTHENTICATION_FAILEDand no session is created.is_bot).Manual scenarios to verify:
/api/v1 surface (e.g. list projects) — programmatic access must still work.References
is_botservice accounts; no linked issue.🤖 Generated with Claude Code
Summary by CodeRabbit