feat: Add session support for mcp#17
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds session-based authentication support to the public v1 API layer by updating the shared base API view classes to accept session cookies in addition to API keys.
Changes:
- Add
BaseSessionAuthenticationtoBaseAPIView.authentication_classes - Add
BaseSessionAuthenticationtoBaseViewSet.authentication_classes
| class BaseAPIView(TimezoneMixin, GenericAPIView, ReadReplicaControlMixin, BasePaginator): | ||
| authentication_classes = [APIKeyAuthentication] | ||
| authentication_classes = [BaseSessionAuthentication, APIKeyAuthentication] | ||
|
|
| class BaseViewSet(TimezoneMixin, ReadReplicaControlMixin, ModelViewSet, BasePaginator): | ||
| model = None | ||
|
|
||
| authentication_classes = [APIKeyAuthentication] | ||
| authentication_classes = [BaseSessionAuthentication, APIKeyAuthentication] | ||
| permission_classes = [ |
387df1d to
09c971f
Compare
hunzlahmalik
left a comment
There was a problem hiding this comment.
Review — session auth on the public REST API
This three-line change is the required keystone for the plane-mcp Cognito flow (Pressingly/plane-mcp-server#2 + Pressingly/foss-server-bundle#86): the MCP forwards a Cognito ID token → Traefik → oauth2-proxy sets X-Auth-Request-User → ProxyAuthMiddleware authenticates the Django user → DRF needs a session auth class to accept that user on /api/v1/. Without it, every REST tool 401s. Correct and necessary.
🟡 Medium
- Widens the CSRF-exempt cookie surface to the public REST API (inline).
BaseSessionAuthentication.enforce_csrfis a hard no-op, so/api/v1/now accepts any valid browser session cookie with CSRF disabled. The only barrier to cross-site write forgery is SameSite cookie policy + the mPass edge. This is consistent with the existing fork (plane/app/views/base.py,license/api,space/viewsalready use this class), so you're extending an established posture — but please (a) confirm the Plane session and_oauth2_proxycookies areSameSite=Lax/Strictin this deployment, and (b) add a one-line note to the PR body acknowledging the change.
🟢 Low
- Empty PR description. The template is still unfilled for a change touching the auth surface of every REST endpoint. Add what/why + the SameSite confirmation.
Ordering is right: [BaseSessionAuthentication, APIKeyAuthentication] tries session first and falls back to PAT, so existing API-key clients are unaffected.
|
|
||
| class BaseAPIView(TimezoneMixin, GenericAPIView, ReadReplicaControlMixin, BasePaginator): | ||
| authentication_classes = [APIKeyAuthentication] | ||
| authentication_classes = [BaseSessionAuthentication, APIKeyAuthentication] |
There was a problem hiding this comment.
🟡 Medium (security). This brings the public /api/v1/ REST surface under BaseSessionAuthentication, whose enforce_csrf is a no-op ("Disable csrf for the rest apis" in plane/authentication/session.py). So any valid browser session cookie can now drive state-changing REST calls with no CSRF check — mitigated only by SameSite cookie policy + the mPass ForwardAuth edge.
Required for the plane-mcp forwarded-ID-token → oauth2-proxy → session flow, and consistent with plane/app/views/base.py / license / space, which already use this class. Please confirm the session + _oauth2_proxy cookies are SameSite=Lax/Strict here, and acknowledge the widened surface in the PR description.
| model = None | ||
|
|
||
| authentication_classes = [APIKeyAuthentication] | ||
| authentication_classes = [BaseSessionAuthentication, APIKeyAuthentication] |
There was a problem hiding this comment.
🟡 Same CSRF-exempt note as BaseAPIView above — BaseViewSet now also accepts session cookies on /api/v1/ writes with CSRF disabled.
Review: CSRF Protection ConcernProblem
Any logged-in browser session can now be exploited by a cross-site attacker (POST/PUT/DELETE to any endpoint without a CSRF token). Why this PR is unnecessary for MCPThe MCP server (
Neither uses Django session cookies. CSRF is irrelevant for token-based auth because a cross-site attacker cannot inject custom headers. The MCP is a server-side process — it doesn't share a browser session. Root causeThe MCP's OAuth path sends Recommended fix: Add a
|
| PR #17 (session auth, no CSRF) | Bearer token auth | |
|---|---|---|
| CSRF protection | Disabled globally | Untouched |
| Attack surface | Every endpoint vulnerable to cross-site session riding | No change |
| Matches MCP's actual auth | No — MCP sends Bearer tokens, not cookies | Yes |
| Internal/external API boundary | Blurred — external API now accepts browser sessions | Clean separation |
Minimum viable alternative
If Bearer token validation is too much work right now, scope the CSRF exemption to only the endpoints the MCP actually calls (via @csrf_exempt on individual views or a dedicated URL prefix), rather than disabling it on every API base class.
Description
Type of Change
Screenshots and Media (if applicable)
Test Scenarios
References