Repository navigation
Support Microsoft Entra ID for MCP OAuth - #86
Open
josipmrsic wants to merge 2 commits into
Open
josipmrsic wants to merge 2 commits into
josipmrsic wants to merge 2 commits into
Conversation
Microsoft Entra ID could sign people in through the browser but could not protect /mcp. Four provider facts stood in the way, and each now has an optional setting or a narrower check whose default keeps today's behavior: - Discovery omits code_challenge_methods_supported although Entra supports S256. An issuer that leaves the field out is accepted; one that lists methods without S256 is still refused. - ARTIFACT_SERVER_OIDC_MCP_AUDIENCE replaces <origin>/mcp as the accepted aud value, because Entra v2.0 access tokens always name the API's client ID. - ARTIFACT_SERVER_OIDC_MCP_SCOPES is advertised as scopes_supported in the MCP protected-resource metadata, so clients request the API's own scope instead of receiving a Microsoft Graph token. - ARTIFACT_SERVER_OIDC_SUBJECT_CLAIM binds people by a claim other than sub on both paths. Entra's sub differs per app registration; oid does not. Decision 0029 records the change and amends the audience rule of 0028; AUTH-029 covers it with a stub issuer shaped like Entra. The deployment guide replaces "Entra cannot protect /mcp" with the Entra setup, including the Application ID URI that avoids AADSTS9010010.
The chart now names the subject-claim and MCP settings in its OIDC validation error, so the static Helm check matches the stable tail of the message and also covers an MCP audience without an issuer.
This was referenced Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi! We found Artifact Server a few weeks ago and really like it. We're setting it up as a private-team installation for our company (about 300 people), and like many companies we sign in with Microsoft Entra ID.
Browser login with Entra worked out of the box.
/mcpdidn't, and the deployment guide already says so ("Microsoft Entra ID cannot protect/mcpthis way"). This PR closes that gap with three optional settings and one narrower discovery check. Without the new settings, every installation behaves exactly as before.What stood in the way
We tested against a real Entra tenant with Claude Code as the MCP client:
code_challenge_methods_supported, so startup turned MCP OAuth off ("OIDC discovery does not advertise S256 PKCE").audis the API's client ID. Entra v2.0 access tokens always name the resource app's client ID, never a URL.scopes_supported, which wasn't published.subis pairwise per app registration.oidis the stable per-person ID in the tenant. Withsub, moving to a new app registration would orphan every member binding.What changes
ARTIFACT_SERVER_OIDC_SUBJECT_CLAIMsuboidARTIFACT_SERVER_OIDC_MCP_AUDIENCE<origin>/mcpARTIFACT_SERVER_OIDC_MCP_SCOPES<origin>/mcp/<scope>code_challenge_methods_supportedout. One that lists methods withoutS256is still refused, and clients always send S256.<origin>/mcprather than adding to it. The protected-resource metadata still names<origin>/mcpas the resource, since MCP clients check it against the URL they connected to.oidc:<issuer>+ subject binding stays one per person. A missing or blank claim is refused.ARTIFACT_SERVER_OIDC_*family, so the all-or-nothing and WorkOS-exclusion checks cover them. Helm, both Compose files and the.envexamples carry them.One piece of the puzzle needs no code: Claude Code sends
<origin>/mcpas the RFC 8707resourceparameter, and Entra refuses aresourcethat doesn't match the scope's app (AADSTS9010010). Setting the app's Application ID URI to<origin>/mcpfixes that; the new "Use Microsoft Entra ID" section indocs/deployment.mdwalks through it.What we deliberately left out
emailoremail_verified. We kept 0028's rule as it is rather than mappingpreferred_username; a person who signed in through the browser once is recognized on/mcpby issuer and subject.Spec and tests
implementing, with a proof gap for deployment evidence). Its tests use the existing stub issuer, which happens to omit the PKCE methods just like Entra, plus anoidclaim knob.oid, a changedsubkeeping one membership, and an email-less access token with the client-ID audience reaching/mcpas the same member.Verification
pnpm checkpasses locally.verify:iterationtier on our fork, Linux and macOS both green: https://github.com/josipmrsic/artifact-server/actions/runs/37789296735That run's branch also carries a one-line update of the pinned Amazon RDS CA bundle checksum. AWS replaced
global-bundle.pemon 2026-09-29, and since then the OCI build fails onmainwith a digest mismatch (your nightly full gate too). We're sending that fix as its own small PR./mcp→ authenticate → tool calls, recognized as the same single member.Happy to rename the variables, split the PR, or change anything else to fit how you'd like this to look.