Skip to content

feat: choose upstream credentials for apps that sign in without a browser - #8042

Merged
danielkov merged 124 commits into
mainfrom
daniel/aim-597-feat-headless-auth-client-choices
Oct 30, 2026
Merged

danielkov merged 124 commits into
mainfrom
daniel/aim-597-feat-headless-auth-client-choices

Conversation

@danielkov

@danielkov danielkov commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

AIM-597 (2 of 2)

Stacked on #8041.

Summary

  • Token requests choose per server. The ID-JAG grant and the workload grant accept upstream_auth_client=<mcp_server_id>:<auth_client_id|none>, repeatable (at most 100). A choice for a server the token does not reach is invalid_request.
  • Decision order per server, for a direct server or each proxied gateway member the subject reaches:
    1. The choice the request names. A credential the subject may not use is access_denied, one not connected or set up is setup_required; neither is silently replaced.
    2. The caller's issuance profile. A profile choice that no longer validates gives way to step 3.
    3. The only ready credential the subject may use. With several, the request fails with selection_required, naming the server.
  • Readiness without a browser. Personal connections are ready only when a stored token exists and needs no reconnect. Identity-provider (EMA) clients are judged when used. The consent preselection never applies.
  • Issuance profiles (writes serialized per caller; authClients.listIssuanceProfiles / setIssuanceProfile / deleteIssuanceProfile, org:admin to write): one per OAuth client, or per workload issuer and subject, on a sign-in. Choices are stored in upstream_client_selections under a new issuance_profile credential kind. A workload acts for nobody, so its profile takes shared credentials only. Choices grant nothing: each issuance checks access again. Audited as issuance-selection-profile:create|update|delete.
  • upstreamclients.CheckConfiguredChoice is now shared by agent keys and profiles.
  • Only where upstream_auth_enforced is on. Elsewhere nothing is decided at issuance, and a request naming a choice is refused rather than ignored.
  • Not covered: issuer-wide workload sessions (they target no single server), and assistant workloads (they bind execution profiles; AIM-598).
  • Dashboard: server settings → Upstream credentials gets an Apps without a browser row when the server offers two or more credentials. It lists each app's choice on this server, and admins can choose or remove one. Workloads are offered shared credentials only.
  • Demo seed: "Nightly report job", a Linear client that signs in without a browser, with a profile choosing the shared token on Linear. Postflight checks it.
  • Platform MCP: new choose_mcp_headless_caller_credential (org admin, external only), next to offer_mcp_upstream_credential. It previews the server's credentials and every headless caller's choice, then changes one caller's choice for that one server through the same serialized write the API uses, so the caller's other servers keep theirs.

Motivation

RFC sections 7.5 and 7.6: callers that cannot see the consent picker still need a definite credential per server. That credential comes from the request, from an admin-configured profile, or from an unambiguous default. Without one, the request fails with an actionable error instead of a silent guess.

Gutternote review steps

  1. Setup: the seeded demo data covers it. Linear offers its personal client and the shared "Engineering automation token".
  2. Sidebar → MCP → Linear → Settings → Upstream credentials.
  3. The Apps without a browser row lists Nightly report job using Engineering automation token.
  4. Click Choose for an app, pick an app, then a credential, and Save. The list updates.
  5. Remove the choice with the trash button. The row shows "None set."

