Skip to content

fix(auth): require an access-token type on the OIDC bearer path (supersedes #1077)#1078

Open
seonghobae wants to merge 7 commits into
developfrom
fix/oidc-require-access-token-type
Open

fix(auth): require an access-token type on the OIDC bearer path (supersedes #1077)#1078
seonghobae wants to merge 7 commits into
developfrom
fix/oidc-require-access-token-type

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What

Fix the Strix HIGH (CVSS 8.8) "OIDC ID tokens accepted as API bearer sessions without token-type validation" in backend/api/auth.py, and add regression tests.

The OIDC verifier accepted any RS256 token whose signature, issuer, and aud matched OIDC_CLIENT_ID, with no token-type check. An OIDC ID token carries aud == client_id, so a frontend ID token could be replayed directly as an API access token (RFC 8725 §3.11).

Why this direction

naruon's API credential is the OIDC access token, not the ID token:

  • frontend/src/app/auth/oidc/callback/route.ts:112 and frontend/src/app/auth/session/route.ts:288 send token_response.access_token as the bearer. There is no id_token credential path anywhere in the frontend.
  • The IdP is Keycloak 24 (docker-compose.infra.yml, docker-compose.gateway.yml). Keycloak access tokens carry body typ: "Bearer"; ID tokens carry typ: "ID".

So the fix requires the token to be marked as an access token — Keycloak body typ: "Bearer", or RFC 9068 header typ: "at+jwt" — and rejects ID-token material and unmarked tokens.

Change

  • Add _require_oidc_access_token(header, payload) to the OIDC decode path; reject anything not marked as an access token.
  • Add regression cases for typ: "ID", the unmarked PoC shape, a header-id variant, and an accepted RFC 9068 at+jwt access token.
  • Update OIDC access-token fixtures to carry the realistic typ: "Bearer".
  • Merge current develop at HEAD 5611121bcc9d21cf0cb551c18a8d6917c5c00f3d, resolving the prior merge conflict.
  • Keep the current base's stronger screenshot SSRF fix: only literal http://127.0.0.1:3000/... targets reach Playwright; no Semgrep suppression remains.

Current-head verification

  • uv run pytest tests/test_auth_real.py tests/test_repo_hygiene.py -q113 passed.
  • uv run ruff check api/auth.py tests/test_auth_real.py tests/test_repo_hygiene.py — passed.
  • uvx --from semgrep==1.169.0 semgrep --config p/javascript --error frontend/screenshot.cjs0 findings across 68 rules.
  • CodeGraph synchronized and _decode_cached_oidc_session_payload -> _require_oidc_access_token plus rejection/acceptance tests inspected.
  • GitHub current-head Trivy, OSV, dependency-review, Bandit, Semgrep, CodeQL JavaScript/TypeScript, CodeQL Python, backend CI, coverage-source-tree, and coverage-evidence checks passed at the time of this update.
  • PR-scoped open code-scanning alerts: 0 after the current Semgrep upload.

Supersedes #1077

#1077 took the inverse approach (treat the ID token as the credential, reject scope/scp). Run against a realistic Keycloak access token, #1077 rejects it and accepts the ID token. Keep #1077 closed/draft in favor of this PR.

The OIDC verifier accepted any RS256 token whose signature, issuer, and
audience matched, without checking token type. Since an OIDC ID token carries
aud == client_id, a frontend ID token could be replayed as an API access token
(RFC 8725 §3.11 / Strix HIGH, CVSS 8.8).

naruon's API credential is the OIDC access token (the frontend sends
token_response.access_token). Require the token to be marked as an access
token — Keycloak body typ="Bearer" or RFC 9068 header typ="at+jwt" — and reject
ID-token material (Keycloak typ="ID") and unmarked tokens.

Regression tests replay an ID-token-shaped token and assert 401; they fail on
the prior code and pass here. OIDC access-token fixtures updated to carry the
realistic typ="Bearer".

Supersedes #1077, whose inverse approach rejected scope-bearing Keycloak access
tokens (breaking SSO login) while still admitting ID tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR governance metadata gate is not ready for 5611121bcc9d21cf0cb551c18a8d6917c5c00f3d:

  • Required check opencode-review is FAILURE on the current head.

Comment thread backend/api/auth.py Fixed
@opencode-agent opencode-agent Bot disabled auto-merge July 13, 2026 12:21
@github-advanced-security

Copy link
Copy Markdown
Contributor

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@seonghobae seonghobae enabled auto-merge July 13, 2026 12:26
@opencode-agent

opencode-agent Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: 51c7b931d4419954b9d68a258e20d37e5167a278
  • Workflow run: 29253516271
  • Workflow attempt: 1
  • Gate result: APPROVE (exit 0)

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Backend (3 files)"]
  S1 --> I1["API and service runtime"]
  I1 --> R1["Review risk: Backend (3 files)"]
  R1 --> V1["backend tests"]
  Evidence --> S2["Changed file: docker-compose.infra.yml"]
  S2 --> I2["repository behavior"]
  I2 --> R2["Review risk: Changed file: docker-compose.infra.yml"]
  R2 --> V2["required checks"]
  Evidence --> S3["Frontend: screenshot.cjs"]
  S3 --> I3["browser runtime and bundle"]
  I3 --> R3["Review risk: Frontend: screenshot.cjs"]
  R3 --> V3["frontend tests"]
Loading

Comment thread frontend/screenshot.cjs Fixed
@opencode-agent opencode-agent Bot disabled auto-merge July 13, 2026 13:10
…ccess-token-type

# Conflicts:
#	backend/tests/test_repo_hygiene.py
#	docker-compose.infra.yml
#	frontend/screenshot.cjs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants