Skip to content

Add service account authentication for external MCP servers - #937

Open
nickmisasi wants to merge 17 commits into
masterfrom
cursor/mcp-service-account-auth-a475
Open

Add service account authentication for external MCP servers#937
nickmisasi wants to merge 17 commits into
masterfrom
cursor/mcp-service-account-auth-a475

Conversation

@nickmisasi

@nickmisasi nickmisasi commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds service account authentication for MCP servers, so automation-oriented agents can call external MCP servers (e.g. Jira) with admin-provisioned shared credentials instead of per-user OAuth.

What's new

  • Each remote MCP server config gains a Service Account Authentication headers section in the System Console (mirrors the existing Headers editor; blank rows and names that collide after trimming are filtered fail-closed via EffectiveServiceAccountHeaders()).
  • Agents gain a single all-or-nothing "Use service accounts for authentication" setting on the agent modal's MCPs tab, with a warning banner explaining the shared-access implications. Only system administrators can enable it (server-enforced; anyone who can manage the agent can turn it off).
  • When enabled (and remote MCP is licensed): external MCP servers authenticate with the configured SA headers (servers without SA headers are excluded fail-closed from the agent's catalog — no fallback to user OAuth); embedded/plugin MCP tools run as the agent's bot account (channel membership is the internal access boundary); all per-user OAuth machinery (connect prompts, mcp_oauth_needed state, per-user server preferences, RHS Tools popover) is bypassed/hidden. /ai_bots reports the effective mode, so the UI degrades to per-user UX on unlicensed servers.
  • The contract applies in every context — DMs, channel mentions, and the AI bridge. Interactive approval is unchanged: the human initiator approves, the service account executes.
  • Attribution: token usage logs gain acting_user_id and tool_auth_mode.
  • Bridge: SA-flagged agents use the SA catalog regardless of the caller-supplied user_id (which still drives usage ACLs and attribution); discovery uses the same catalog as execution.
  • Unlicensed servers: SA mode is fully off — the agent authenticates per-user like any other agent.
  • Non-SA agents are unchanged everywhere, including bridge user_id semantics.
  • Docs updated: docs/features/multiplayer_tool_calling.md (the "service accounts are not a concept" contract is replaced with per-user vs service-account modes), docs/admin_guide.md, public/bridgeclient/README.md, user guide, managing-agents.

Implementation notes

  • SA connections pool per (bot user ID, service-account mode) in mcp.ClientManager, with an isolated shared tools cache namespace (sa:<server>); X-Mattermost-UserID is the agent bot's ID. The "SA never surfaces OAuth prompts" invariant is enforced structurally in Client.oauthNeededError.
  • Catalog selection lives at the single funnel (llmcontext.Builder.getToolsStoreForUser) via UsesServiceAccountCatalog, so conversations, the bridge, discovery, and channel analysis all inherit it with no per-site branching.
  • The agent editor skips orphaned-tool reconciliation for SA agents (the editing user's per-user catalog is not authoritative for an SA agent's grants).
  • New migration 000010 adds UseServiceAccountAuth to Agents_UserAgents.
  • Bridge tool_hooks are deliberately not accounted for in SA mode (their only consumer, the Channel Automation plugin, is being retired).

Security review follow-up (2026-08-06)

Addresses two findings from @edgarbellot's review:

  • Post-enable privilege ratchet closed. Previously only the false→true transition of useServiceAccountAuth required system admin, so after an admin enabled it, any agent manager could widen the agent (user access, tool auto-grant, service, instructions) under the admin-granted credentials. Now any save whose resulting state keeps the flag on requires system admin. Non-admin managers keep full read access to the config and retain two escape hatches: turning the setting off (optionally editing anything else in that same request, since the result is a normal agent) or deleting the agent. Re-enabling requires admin again. The webapp shows non-admin managers a read-only editor (banner + disabled Save) while the flag stays on, with the MCPs tab kept reachable as the off-switch even when tools are disabled.
  • Credential leak on cross-origin redirects fixed. The MCP HTTP transports re-added credentials on every request, defeating Go's cross-host Authorization stripping when a server issued a redirect. The fix covers all three credential types (SA headers, admin-configured headers, per-user OAuth tokens) with two layers: a CheckRedirect policy that rejects redirects leaving the configured server origin, and origin-gated injection inside headerTransport and authenticationTransport (fail-closed on unparsable base URLs; in-process plugin transports are unaffected). OAuth discovery/token flows to a different authorization-server origin are untouched since they don't route through these transports.
  • The third finding (embedded tools executing as the agent's bot account rather than intersecting with the requesting user's permissions) is the documented design of SA mode — the bot's channel membership is the internal access boundary — and is tracked as accepted risk outside this PR.

Demo — a non-admin agent maintainer gets a read-only editor for an SA agent, with turning the setting off as the only way to re-enable saving:

non_admin_maintainer_read_only_sa_agent_editor.mp4

QA (manual, local server + real Anthropic LLM + PAT-gated external MCP server)

  • Adding SA headers in the console, creating an SA agent (warning banner), and saving all persist correctly. (Screenshots console_mcp_server_service_account_section.webp and agent_mcps_tab_sa_checkbox_warning.webp in the run's artifact history.)
  • End-to-end proof: chatting with the SA agent and calling the external server's get_me tool returns the service account's username (not the signed-in user), after human approval, with no OAuth prompt; the RHS Tools popover is hidden. (sa_agent_tool_call_runs_as_service_account.mp4 in the run's artifact history.)
  • Regression: a normal agent keeps the per-user Tools popover with a Connect button for the same server. (normal_agent_keeps_per_user_tools_ux.mp4 in the run's artifact history.)
  • Follow-up fixes verified with go test ./api/... ./mcp/..., webapp type-check + jest, eslint, i18n drift check, and the recorded manual demo of the read-only maintainer editor.

Release Note

Added service account authentication for external MCP servers. MCP server configurations accept a new Service Account Authentication headers section, and agents gained a "Use service accounts for authentication" setting that only system administrators can enable: when enabled, external MCP tool calls authenticate with the admin-configured service account headers (servers without them are excluded fail-closed) and embedded Mattermost tools run as the agent's bot account. While the setting is enabled, saving any change to the agent requires a system administrator; anyone who can manage the agent can still view it, turn the setting off, or delete it. MCP connections now refuse HTTP redirects to a different origin and never send credentials cross-origin. Added the UseServiceAccountAuth column to the Agents_UserAgents table. Token usage logs gained acting_user_id and tool_auth_mode fields.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Added an agent-level “use service accounts for authentication” setting, persisted end-to-end and surfaced in the UI.
    • External MCP access now uses admin-configured service-account headers and fails closed for servers without valid headers.
    • Service-account agents hide the Tools UI and select the service-account tool catalog for execution.
  • Bug Fixes
    • Enforced admin-only enablement for turning this setting on.
  • Documentation
    • Updated admin/user and bridge/multiplayer calling guidance for service-account credential boundaries and fail-closed behaviour.
  • Tests
    • Expanded coverage for persistence, catalogue selection/execution, and token-attribution by auth mode.

cursoragent and others added 6 commits July 28, 2026 20:31
… flag

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
…context

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

🤖 LLM Evaluation Results

OpenAI

⚠️ Overall: 22/28 tests passed (78.6%)

Provider Total Passed Failed Pass Rate
⚠️ OPENAI 28 22 6 78.6%

❌ Failed Evaluations

Show 6 failures

OPENAI

1. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: is a list of bugs
  • Reason: The output does not provide an actual list of bugs; it states inability to access the bug tracker and only supplies an empty table template without any bug entries.

2. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: includes a description of each bug
  • Reason: The output does not include any actual bug descriptions; it states the assistant cannot access the bug tracker and provides a blank template/table. Since no specific bugs are listed or described, it fails the rubric requirement to include a description of each bug.

3. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes each bug to a user
  • Reason: The output provides a template with a 'Reported by' column but does not actually attribute any bugs to any specific user, nor does it list any bugs at all.

4. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes the bug about trying to save without a color and the save button not doing anything to @maria.nunez
  • Reason: The output does not mention the specific bug about trying to save without a color or the save button doing nothing, and it does not attribute any bug to @maria.nunez.

5. TestConversationMentionHandling/[openai]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: the bug about the end user being able to change channel banner is attributed to @maria.nunez
  • Reason: The output does not mention any specific bug about an end user being able to change the channel banner, and it does not attribute such a bug to @maria.nunez (or anyone). It only asks the user to paste bug reports.

6. TestDirectMessageConversations/[openai]_bot_dm_tool_introspection

  • Score: 0.50
  • Rubric: mentions Github and refers to the documentation
  • Reason: The output refers to documentation (Mattermost docs link) but does not mention GitHub anywhere, so it fails the rubric requirement to both mention Github and refer to the documentation.

Anthropic

⚠️ Overall: 21/28 tests passed (75.0%)

Provider Total Passed Failed Pass Rate
⚠️ ANTHROPIC 28 21 7 75.0%

❌ Failed Evaluations

Show 7 failures

ANTHROPIC

1. TestReactEval/[anthropic]_react_cat_message

  • Score: 0.00
  • Rubric: The word/emoji is a cat emoji or a heart/love emoji
  • Reason: The output is the text string "heart_eyes_cat", not an actual cat emoji (e.g., 😺/🐱) or a heart/love emoji (e.g., ❤️/😍).

2. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: is a list of bugs
  • Reason: The output states it cannot access messages or bug tracking tools and suggests how to compile a list, but it does not actually provide a list of bugs.

3. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: includes a description of each bug
  • Reason: The output states it cannot access messages or bug tracking tools and suggests ways to compile a list, but it does not actually describe any bugs (no individual bug descriptions are provided).

4. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes each bug to a user
  • Reason: The output does not list any bugs, nor does it attribute bugs to specific users. It only states lack of access and suggests how to compile a list.

5. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: attributes the bug about trying to save without a color and the save button not doing anything to @maria.nunez
  • Reason: The output does not mention the specific bug (saving without a color causing the save button to do nothing) nor does it attribute it to @maria.nunez. It only states lack of access and suggests ways to compile bug reports.

6. TestConversationMentionHandling/[anthropic]_conversation_from_attribution_long_thread.json

  • Score: 0.00
  • Rubric: the bug about the end user being able to change channel banner is attributed to @maria.nunez
  • Reason: The output only states it cannot access messages and suggests ways to compile bugs; it does not mention the specific bug about changing the channel banner or attribute it to @maria.nunez.

7. TestDirectMessageConversations/[anthropic]_bot_dm_tool_introspection

  • Score: 0.00
  • Rubric: mentions Github and refers to the documentation
  • Reason: The output refers to the documentation (docs.mattermost.com) but does not mention GitHub. Since the rubric requires both mentioning GitHub and referring to the documentation, it fails.

This comment was automatically generated by the eval CI pipeline.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Agents now support opt-in service-account authentication across persistence, MCP execution, bridge and conversation flows, administration, UI configuration, and token attribution. External MCP servers without valid service-account headers are excluded, while agent identity is preserved for execution and logging.

Changes

Service-account authentication

Layer / File(s) Summary
Agent contracts and storage
api/..., store/..., llm/configuration.go, config/mcp_config.go
Agent requests, responses, database records, bot configuration, and MCP server configuration carry service-account settings and headers, with admin-only enablement checks.
MCP client execution
mcp/...
Service-account clients use static headers, separate client pools and cache namespaces, fail-closed server selection, and no OAuth-needed classification.
Catalog, bridge, and conversation flows
llmcontext/..., api/api_llm_bridge.go, conversations/...
Tool catalogs use the agent bot identity when service-account mode is licensed and enabled; bridge and conversation flows use the service-account catalog.
Web application controls
webapp/src/components/agents/..., webapp/src/components/system_console/..., webapp/src/components/rhs/...
Agent configuration exposes the service-account toggle, system administration supports service-account headers, and the per-user Tools popover is hidden for service-account agents.
Attribution and documentation
llm/token_tracking.go, docs/..., public/bridgeclient/README.md
Token logs include acting identity and tool authentication mode, and documentation covers credential, approval, fail-closed, and bridge API behaviour.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: Setup Cloud Test Server

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding service account authentication for external MCP servers.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/mcp-service-account-auth-a475

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
webapp/src/components/system_console/mcp_servers.tsx (1)

126-130: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Service-account credentials render in cleartext.

The section help text advertises this field for a personal access token, yet the value input is plain text while the adjacent OAuth Client Secret (line 359) uses type='password'. Consider threading a maskValues prop through HeaderMapEditor and enabling it for the service-account map so the credential isn't shoulder-surfable in the System Console.

🔒 Sketch
 const HeaderMapEditor = ({
     headers,
     onChange,
+    maskValues = false,
 }: {
     headers: {[key: string]: string};
     onChange: (headers: {[key: string]: string}) => void;
+    maskValues?: boolean;
 }) => {
                         <HeaderInput
+                            type={maskValues ? 'password' : 'text'}
                             placeholder={intl.formatMessage({defaultMessage: 'Value'})}
                 <HeaderMapEditor
                     headers={config.serviceAccountHeaders}
+                    maskValues={true}
                     onChange={(serviceAccountHeaders) => onChange(serverIndex, {...config, serviceAccountHeaders})}
                 />

Note this would also require updating the positional Value placeholder lookups in webapp/src/components/system_console/mcp_servers.test.tsx (lines 142-143) if you switch input types.

Also applies to: 303-313

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/system_console/mcp_servers.tsx` around lines 126 - 130,
Update HeaderMapEditor and its service-account header map usage to accept and
apply a maskValues prop, rendering credential value inputs with password masking
when enabled while preserving plain-text behavior elsewhere. Enable masking for
the service-account credentials, and update the affected mcp_servers tests to
locate the Value inputs without relying on their changed input type or position.
webapp/src/components/agents/tabs/mcps_tab.tsx (1)

149-158: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

aria-checked is ignored on an implicit role='button'.

Assistive tech drops aria-checked unless the element exposes a checkable role, so the on/off state isn't announced. The aria-label still flips between "Enable"/"Disable", so the control remains usable — hence optional — but role='switch' makes the state programmatically correct.

♻️ Proposed tweak
                 <ServerToggle
                     type='button'
+                    role='switch'
                     aria-label={serverToggleLabel}
                     aria-checked={allOn}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/tabs/mcps_tab.tsx` around lines 149 - 158,
Update the ServerToggle element to expose a checkable switch role so its
existing aria-checked={allOn} state is announced by assistive technology.
Preserve the current label, toggle handler, disabled behavior, and visual state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/mcp_config.go`:
- Around line 122-148: Update EffectiveServiceAccountHeaders to trim each header
name and value before storing them, while continuing to skip entries whose
trimmed name or value is empty. Use the trimmed strings as the map key and value
so HasServiceAccountAuth only exposes valid credentials, and add coverage in the
existing table-driven tests for padded headers.

In `@mcp/client_manager.go`:
- Around line 105-115: Update ClientManager.closeIdleClients to delete the
matching m.activity entry whenever an idle client is removed from m.clients,
using the same key and preserving the existing timeout and client-closing
behavior.

In `@store/migrations/reviews/000010_user_agent_service_account_auth.md`:
- Around line 33-41: Update the “Table Locks & Impact” and “Zero Downtime”
sections to state that the metadata-only ADD COLUMN still acquires ACCESS
EXCLUSIVE and may wait for active transactions or conflicting locks. Remove
“returns instantly” and unqualified “Negligible” claims, and describe the impact
as dependent on lock-wait duration.

In `@webapp/src/components/system_console/mcp_servers.tsx`:
- Around line 101-108: Update updateHeader so renaming a key rebuilds the
headers map in its existing Object.entries order, replacing oldKey with newKey
at the original position rather than deleting and appending it. Preserve the
current value update and onChange behavior, including the unchanged-key path, so
rows rendered by index remain stable.

---

Nitpick comments:
In `@webapp/src/components/agents/tabs/mcps_tab.tsx`:
- Around line 149-158: Update the ServerToggle element to expose a checkable
switch role so its existing aria-checked={allOn} state is announced by assistive
technology. Preserve the current label, toggle handler, disabled behavior, and
visual state.

In `@webapp/src/components/system_console/mcp_servers.tsx`:
- Around line 126-130: Update HeaderMapEditor and its service-account header map
usage to accept and apply a maskValues prop, rendering credential value inputs
with password masking when enabled while preserving plain-text behavior
elsewhere. Enable masking for the service-account credentials, and update the
affected mcp_servers tests to locate the Value inputs without relying on their
changed input type or position.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 7c279556-1ca7-4c98-9fd5-3d2103f02588

📥 Commits

Reviewing files that changed from the base of the PR and between 48a6b5e and b5f5de2.

📒 Files selected for processing (58)
  • api/api.go
  • api/api_agents.go
  • api/api_agents_test.go
  • api/api_channel_analysis_test.go
  • api/api_config_test.go
  • api/api_llm_bridge.go
  • api/api_llm_bridge_service_account_test.go
  • api/api_llm_bridge_test.go
  • api/api_no_tools_test.go
  • api/api_test.go
  • bots/bot.go
  • config/mcp_config.go
  • config/mcp_config_test.go
  • conversations/bot_channel_tool_filter_test.go
  • conversations/conversations_test.go
  • conversations/dm_conversation_test.go
  • conversations/handle_messages.go
  • conversations/service_account_test.go
  • conversations/single_build_test.go
  • docs/admin_guide.md
  • docs/features/managing_agents.md
  • docs/features/multiplayer_tool_calling.md
  • docs/user_guide.md
  • e2e/helpers/agent-api.ts
  • llm/configuration.go
  • llm/configuration_test.go
  • llm/context.go
  • llm/token_tracking.go
  • llm/token_tracking_test.go
  • llm/token_usage_fields.go
  • llmcontext/llm_context.go
  • llmcontext/llm_context_license_test.go
  • llmcontext/llm_context_test.go
  • mcp/client.go
  • mcp/client_embedded_oauth_test.go
  • mcp/client_manager.go
  • mcp/client_manager_test.go
  • mcp/client_test.go
  • mcp/service_account_test.go
  • mcp/user_clients.go
  • public/bridgeclient/README.md
  • store/agents.go
  • store/agents_test.go
  • store/migrations/000010_user_agent_service_account_auth.down.sql
  • store/migrations/000010_user_agent_service_account_auth.up.sql
  • store/migrations/reviews/000010_user_agent_service_account_auth.md
  • store/store_test.go
  • webapp/src/bots.tsx
  • webapp/src/components/agents/agent_config_view.test.tsx
  • webapp/src/components/agents/agent_config_view.tsx
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.tsx
  • webapp/src/components/rhs/rhs_header.test.tsx
  • webapp/src/components/rhs/rhs_header.tsx
  • webapp/src/components/system_console/mcp_servers.test.tsx
  • webapp/src/components/system_console/mcp_servers.tsx
  • webapp/src/i18n/en.json
  • webapp/src/types/agents.ts

Comment thread config/mcp_config.go
Comment thread mcp/client_manager.go Outdated
Comment thread store/migrations/reviews/000010_user_agent_service_account_auth.md Outdated
Comment thread webapp/src/components/system_console/mcp_servers.tsx
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
llmcontext/llm_context.go (1)

272-292: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Record service-account mode outside the optional MCP-provider branch.

mcpToolProvider is nullable, but c.ToolAuthMode is set only when that provider exists. A licensed service-account agent without an MCP provider therefore reaches token tracking with the default user mode, causing acting_user_id to be attributed to the requesting user instead of the bot account. Set c.ToolAuthMode immediately after useServiceAccount is computed, before the provider check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@llmcontext/llm_context.go` around lines 272 - 292, Set c.ToolAuthMode
immediately after useServiceAccount is computed, before any nullable
mcpToolProvider check, assigning service-account mode when enabled and
preserving user mode otherwise. Remove the later service-account assignment
inside the provider branch while keeping the existing tool-fetch behavior
unchanged.
api/api_llm_bridge.go (1)

318-323: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the new bridge error through i18n.

Line 321 adds a client-visible error as a hardcoded English string. Route it through the repository’s i18n extraction or established API error-key mechanism.

As per coding guidelines: “All new user-facing strings must go through i18n extraction instead of being hardcoded.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/api_llm_bridge.go` around lines 318 - 323, Update the new error returned
by the tool-hook validation in the request bridge to use the repository’s
established i18n extraction or API error-key mechanism instead of a hardcoded
English message. Preserve the existing BadRequest status and validation behavior
in the service-account catalog check.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@api/api_llm_bridge.go`:
- Around line 318-323: Update the new error returned by the tool-hook validation
in the request bridge to use the repository’s established i18n extraction or API
error-key mechanism instead of a hardcoded English message. Preserve the
existing BadRequest status and validation behavior in the service-account
catalog check.

In `@llmcontext/llm_context.go`:
- Around line 272-292: Set c.ToolAuthMode immediately after useServiceAccount is
computed, before any nullable mcpToolProvider check, assigning service-account
mode when enabled and preserving user mode otherwise. Remove the later
service-account assignment inside the provider branch while keeping the existing
tool-fetch behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 1a2cf38f-75d0-4f24-b82b-b890dac696f9

📥 Commits

Reviewing files that changed from the base of the PR and between b5f5de2 and 97fecc2.

📒 Files selected for processing (34)
  • api/api_llm_bridge.go
  • api/api_llm_bridge_service_account_test.go
  • api/api_llm_bridge_test.go
  • api/api_test.go
  • bots/bot.go
  • config/mcp_config.go
  • config/mcp_config_test.go
  • conversations/bot_channel_tool_filter_test.go
  • conversations/handle_messages.go
  • conversations/service_account_test.go
  • conversations/single_build_test.go
  • llm/configuration.go
  • llm/configuration_test.go
  • llm/context.go
  • llm/token_tracking.go
  • llm/token_tracking_test.go
  • llm/token_usage_fields.go
  • llmcontext/llm_context.go
  • llmcontext/llm_context_license_test.go
  • llmcontext/llm_context_test.go
  • mcp/client.go
  • mcp/client_manager.go
  • mcp/client_test.go
  • mcp/service_account_test.go
  • mcp/user_clients.go
  • webapp/src/bots.tsx
  • webapp/src/components/agents/agent_config_view.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.tsx
  • webapp/src/components/rhs/rhs_header.test.tsx
  • webapp/src/components/rhs/rhs_header.tsx
  • webapp/src/components/system_console/mcp_servers.test.tsx
  • webapp/src/components/system_console/mcp_servers.tsx
  • webapp/src/types/agents.ts
💤 Files with no reviewable changes (3)
  • conversations/bot_channel_tool_filter_test.go
  • conversations/single_build_test.go
  • llm/token_tracking_test.go
🚧 Files skipped from review as they are similar to previous changes (27)
  • llm/context.go
  • webapp/src/components/rhs/rhs_header.tsx
  • llm/configuration.go
  • webapp/src/bots.tsx
  • webapp/src/types/agents.ts
  • conversations/handle_messages.go
  • bots/bot.go
  • webapp/src/components/agents/agent_config_view.test.tsx
  • webapp/src/components/rhs/rhs_header.test.tsx
  • mcp/client_test.go
  • webapp/src/components/system_console/mcp_servers.test.tsx
  • llm/token_usage_fields.go
  • conversations/service_account_test.go
  • mcp/user_clients.go
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • config/mcp_config_test.go
  • llmcontext/llm_context_license_test.go
  • config/mcp_config.go
  • webapp/src/components/agents/tabs/mcps_tab.tsx
  • api/api_test.go
  • api/api_llm_bridge_service_account_test.go
  • webapp/src/components/system_console/mcp_servers.tsx
  • llmcontext/llm_context_test.go
  • api/api_llm_bridge_test.go
  • mcp/client.go
  • mcp/client_manager.go
  • mcp/service_account_test.go

@mm-cloud-bot

Copy link
Copy Markdown

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to install plugin via mmctl: failed with status code 500

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit 97fecc2881155d241b3f6dbff7be94abdec14025

Plugin Version Artifact
agents 97fecc2 Download

@mm-cloud-bot

Copy link
Copy Markdown

Test server destroyed

cursoragent and others added 2 commits July 29, 2026 01:22
…account-auth-a475

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
… tests

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
webapp/src/components/agents/tabs/mcps_tab.tsx (1)

313-375: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Bypass per-user OAuth controls in service-account mode.

When useServiceAccountAuth is enabled, this still renders Connect, connection badges, and instructions telling a user to authenticate. Hide these controls and render the service-account catalogue/state instead; otherwise the UI contradicts the selected authentication mode.

Based on PR objectives: per-user OAuth flows and related UI are bypassed.

Also applies to: 394-402

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/components/agents/tabs/mcps_tab.tsx` around lines 313 - 375,
Update the MCP server rendering around ServerHeaderButton and ConnectButton to
branch on useServiceAccountAuth: when service-account authentication is enabled,
suppress the per-user Connect control, authentication/connection badges, and
related user-authentication instructions, and render the existing
service-account catalogue/state instead. Preserve the current OAuth UI behavior
when useServiceAccountAuth is disabled, including the applicable tools metadata
and server expansion controls.
🧹 Nitpick comments (1)
llm/configuration_test.go (1)

668-672: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add read-side and true-value coverage for the new flag.

This assertion covers only marshalling false. Add table-driven unmarshal/round-trip cases for both true and false; otherwise a regression while loading persisted or API configuration could silently disable service-account authentication while this test still passes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@llm/configuration_test.go` around lines 668 - 672, Extend the BotConfig
coverage in the existing configuration tests with table-driven cases that
unmarshal and round-trip both true and false values for UseServiceAccountAuth,
verifying the loaded value is preserved and serialized correctly. Retain the
current false-field presence assertions and use the existing
MCPDynamicToolLoading/UseServiceAccountAuth symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/features/multiplayer_tool_calling.md`:
- Around line 81-82: Update the approval rationale in the multiplayer
tool-calling documentation to qualify that tools run with the initiator’s
credentials only for per-user authentication. Keep the existing initiator-only
approval behavior and service-account explanation unchanged.

---

Outside diff comments:
In `@webapp/src/components/agents/tabs/mcps_tab.tsx`:
- Around line 313-375: Update the MCP server rendering around ServerHeaderButton
and ConnectButton to branch on useServiceAccountAuth: when service-account
authentication is enabled, suppress the per-user Connect control,
authentication/connection badges, and related user-authentication instructions,
and render the existing service-account catalogue/state instead. Preserve the
current OAuth UI behavior when useServiceAccountAuth is disabled, including the
applicable tools metadata and server expansion controls.

---

Nitpick comments:
In `@llm/configuration_test.go`:
- Around line 668-672: Extend the BotConfig coverage in the existing
configuration tests with table-driven cases that unmarshal and round-trip both
true and false values for UseServiceAccountAuth, verifying the loaded value is
preserved and serialized correctly. Retain the current false-field presence
assertions and use the existing MCPDynamicToolLoading/UseServiceAccountAuth
symbols.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 30bcc64f-1b71-4c69-95a7-39965355eecf

📥 Commits

Reviewing files that changed from the base of the PR and between 97fecc2 and 52bf800.

📒 Files selected for processing (26)
  • api/api_agents_test.go
  • api/api_config_test.go
  • api/api_llm_bridge.go
  • api/api_llm_bridge_service_account_test.go
  • api/api_llm_bridge_test.go
  • api/api_test.go
  • config/mcp_config_test.go
  • conversations/service_account_test.go
  • conversations/single_build_test.go
  • docs/admin_guide.md
  • docs/features/multiplayer_tool_calling.md
  • llm/configuration_test.go
  • llm/token_tracking_test.go
  • llmcontext/llm_context.go
  • llmcontext/llm_context_test.go
  • mcp/client_embedded_oauth_test.go
  • mcp/client_manager.go
  • mcp/client_test.go
  • mcp/service_account_test.go
  • mcp/user_clients.go
  • public/bridgeclient/README.md
  • store/agents_test.go
  • webapp/src/components/agents/agent_config_view.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.tsx
  • webapp/src/components/system_console/mcp_servers.test.tsx
💤 Files with no reviewable changes (2)
  • api/api_llm_bridge.go
  • api/api_llm_bridge_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • conversations/single_build_test.go
  • api/api_test.go
  • docs/admin_guide.md
  • webapp/src/components/agents/agent_config_view.test.tsx
  • mcp/client_manager.go
  • llmcontext/llm_context.go

Comment thread docs/features/multiplayer_tool_calling.md Outdated
@nickmisasi
nickmisasi marked this pull request as ready for review July 29, 2026 13:40

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52bf800312

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread webapp/src/components/agents/tabs/mcps_tab.tsx
Comment thread webapp/src/components/agents/tabs/mcps_tab.tsx
…rve header row order, doc fixes

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/mcp_config.go`:
- Around line 120-129: Update the ServiceAccountHeaders normalization loop to
detect duplicate trimmed names before inserting into headers, including
collisions caused by whitespace. Return or propagate a validation error for any
duplicate instead of overwriting an existing value; retain skipping of blank
names and values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5ce082db-2018-4b2f-be3c-8d6381dc4e65

📥 Commits

Reviewing files that changed from the base of the PR and between 52bf800 and e9bf589.

📒 Files selected for processing (7)
  • config/mcp_config.go
  • config/mcp_config_test.go
  • docs/features/multiplayer_tool_calling.md
  • mcp/client_manager.go
  • store/migrations/reviews/000010_user_agent_service_account_auth.md
  • webapp/src/components/system_console/mcp_servers.test.tsx
  • webapp/src/components/system_console/mcp_servers.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • store/migrations/reviews/000010_user_agent_service_account_auth.md
  • webapp/src/components/system_console/mcp_servers.test.tsx
  • config/mcp_config_test.go
  • webapp/src/components/system_console/mcp_servers.tsx
  • mcp/client_manager.go

Comment thread config/mcp_config.go Outdated
…t review findings

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
config/mcp_config.go (1)

114-142: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Canonicalize header names before collision checks in config/mcp_config.go:114-142 strings.TrimSpace(name) still treats Authorization and authorization as distinct, but the downstream request code applies headers with req.Header.Set, which folds them to the same wire header. That leaves case-only duplicates to clobber each other by map iteration order; use textproto.CanonicalMIMEHeaderKey (or equivalent case-folding) when counting and storing these headers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/mcp_config.go` around lines 114 - 142, Update
EffectiveServiceAccountHeaders to canonicalize each trimmed header name with
textproto.CanonicalMIMEHeaderKey before collision counting and storage. Use the
same canonical form in both loops so case-only duplicates such as Authorization
and authorization are treated as collisions and omitted, while preserving
existing trimming and empty-value filtering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@config/mcp_config.go`:
- Around line 114-142: Update EffectiveServiceAccountHeaders to canonicalize
each trimmed header name with textproto.CanonicalMIMEHeaderKey before collision
counting and storage. Use the same canonical form in both loops so case-only
duplicates such as Authorization and authorization are treated as collisions and
omitted, while preserving existing trimming and empty-value filtering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 71a4c31c-86f6-4ee9-9871-ca8370916ee9

📥 Commits

Reviewing files that changed from the base of the PR and between e9bf589 and 7c6bf30.

📒 Files selected for processing (10)
  • api/api.go
  • api/api_agents.go
  • api/api_agents_test.go
  • api/api_test.go
  • config/mcp_config.go
  • config/mcp_config_test.go
  • docs/admin_guide.md
  • webapp/src/components/agents/tabs/mcps_tab.test.tsx
  • webapp/src/components/agents/tabs/mcps_tab.tsx
  • webapp/src/i18n/en.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • webapp/src/i18n/en.json
  • docs/admin_guide.md

…l closed

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
@nickmisasi nickmisasi self-assigned this Aug 4, 2026
@nickmisasi nickmisasi added the 2: Dev Review Requires review by a developer label Aug 4, 2026
@nickmisasi nickmisasi added the 3: Security Review Review requested from Security Team label Aug 4, 2026
@nickmisasi

Copy link
Copy Markdown
Collaborator Author

@cursor fix this PR's conflicts with master.

@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

The merge conflicts with master are resolved and pushed. I merged origin/master into the branch; only two files conflicted, and everything else auto-merged.

  • webapp/src/components/agents/agent_config_view.test.tsx: master added two custom-instructions character-limit tests right where this branch had renamed "preserves explicit dynamic tool loading false on update" to "preserves explicit MCP settings on update" (adding the useServiceAccountAuth assertion). I kept both — master's new tests plus this branch's extended version of the update-preservation test.
  • webapp/src/i18n/en.json: both sides added one extracted string key; I kept both in sorted order.

Verification all passed: go build ./... and go vet ./... compile cleanly (the initial build failure was just the gitignored generated server/manifest.go missing in a fresh workspace, fixed by make apply), all 14 tests in the resolved test file pass, make check-i18n confirms en.json exactly matches re-extraction with no drift, and a repo-wide scan found no leftover conflict markers. The merge commit is pushed to cursor/mcp-service-account-auth-a475.

Open in Web Open in Cursor 

…account-auth-a475

# Conflicts:
#	webapp/src/components/agents/agent_config_view.test.tsx
#	webapp/src/i18n/en.json

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>
@mm-cloud-bot

Copy link
Copy Markdown

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to install plugin via mmctl: failed with status code 500

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit 11cef5fbdb1af9ade955bfe77a25001b726aa883

Plugin Version Artifact
agents 11cef5f Download

@mm-cloud-bot

Copy link
Copy Markdown

Test server destroyed

@edgarbellot edgarbellot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Great work! Let me know if you have any questions about my comments

Comment thread llmcontext/llm_context.go

// Get tools from all connected servers
mcpTools, mcpErrors = b.mcpToolProvider.GetToolsForUser(ctx, userID)
if useServiceAccount {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In SA mode this branch builds the embedded tool catalog for the bot user, not the requesting user, and ensureEmbeddedSessionID(key.userID) mints the Mattermost session for that bot. Every embedded tool call then runs with the bot's own permissions. Nothing downstream re-checks the tool's output against what the requesting user is actually allowed to see - the code only asks "can the bot do this", never "can this user see this".

Confirmed it live: a user who was not a member of a private channel asked the SA agent to summarize it. The agent called read_channel as the bot (which is a member) and returned the full message history to someone who should never have seen it.

Suggested fix: the missing piece is a permission check between the tool call and its result - something that, for channel-scoped reads, intersects the target channel against the requesting user's own membership before returning content, not just the bot's. Right now that check doesn't exist anywhere in the SA path.

Same root cause also affects write tools like add_channel_member (unconfigured tools default to enabled/ask per GetToolPolicy, config/mcp_config.go) - it only didn't fire in my test because the bot lacked manage_private_channel_members, which is Mattermost's own guard, not this code's. If a bot ever holds a higher channel/team role, the same missing check lets that write through too.

@nickmisasi nickmisasi Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is something I am aware of and have done intentionally. If we were to implement your suggested fix where permissions are intersected, you'd end up in a state where the service account has less access than if the invoker's permissions were just being used themselves.

Channel membership being required is the thing that is gating access. It's actually something that's already in the product: #611 - bot invocations of an Agent use the bot's access. I have an incoming commit to fix your finding here: #937 (comment) - after which only trusted system administrators will be able to set or manage these agents (non system administrator maintainers will be read only)

Long term as possible scoped tokens, or even ABAC for bot accounts (because they're just users at the end of the day) come into play for channel access, this'll get more secure. But for now it's a deliberate, documented thing: https://github.com/mattermost/mattermost-plugin-agents/pull/937/changes#diff-358388706355238989c5323624130cf04d7e4b827ea00f92e298c1ea23d6d19dR57-R67

Comment thread api/api_agents.go Outdated
return
}

// Only the false→true transition escalates; an already-enabled flag was

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Once a system admin turns on useServiceAccountAuth for an agent, anyone who can manage that agent - even without manage_system - can freely widen it afterwards: open it to every user on the instance, auto-enable every tool, or point it at a different LLM provider.

Why: the admin-only check only fires on the false→true transition of the flag. Every field after that - userAccessLevel, autoEnableNewMCPTools, adminUserIDs, serviceID - gets overwritten under the same check as any normal agent edit: canManageAgent, which passes for the agent's own creator or anyone in adminUserIDs. Neither of those requires being a system admin. So the code checks "is this person allowed to touch this agent at all", never "is this specific change one that needs admin rights".

Confirmed in my test instance: a system admin enabled SA on an agent created by, and scoped to, a regular member. That same member - still with no manage_system permission - then opened the agent to everyone and auto-enabled every tool, in one request that returned 200.

Is this intentional? If not, once useServiceAccountAuth is true, I would suggest that changes that widen its reach (userAccessLevel, userIDs, channelIDs, teamIDs, adminUserIDs, autoEnableNewMCPTools, enabledMCPTools) should require the same canEnableServiceAccountAuth check as enabling the flag itself, not just canManageAgent.

Comment thread mcp/client.go
headers := make(map[string]string)
headers[MMUserIDHeader] = c.userID
maps.Copy(headers, serverConfig.Headers)
headers := remoteConnectionHeaders(c.userID, serverConfig, c.serviceAccount)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The service-account header (the shared, admin-provisioned credential) gets resent to a different host if the configured MCP server responds with an HTTP redirect.

Why: remoteConnectionHeaders builds the SA header, and createSession hands it to httpClientForMCP, which wires it into a custom http.RoundTripper that re-adds the header on every outgoing request - including the one Go automatically sends when following a redirect. Go's http.Client normally strips Authorization on a cross-host redirect, but that only removes what's already set on the request; it can't stop a custom transport from putting the same header back one line later.

Reproduced in my test instance: pointed this client at a test server configured with a fake SA header, made it respond with a redirect to a different host, and watched that header arrive intact at the redirect target.

Suggested fix: the http.Client built in createSession/httpClientForMCP needs a CheckRedirect that either blocks redirects to a different origin than serverConfig.BaseURL, or drops the injected headers before the redirected request goes out - without that, re-adding the header unconditionally on every RoundTrip defeats any redirect check added elsewhere.

cursoragent and others added 4 commits August 6, 2026 16:47
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
…account-auth-a475

# Conflicts:
#	docs/user_guide.md

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
Co-authored-by: nick.misasi <nick.misasi@mattermost.com>
@nickmisasi

Copy link
Copy Markdown
Collaborator Author

/update-branch

@mattermost-build

Copy link
Copy Markdown
Collaborator

Error trying to update the PR.
Please do it manually.

@crspeller crspeller left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks mostly good, but I think we need to allow non-system admins to make service accounts.

Comment thread api/api_agents.go
// Any save that keeps service account auth enabled is admin-only, since every
// field shapes what the admin-granted credentials can do; anyone who can
// manage the agent may still turn it off.
if req.UseServiceAccountAuth && !canSaveServiceAccountAuth(a.pluginAPI, userID) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems clunky and involves system admins. Can we have these service accounts created on the user side and grant access to them? (This breaks Agents being managed by users, on Hub that means we are back to begging to update the PDE intake agent)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2: Dev Review Requires review by a developer 3: Security Review Review requested from Security Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants