Skip to content

Conversation

@garland3
Copy link
Collaborator

Summary

Enables MCP servers to authenticate using capability tokens when accessing files through /api/files/download/ endpoints.

Previously, the middleware only checked for X-User-Email headers, causing MCP server requests to fail in production nginx environments. The middleware would redirect unauthenticated requests to /auth, which doesn't exist, resulting in 404 errors.

Changes

  • Check capability tokens in query parameters before validating X-User-Email headers
  • Return HTTP 401 for unauthenticated API endpoint requests instead of redirecting
  • Maintain backward compatibility with browser-based authentication via headers

Technical Details

Authentication Flow (Before):

MCP server → GET /api/files/download/...?token=xyz
  → Middleware checks X-User-Email header (missing)
  → Redirects to /auth (302)
  → 404 Not Found

Authentication Flow (After):

MCP server → GET /api/files/download/...?token=xyz
  → Middleware validates capability token
  → Extracts user email from token claims
  → Request proceeds to route handler

Testing

  • ✅ All 56 backend tests pass (including existing capability token tests)
  • ✅ All 21 frontend tests pass
  • ✅ All 3 e2e tests pass
  • ✅ Backward compatible with browser-based authentication
  • ✅ Maintains existing middleware auth behavior for non-API endpoints

Impact

Fixes the issue where MCP servers (like pdfbasic) fail to download files in production K8s environments with nginx authentication proxies. Browser-based requests continue to work as before.

🤖 Generated with Claude Code

Enables MCP servers to authenticate using capability tokens when accessing
files through /api/files/download/ endpoints. Previously, the middleware
only checked for X-User-Email headers, causing MCP server requests to fail
in production nginx environments.

Changes:
- Check capability tokens before X-User-Email header validation
- Return 401 for unauthenticated API requests instead of redirecting
- Maintain backward compatibility with browser-based authentication

Fixes authentication flow for MCP servers making programmatic file requests
with tokenized URLs while preserving existing browser-based auth behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@garland3 garland3 requested a review from Copilot October 31, 2025 23:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR enhances the authentication middleware to support capability tokens for file downloads, enabling MCP servers and headless tools to access files without session cookies. It also improves the authentication failure handling by distinguishing between API endpoints (return 401) and browser endpoints (redirect to /auth).

Key changes:

  • Added capability token authentication for file download URLs
  • Distinguished API endpoint authentication failures (401) from browser endpoint failures (redirect)
  • Added HTTPException import for proper error handling

Comment on lines 38 to 45
# Valid capability token - extract user from token and allow request
user_email = claims.get('u')
if user_email:
logger.info(f"Authenticated via capability token for user: {user_email}")
request.state.user_email = user_email
return await call_next(request)
else:
logger.warning("Valid token but missing user email claim")
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

Missing validation that the file_key in the token matches the requested file. The token contains both user ('u') and file key ('k') claims, but only the user is being verified here. An attacker could reuse a valid token for one file to access a different file. Add validation: file_key = request.url.path.split('/api/files/download/')[1].split('?')[0] and if claims.get('k') != file_key: logger.warning('Token file key mismatch'); continue to standard auth.

Suggested change
# Valid capability token - extract user from token and allow request
user_email = claims.get('u')
if user_email:
logger.info(f"Authenticated via capability token for user: {user_email}")
request.state.user_email = user_email
return await call_next(request)
else:
logger.warning("Valid token but missing user email claim")
# Validate that the file key in the token matches the requested file
file_key = request.url.path.split('/api/files/download/')[1].split('?')[0]
if claims.get('k') != file_key:
logger.warning("Token file key mismatch")
else:
# Valid capability token - extract user from token and allow request
user_email = claims.get('u')
if user_email:
logger.info(f"Authenticated via capability token for user: {user_email}")
request.state.user_email = user_email
return await call_next(request)
else:
logger.warning("Valid token but missing user email claim")

Copilot uses AI. Check for mistakes.
Clarifies why the middleware only validates capability token authenticity
but not file key matching. The route handler validates file key authorization,
maintaining proper separation of concerns between authentication (middleware)
and authorization (route handlers).

Addresses review feedback from Copilot AI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@garland3 garland3 merged commit 0473639 into main Oct 31, 2025
5 checks passed
@garland3 garland3 deleted the fix-capability-token-auth branch October 31, 2025 23:18
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