Add Bearer Authentication to the MCP server plugin#21622
Conversation
| @server_thread = framework.threads.spawn('MCPServer', false) do | ||
| @mcp_server.start(transport: :http, host: host, port: port) | ||
| print_status("Starting MCP server on HTTP transport...") | ||
| @mcp_server.start(transport: :http, host: host, port: port, auth_token: auth_token) | ||
| end | ||
|
|
||
| @started_at = Time.now | ||
| print_server_status(mcp_config) | ||
| print_status("MCP server listening on http://#{Rex::Socket.to_authority(mcp_config[:host], mcp_config[:port])}/") | ||
| if auth_token_generated | ||
| print_status("Authentication: Bearer token (auto-generated)") | ||
| print_status(" Configure your MCP client with: Authorization: Bearer #{auth_token}") | ||
| else | ||
| print_status("Authentication: #{auth_token ? 'enabled' : 'disabled'}") | ||
| end |
There was a problem hiding this comment.
While this code is duplicated, I took care so that what is printed is consistent between the plugin and the msfmcpd stand-alone utility, so it's not visually obvious that they're different.
|
Two things came up during testing:
I expected the second tab to fail, as the address is already in use. Otherwise, the bearer auth tests: Default auto-generated tokenCustom TokenStarting: Then: No auth tokenStarting: Then: |
|
I checked and both of these are pre-existing issues so I've opened dedicated tickets describing them in #21650 and #21649. I should note that I wasn't able to reproduce the exact issue you noted with the dual bind, but it was definitely not correct, just in the way I described in the issue, not how you described it. |
|
Unsure what about my environment is different, but I still didn't receive a Apart from that, re-testing after latest changes: Default tokenCustom tokenNo auth/token defined
Working as expected. CI also passed. Happy to land this once the final review from Christophe comes through 👍 |
Description
This adds bearer authentication to the MCP server started by the plugin, bringing it inline with the same behavior added in #21527. The value meanings are the same, by default a random token is generated, if the user explicitly sets a blank value, authentication is disabled otherwise the token is used as-is.
Related Issue:
Breaking Changes
None
Reviewer Notes
Verification Steps
Use the same steps from #21527
AuthToken=to the end, see that now authentication is disabledAuthToken=whateverto the end, see that now authentication is enabled, and the token is not echoed back to the user. They gave it to us, they know it, we don't need to be logging it anywhere.Test Evidence
Environment
AI Usage Disclosure
Did everything by hand.
Pre-Submission Checklist
documentation/modules(new modules only)lib/changes)Hardware and Complex Software Module Guidance
If your module targets specialized hardware (routers, IoT, PLCs, etc.) or complex software (licensed, multi-service, or multi-version), provide a pcap, screen recording, or video showing successful execution.
Email sanitized pcaps/recordings to msfdev@metasploit.com — remove real IPs, credentials, and hostnames before sending. If hardware/software is unavailable, explain in the PR description.
Responsiveness and PR Takeover Policy
We want every contribution to make it into the project. If approximately 2 weeks pass after a review request without a comment or code update from you, the team may take over the PR and complete the work on your behalf.
If this happens, you will remain credited as a co-author on the final commit — your contribution is always recognized.
This policy exists to keep the project moving forward. It is not a reflection on the quality of your work or your involvement. Life happens, and we would rather finish the work together than let a good contribution go stale.