🚀 Preview Environment (PR #8042)

Preview URL: https://pr-8042.dev.getgram.ai

Component Status Details Updated (UTC)
✅ Database Ready Existing database reused 2026-10-30 08:51:11.
✅ Images Available Container images ready 2026-10-30 08:57:03.

Gram Preview Bot

Administrators can create remote_session, service_account, static_credential
and pass_through auth clients, offer an existing client on another server, and
replace an administrator-created client's headers. The typed resolver treats
administrator-created clients and associations as authoritative instead of
checking them against the server settings that backfilled copies mirror.
Server settings add a credential another server already offers or create a
new one, and a credential's sheet edits and rotates its headers. The static
Authorization identity choice is renamed to a shared credential. Agent pages
edit credential blocks for managers and connection owners.
…o daniel/aim-596-feat-dashboard-auth-client-setup
Where typed auth client resolution is enforced, consent offers each server's
permitted auth clients, preselects the remembered choice or the fixed order,
and records the picked client and its own registration on the new
credential. Gateways offer the choice per member and capture tools through it.
A member added after a credential was issued gets no-auth or its single
ready permitted client recorded atomically the first time it is used, where
typed auth client resolution is enforced. Otherwise it stays unavailable
until the person reconnects to choose.
Agent key creation records an auth client choice per server under a new
api_key credential kind, rotation copies it, and under enforcement the key
is served through those choices on direct servers and agent gateway members.
The key wizard asks for a credential on servers offering several.
…wser

Token requests name a per-server choice with upstream_auth_client; issuance
profiles let an admin choose for an OAuth client or workload subject; with
neither, the only ready credential is used, else selection_required.
@danielkov
danielkov requested review from a team as code owners October 24, 2026 16:12
@linear-code

linear-code Bot commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

AIM-597

@changeset-bot

changeset-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea2f8b5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
server Minor
dashboard Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the preview Spawn a preview environment label Oct 24, 2026
@danielkov

Copy link
Copy Markdown
Contributor Author

Demo

Upstream credentials with the Apps without a browser row

What it shows:

  1. Linear offers two credentials, so Apps without a browser appears under them.
  2. The seeded "Nightly report job" uses the shared token on Linear.

@danielkov

Copy link
Copy Markdown
Contributor Author

Choosing a credential for a workload

Choosing for a workload: only shared credentials are offered, because a workload acts for nobody.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed

This PR changes authentication, authorization, or input validation. Ultrareviews find 2.4x more serious bugs than standard reviews. Comment @cubic-dev-ai ultrareview to run one.

View guided diff | Turn on auto-fix | Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 73 files

Requires human review: Auto-approval blocked because this review re-detected 6 unresolved issues already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread server/internal/authclients/queries.sql Resolved
Comment thread server/design/authclients/design.go Resolved
Bind a registration's issuer to the client's project, refuse empty header
entries, keep stored secrets across '_'/'-' spellings, report members'
choice among several credentials in get_my_mcp_connection_status, map the
post-offer read failure, and cover admin clients and cross-server offers.
…o daniel/aim-596-feat-dashboard-auth-client-setup
…aniel/aim-596-feat-consent-auth-client-picker
Let an agent approval proceed when only the person lacks access, offer
connect and reconnect for identity-provider clients, record no client for a
no-auth server, and let a later no-auth approval clear the remembered client.
…niel/aim-596-feat-gateway-new-member-choices

# Conflicts:
#	server/internal/mcpservers/upstreamclients/queries.sql
…o daniel/aim-596-feat-dashboard-auth-client-setup
…aniel/aim-596-feat-consent-auth-client-picker
…niel/aim-596-feat-gateway-new-member-choices
…niel/aim-596-feat-agent-key-auth-client-choices
… daniel/aim-597-mig-issuance-selection-profiles
…niel/aim-597-feat-headless-auth-client-choices
@blacksmith-sh

This comment has been minimized.

@daviddanialy daviddanialy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

approved for prod iterating behind feature flag

Drops the "New:" prefix, names the reuse option "Existing credential" and
leaves it out when no other server has a credential to share. The upstream
sign-in row now says what probing a remote upstream found.
…aniel/aim-596-feat-consent-auth-client-picker
…niel/aim-596-feat-gateway-new-member-choices
…niel/aim-596-feat-agent-key-auth-client-choices
… daniel/aim-597-mig-issuance-selection-profiles
…niel/aim-597-feat-headless-auth-client-choices
…suance-selection-profiles

Regenerate the issuance selection profiles migration on top of main (SQL unchanged).
Base automatically changed from daniel/aim-597-mig-issuance-selection-profiles to main October 29, 2026 22:32
…choices

# Conflicts:
#	server/internal/demoseed/queries/04_servers.sql
#	server/internal/demoseed/repo/09_postflight_identity.sql.go
#	server/internal/platformmcp/queries.sql

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

cubic analysis

All reported issues were addressed across 78 files

Linked issue analysis

Linked issue: AIM-597: feat: establish explicit auth choices for headless token issuance

Status Acceptance criteria Notes
✅ Honor explicit per-server choices on supported token grants The ID-JAG and workload grant handlers parse per-server choices and pass them to the headless resolver.
✅ Use profiles or a sole ready option; never guess The resolver follows request, profile, then unambiguous-ready-credential order and returns selection-required instead of consent preselection.
✅ Validate access and readiness before recording session choices The resolver validates choices across targeted servers before recording them; invalid access or setup returns an error.
✅ Configure caller-scoped profiles through management and admin tools The PR adds profile list, set, and delete APIs, dashboard controls, and a Platform MCP credential-choice tool.
✅ Keep inbound EMA separate; restrict workloads to shared credentials The token handler treats inbound EMA as identity only, while workload profiles are limited to shared credentials.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread server/internal/platformmcp/queries.sql Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

@danielkov
danielkov added this pull request to the merge queue Oct 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 30, 2026
@danielkov
danielkov added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 5fcad8a Oct 30, 2026
50 of 54 checks passed
@danielkov
danielkov deleted the daniel/aim-597-feat-headless-auth-client-choices branch October 30, 2026 09:22
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

preview Spawn a preview environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants