Skip to content

docs: add Keycloak SSO setup guide - #188

Open
gcgoncalves wants to merge 2 commits into
mainfrom
6929-sso-setup-docs
Open

gcgoncalves wants to merge 2 commits into
mainfrom
6929-sso-setup-docs

Conversation

@gcgoncalves

@gcgoncalves gcgoncalves commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Closes: IBM/mcp-context-forge#6929

Document registering this BFF as its own Keycloak client (confidential, standard flow, PKCE S256), the redirect-URI exact-match gotcha, the SSO_* env vars, and the gateway-side trusted_for_api_auth/api_audience admin-API step -- which has no env var equivalent. Links to mcp-context-forge's own Keycloak tutorial for realm/role-mapper detail rather than duplicating it.

Scope note:
This PR also adds docs/SSO.md, a 454-line architecture trace of the full login/refresh/logout flow. That's broader than issue IBM/mcp-context-forge#6929asked for (setup docs only) — flagging it here rather than splitting it out post-hoc, since the two docs cross-reference each other throughout. Reviewers should treat SSO.md as a second, independent review surface: it documents internals (cache TTLs, defaults, GETDEL/timingSafeEqual details) that will need upkeep as the code changes, separately from the setup guide's accuracy.

@gcgoncalves
gcgoncalves requested review from marekdano and vishu-bh and removed request for vishu-bh October 12, 2026 12:44

@marekdano marekdano 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.

1. 🔴 Functionally-impacting — Missing SSO_API_TOKEN_AUTH_ENABLED step

File: docs/sso-keycloak-setup.md, look at section 3 of this doc

The guide never says to set SSO_API_TOKEN_AUTH_ENABLED=true on the gateway. It defaults to false (mcpgateway/config.py:629). With the default, the trusted_for_api_auth admin-API step the guide describes has no effect — every proxied call returns 401. Only docs/SSO.md's section 7 mentions the flag; issue #6929 asks the guide to be sufficient on its own.

Fix: Add the flag as step 3.0, same code-block style as the PUT call; name it in a "login succeeds but every API call 401s" troubleshooting note.

2. 🔴 Functionally-impacting — Wrong provider-id guidance

File: docs/sso-keycloak-setup.md, look at section 3 of this doc

"Find the provider id (usually the realm/client slug…)" is wrong — the bootstrap hardcodes "id": "keycloak" (mcpgateway/utils/sso_bootstrap.py:268). A reader will search for a slug that doesn't exist.

Fix: State the id is keycloak; keep the GET call only as a way to confirm it.

3. 🔴 Functionally-impacting — SSO.md scope drift

File: docs/SSO.md (454 lines, whole file)

Not requested by the PR description or issue #6929, which asks only for setup docs. Quotes internals (cache TTLs, defaults, GETDEL/timingSafeEqual details) that will drift out of sync with code. Also adds 6 line-number-keyed entries to .secrets.baseline, which break on any future edit above them.

Fix: Split into its own PR with its own review, or call out the scope explicitly in the PR body; replace line-keyed baseline entries with # pragma: allowlist secret where possible, or regenerate the baseline last.

4. 🟡 Medium — PUBLIC_ORIGIN/TRUST_PROXY rule stated backwards

File: docs/sso-keycloak-setup.md — "redirect-URI gotcha"

Guide says to set PUBLIC_ORIGIN "whenever TRUST_PROXY=true," attributing this to OAUTH.md. Inverted: config.ts:238 requires PUBLIC_ORIGIN or TRUST_PROXY=true, not both — confirmed by OAUTH.md:60,389 ("or," not "and") — and index.ts:41 wires trustProxy into Fastify so request.protocol correctly follows X-Forwarded-Proto once trusted.

Fix: "Set PUBLIC_ORIGIN explicitly. Without it, the BFF derives the origin from request.protocol/request.host, which is wrong behind a proxy you do not trust with TRUST_PROXY=true."

5. 🟡 Medium — TTL-decision bullet mischaracterizes fallback

File: docs/SSO.md:291

Claims a refresh-token-less session is "capped at the access token's own expires_in." Actually establish-session.ts falls back to the full config.sessionTtlSeconds when expires_in is missing/invalid. The session does die quickly, but via a separate mechanism — tokenExpiresAt stamped at now + 0 tripping isActuallyExpired on the next request — not the one described.

6. 🟢 Minor — Trailing-slash example missing

File: docs/sso-keycloak-setup.md — "redirect-URI gotcha"

Bullet titled "Trailing slash / missing port" only shows a :443 example.

