Summary
The http transport currently authenticates callers with a single static shared secret (OCIS_MCP_HTTP_SECRET). Every request that presents the secret runs tools using the same oCIS credential configured at server startup (OCIS_MCP_APP_TOKEN_* / OCIS_MCP_OIDC_ACCESS_TOKEN). There is no way for individual callers to authenticate as themselves.
MCP has an official Authorization spec (OAuth 2.1 + PKCE) that HTTP-transport servers can implement so each connecting client authorizes their own access, instead of sharing one operator-provisioned credential.
Current behavior
OCIS_MCP_TRANSPORT=http starts an HTTP listener guarded by RequireBearer(cfg.HTTPSecret) (internal/middleware/http.go).
- The bearer secret only proves "this caller is allowed to talk to the server," it carries no per-user identity.
- All tool calls execute against the single oCIS identity loaded via
config.Load() at process start.
- This is documented as intentional in the README ("Prefer stdio... for single-client, local use"; HTTP mode is explicitly a shared-credential proxy).
Motivation
A centrally hosted ocis-mcp-server would let a team share one running instance instead of everyone running their own local process. That only works safely if each connecting user acts with their own oCIS identity and permissions, rather than everyone sharing one operator-provisioned credential behind a single bearer secret. Per-user OAuth is what makes a shared HTTP deployment viable for multiple distinct users.
Proposal
Implement the MCP transport as an OAuth 2.1 resource server that delegates authorization entirely to oCIS's existing OIDC provider. It should not issue or manage tokens itself:
- The HTTP transport advertises OAuth Protected Resource Metadata (RFC 9728) pointing at the oCIS OIDC issuer already configurable via
OCIS_MCP_OIDC_ISSUER.
- MCP clients perform the standard authorization-code + PKCE flow directly against oCIS's OIDC provider, the same one users already authenticate against for the web UI and desktop clients.
- The server validates the resulting access token on each request (audience, issuer, expiry) and calls the oCIS API with that caller's own token. No separate credential store, no server-side user database, no token minting logic to maintain.
- Per-user oCIS permissions apply automatically, since the token is the user's own oCIS session, just obtained through the MCP OAuth flow.
- The OAuth client is registered per deployment, using the admin-configured
OCIS_MCP_OIDC_CLIENT_ID (and OCIS_MCP_OIDC_CLIENT_SECRET if a confidential client is used), the same fields already present in internal/config/config.go but not yet wired into a flow.
This matches how the MCP Authorization spec (2025-06-18 revision) models the normal case: the MCP server acts as a resource server and delegates to an external authorization server, rather than being its own AS.
Documentation
This change needs README coverage for: registering an OAuth client against oCIS's IDP (or an external OIDC provider), the new required env vars, and how this mode differs from the existing OCIS_MCP_HTTP_SECRET shared-secret mode.
Open questions
- Confirm oCIS's OIDC provider issues audience-restricted tokens (or supports token exchange) so a token minted for the MCP server can't be replayed against unrelated oCIS APIs.
- Relationship to
OCIS_MCP_HTTP_SECRET: likely deprecated or removed once per-user OAuth exists, since it serves the same "gate the HTTP endpoint" purpose but coarser.
Summary
The
httptransport currently authenticates callers with a single static shared secret (OCIS_MCP_HTTP_SECRET). Every request that presents the secret runs tools using the same oCIS credential configured at server startup (OCIS_MCP_APP_TOKEN_*/OCIS_MCP_OIDC_ACCESS_TOKEN). There is no way for individual callers to authenticate as themselves.MCP has an official Authorization spec (OAuth 2.1 + PKCE) that HTTP-transport servers can implement so each connecting client authorizes their own access, instead of sharing one operator-provisioned credential.
Current behavior
OCIS_MCP_TRANSPORT=httpstarts an HTTP listener guarded byRequireBearer(cfg.HTTPSecret)(internal/middleware/http.go).config.Load()at process start.Motivation
A centrally hosted
ocis-mcp-serverwould let a team share one running instance instead of everyone running their own local process. That only works safely if each connecting user acts with their own oCIS identity and permissions, rather than everyone sharing one operator-provisioned credential behind a single bearer secret. Per-user OAuth is what makes a shared HTTP deployment viable for multiple distinct users.Proposal
Implement the MCP transport as an OAuth 2.1 resource server that delegates authorization entirely to oCIS's existing OIDC provider. It should not issue or manage tokens itself:
OCIS_MCP_OIDC_ISSUER.OCIS_MCP_OIDC_CLIENT_ID(andOCIS_MCP_OIDC_CLIENT_SECRETif a confidential client is used), the same fields already present ininternal/config/config.gobut not yet wired into a flow.This matches how the MCP Authorization spec (2025-06-18 revision) models the normal case: the MCP server acts as a resource server and delegates to an external authorization server, rather than being its own AS.
Documentation
This change needs README coverage for: registering an OAuth client against oCIS's IDP (or an external OIDC provider), the new required env vars, and how this mode differs from the existing
OCIS_MCP_HTTP_SECRETshared-secret mode.Open questions
OCIS_MCP_HTTP_SECRET: likely deprecated or removed once per-user OAuth exists, since it serves the same "gate the HTTP endpoint" purpose but coarser.