Skip to content

Token introspection verification & integration tests#4180

Merged
stevenvegt merged 7 commits intofeature/4144-mixed-scopesfrom
4144-5-introspection-and-integration
Apr 24, 2026
Merged

Token introspection verification & integration tests#4180
stevenvegt merged 7 commits intofeature/4144-mixed-scopesfrom
4144-5-introspection-and-integration

Conversation

@stevenvegt
Copy link
Copy Markdown
Member

@stevenvegt stevenvegt commented Apr 13, 2026

Parent PRD

#4144

Summary

Final PR in the mixed OAuth2 scopes feature. Adds focused end-to-end integration tests for the dynamic scope policy path — real authzen.Client calls, httptest.NewServer standing in for the PDP — validating the HTTP roundtrip that mock-based unit tests skip.

No production code changes.

What changed

auth/api/iam/integration_test.go (new, +176) — one TestIntegration_DynamicScopePolicy_AuthZenEndToEnd with three subtests, each wiring a real authzen.Client pointed at an httptest.NewServer:

  1. PDP approves all scopes — token contains all scopes and the captured AuthZen request shape is asserted on the wire (subject.type=organization, action.name=request_scope, context.policy, evaluations in request order).
  2. PDP partial denial — denied extra scope excluded from token.Scope.
  3. PDP denies credential profile scope — rejected with access_denied, no token issued.

How to review

Read the test doc comment first — it states explicitly what's not covered to avoid duplicating policy/authzen/client_test.go (HTTP errors, timeouts) and s2s_vptoken_test.go (VP validation, profile-only rejection).

Then the three subtests. The startPDP helper both responds with canned decisions and captures the decoded request for post-call assertions. Subtest 1 is the only place where AuthZen request serialization is validated over the wire — evaluation order, context.policy, and subject.type all originate in code paths that unit tests mock past.

Deviations from spec

  • Dropped 6 of the originally-planned test cases during self-review:
    • HTTP 500 / network errors / timeouts — covered by policy/authzen/client_test.go
    • Profile-only / passthrough integration — covered by s2s_vptoken_test.go
    • Multi-scope introspection — introspection is pointer pass-through of token.Scope; no multi-scope-specific logic
    • Backwards-compatibility — no version-specific code path
  • Added access_denied integration (not in spec) — identified as security-critical during self-review.
  • Final suite: 3 focused tests instead of the spec's 6.

Dependencies

Depends on: #4179 (server-side multi-scope flow) — provides handleS2SAccessTokenRequest and PDPBackend.AuthZenEvaluator() that these tests exercise. Transitively depends on #4176, #4177, #4178.

Review order: #4176#4177#4178#4179#4180. No new production API here, so the review value is confirming the scenarios are the right ones and the assertions catch what matters.

Design context