Fix: Add a trailing-slash example, or retitle the bullet.

7. 🟢 Minor — Loopback http: exception omitted

File: docs/sso-keycloak-setup.md, look at section 2 of this doc

SSO_KEYCLOAK_PUBLIC_BASE_URL row says it "Must be https://"; validateKeycloakUrl (config.ts:40) also allows http: for loopback hosts.

Fix: Add "(http: only for localhost/127.0.0.1)".

8. 🟢 Minor — Stale process narration

File: docs/sso-keycloak-setup.md, look at section 3 of this doc

"confirmed by reading the gateway's own sso_bootstrap.py" is working-process narration that will go stale.

Fix: Delete; keep only the fact that the setting is admin-API-only.

9. 🟢 Minor — Inconsistent "why a second client" rationale

File: docs/SSO.md, look at section 1 of this doc, vs sso-keycloak-setup.md "Why a second client"

"not designed to hand a session back to a third party" vs "not cross-origin-safe" — two different reasons, neither backed by a gateway reference.

Fix: Use one sentence in both places, or link a gateway doc/issue that supports the claim.

10. 🔵 Suggestion — Name required admin-token permissions

File: docs/sso-keycloak-setup.md, look at section 3 of this doc

Guide doesn't say the admin token needs admin.sso_providers:read and admin.sso_providers:update.

Fix: Add one line naming them so a non-platform-admin token doesn't fail with 403.

11. 🔵 Suggestion — No recommended api_audience

File: docs/sso-keycloak-setup.md, look at section 3 of this doc

No recommended value shown; a shared audience like mcp-gateway weakens the confused-deputy protection api_audience provides.

Fix: Recommend an audience specific to this BFF client, and show the Audience-mapper steps or link to the gateway tutorial section on them.

gcgoncalves and others added 2 commits October 13, 2026 16:08
Document registering this BFF as its own Keycloak client (confidential,
standard flow, PKCE S256), the redirect-URI exact-match gotcha, the
SSO_* env vars, and the gateway-side trusted_for_api_auth/api_audience
admin-API step -- which has no env var equivalent. Links to
mcp-context-forge's own Keycloak tutorial for realm/role-mapper detail
rather than duplicating it.

Refs: IBM/mcp-context-forge#6929

Signed-off-by: Gabriel Costa <gabrielcg@proton.me>
  - Add the missing SSO_API_TOKEN_AUTH_ENABLED=true gateway step (split
    section 3 into 3.1/3.2); without it the trusted_for_api_auth step has
    no effect and every proxied call 401s.
  - Fix provider-id guidance: the gateway bootstrap hardcodes id "keycloak",
    not a realm/client slug.
  - Correct the PUBLIC_ORIGIN/TRUST_PROXY redirect-URI guidance: the
    derived origin is already correct once TRUST_PROXY=true and the proxy
    sets forwarded headers; PUBLIC_ORIGIN is for when the proxy isn't
    trusted, not "whenever TRUST_PROXY=true".
  - Fix SSO.md's TTL-decision bullet: a missing/invalid expires_in falls
    back to the full sessionTtlSeconds, not a cap derived from the token;
    tokenExpiresAt ends the session on the next request instead.
  - Minor: add a trailing-slash example, note the loopback http: exception
    on SSO_KEYCLOAK_PUBLIC_BASE_URL, name the admin-token permissions
    needed, flag shared-audience reuse as weakening api_audience's
    confused-deputy protection, remove stale sso_bootstrap.py narration,
    and unify the "why a second client" wording between the two docs.

Signed-off-by: Marek Dano <mk.dano@gmail.com>
@marekdano
marekdano force-pushed the 6929-sso-setup-docs branch from 67ba205 to 33d4613 Compare October 13, 2026 15:22
@marekdano

marekdano commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Pushed fixes for the review findings above (1, 2, 4–11) since Gabriel's out:

  • Added the missing SSO_API_TOKEN_AUTH_ENABLED=true gateway step (section 3 split into 3.1/3.2)
  • Fixed the provider-id guidance (keycloak, not a realm/client slug)
  • Corrected the PUBLIC_ORIGIN/TRUST_PROXY redirect-URI logic
  • Fixed the TTL-decision bullet in SSO.md
  • Addressed the minor/suggestion items (trailing-slash example, loopback http: exception, admin-token permissions, audience-reuse note, unified "why a second client" wording)

Item 3 (SSO.md scope) is handled via the updated PR description instead of a code change.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup docs

2 participants