Acceptance Criteria

  • Integration tests cover the dynamic scope policy end-to-end (real HTTP)
  • Security-critical paths (access_denied) validated over real HTTP
  • Request serialization verified on the wire
  • No production code changes needed (introspection passes token.Scope through unchanged)
  • Scope justified in the test doc comment (what's covered vs. intentionally not)
  • Multi-scope introspection tests — dropped: no multi-scope-specific code exists
  • Profile-only/passthrough integration tests — dropped: covered by existing unit tests
  • HTTP error / timeout integration tests — dropped: covered by policy/authzen/client_test.go
Original implementation spec (used during AI-assisted development)

Parent PRD

#4144

Implementation Spec

Overview

Final PR in the mixed OAuth2 scopes feature. Adds end-to-end integration tests that exercise the dynamic scope policy path with a real AuthZen HTTP client (via httptest.NewServer), validating the full HTTP roundtrip that mock-based unit tests skip over.

No production code changes.

Key files

  • auth/api/iam/integration_test.go (new) — E2E integration tests for dynamic scope policy

Design decisions

Scope narrowed during self-review. The spec suggested a broad set of integration tests covering all scope policies and introspection behavior. On review, many would duplicate existing unit tests or test trivial pointer pass-through. The final test suite is focused on what actually requires integration:

  • Real HTTP roundtrip for dynamic scope policy — exercises request serialization, response parsing, and end-to-end wiring that mock-based unit tests don't reach.
  • Security-critical path — PDP denying the credential profile scope over real HTTP.

Tests explicitly not included (and why):

  • HTTP 500 / network errors — already covered by policy/authzen/client_test.go
  • Profile-only / passthrough integration — already covered by s2s_vptoken_test.go unit tests
  • Multi-scope introspection — introspection is Scope: &token.Scope (pointer pass-through); no multi-scope logic exists
  • Backwards compatibility — no version-specific code path to test

Tests added

TestIntegration_DynamicScopePolicy_AuthZenEndToEnd — uses real authzen.Client pointing at httptest.NewServer:

  1. PDP approves all scopes — asserts the token contains all scopes AND that the HTTP request body had the correct shape over the wire (subject.type=organization, action.name=request_scope, context.policy, ordered evaluations)
  2. PDP partial denial — denied scopes excluded from the token
  3. PDP denies credential profile scope — request rejected with access_denied, no token issued

Deviations from original spec

  • Dropped 6 proposed test cases as duplicative or trivial (see design decisions)
  • Added access_denied integration test (originally not in spec but identified as security-critical during review)
  • Consolidated to 3 focused tests instead of 6

Acceptance Criteria

  • Integration tests cover the dynamic scope policy end-to-end (real HTTP)
  • Security-critical paths (access_denied) validated over real HTTP
  • Request serialization verified on the wire
  • No production code changes needed (confirmed: introspection passes token.Scope through unchanged)
  • Multi-scope introspection tests — dropped: no multi-scope-specific code exists to test
  • Profile-only/passthrough integration tests — dropped: covered by existing unit tests

@qltysh
Copy link
Copy Markdown

qltysh Bot commented Apr 13, 2026

Qlty


Coverage Impact

⬇️ Merging this pull request will decrease total coverage on feature/4144-mixed-scopes by 0.02%.

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

stevenvegt and others added 7 commits April 16, 2026 14:44
Exercises the server-side token handler with a real AuthZen HTTP client
talking to an httptest server. Unlike unit tests that mock the evaluator,
this validates the full HTTP roundtrip: request serialization, response
parsing, and error propagation.

Tests cover: PDP approves all, partial denial, HTTP 500 error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify that tokens with space-delimited scope strings (from multi-scope
requests) are returned unchanged via both IntrospectAccessToken and
IntrospectAccessTokenExtended. Also cover backwards compatibility for
single-scope legacy tokens.

No production code changes needed — the existing introspection passes
AccessToken.Scope through as-is, which correctly handles the OAuth2
space-delimited scope format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies that tokens issued via dynamic scope policy carry their
validated credential claims through to the introspection response as
AdditionalProperties, enabling resource servers to make authorization
decisions without re-processing VPs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop tests that duplicated existing unit-test coverage or tested trivial
pointer pass-through:
- Remove HTTP-500 integration test (covered by authzen client tests)
- Remove multi-scope introspection tests (introspection is a pointer
  pass-through; no multi-scope-specific code exists)
- Remove backwards-compat introspection test (no compat code exists)
- Remove multi-scope claims introspection test (duplicates existing
  InputDescriptorConstraintIdMap test)

Add the security-critical path:
- PDP denies credential profile scope over real HTTP → access_denied

Use t.Cleanup for httptest server cleanup (proper subtest scoping).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@stevenvegt stevenvegt force-pushed the 4144-5-introspection-and-integration branch from bd9d5ca to 828a1c1 Compare April 16, 2026 14:23
@stevenvegt stevenvegt marked this pull request as ready for review April 21, 2026 09:44
Base automatically changed from 4144-4-server-side-flow to feature/4144-mixed-scopes April 24, 2026 12:35
@stevenvegt stevenvegt merged commit 12fc532 into feature/4144-mixed-scopes Apr 24, 2026
3 of 4 checks passed
@stevenvegt stevenvegt deleted the 4144-5-introspection-and-integration branch April 24, 2026 12:36
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.

Support mixed OAuth2 scopes with configurable scope policy

1 participant