Skip to content

Add programmable demo resource server for RS spans and authorization scenarios #54

@michaelw

Description

@michaelw

Summary

Add an owned programmable demo resource server for local demo and e2e scenarios.

The current local stack relies on httpbin as the upstream resource server. That is useful for generic header echoing, but it does not provide resource-server application spans, stable introspection owned by this repo, or programmable RS-side authorization behavior.

The new RS should act as a standards-shaped e2e harness: it should echo what reached the resource server, emit OpenTelemetry application spans, and optionally enforce bearer/JWT authorization checks against configured route scenarios.

Goals

  • Show the resource server application span in Jaeger without requiring mesh tracing.
  • Replace httpbin response parsing with a stable introspection protocol owned by this repo.
  • Remove the current workaround where OPTIONS with bearer has to be verified through plugin/fake issuer logs because httpbin does not echo useful request headers in the response body.
  • Support fake issuer scenarios and Keycloak scenarios from Add with-keycloak e2e profile for real RFC 8693 token exchange testing #9.
  • Allow e2e scenarios to synthesize resource-server behavior such as allow, deny, fail, token validation, resource/audience/scope checks, and denylists.

Proposed Behavior

Add a new demo/e2e resource server with route-driven behavior.

Routes should use action, not mode.

Initial actions:

  • echo: validate according to route auth config, then return introspection JSON.
  • deny: return configured status/error without forwarding to any backend.
  • fail: synthesize configured upstream failure behavior.

The RS should expose:

  • /healthz
  • catch-all route handling for configured scenario paths
  • stable introspection JSON including:
    • method
    • path
    • host
    • selected headers
    • observed Authorization
    • parsed bearer token summary
    • decoded JWT claims when token is JWT-shaped
    • trace context observed at the RS
    • route/action/auth decision metadata safe for demo use

Token Validation

Support two explicit validation profiles:

  • unsigned-demo-jwt: compatibility with the existing fake issuer’s unsigned demo JWTs.
  • oidc-jwt: production-like validation for Keycloak-issued access tokens using OIDC discovery and JWKS.

For oidc-jwt, the RS should validate:

  • supported signing algorithm, initially RS256
  • issuer allowlist
  • signature via JWKS
  • exp
  • nbf when present
  • audience requirements
  • configured scope requirements
  • configured resource requirements where represented in the token
  • optional denylists

Bearer-token failures should use RFC 6750-shaped responses where practical:

  • missing/malformed token: bearer challenge / invalid_request
  • invalid signature, expired token, unknown issuer, denylisted token: invalid_token
  • valid token with insufficient scope/audience/resource: insufficient_scope or 403 as appropriate

Scenario Config Sketch

routes:
  - pathPrefix: /anything/keycloak-audience
    methods: ["GET", "OPTIONS"]
    action: echo
    auth:
      validation: oidc-jwt
      requireBearer: true
      trustedIssuers:
        - https://keycloak.int.kube/realms/token-exchange-e2e
      audiences:
        - tx-audience-client
      scopes:
        - profile

  - pathPrefix: /anything/yellow
    methods: ["GET", "OPTIONS"]
    action: echo
    auth:
      validation: unsigned-demo-jwt
      requireBearer: true
      issuers:
        - fake-token-endpoint
      audiences:
        - httpbin-yellow
      resources:
        - https://httpbin.int.kube/anything/yellow
      scopes:
        - yellow

  - pathPrefix: /anything/rs-denied
    methods: ["GET"]
    action: deny
    status: 403
    error: token_denied

Acceptance Criteria

  • A new demo/e2e resource-server binary is added.
  • The resource server initializes OpenTelemetry and emits HTTP server spans with service name fake-resource-server or equivalent.
  • The resource server has a documented, stable introspection response schema.
  • Existing fake issuer scenarios can verify exchanged or preserved upstream Authorization through RS introspection instead of httpbin response shape.
  • Non-preflight OPTIONS with bearer can verify the exchanged token from RS introspection, without scraping plugin or issuer logs.
  • True CORS preflight behavior remains covered and returns expected CORS headers.
  • Keycloak scenarios from Add with-keycloak e2e profile for real RFC 8693 token exchange testing #9 can validate Keycloak-issued exchanged access tokens at the RS using OIDC/JWKS.
  • RS auth failures map to safe bearer-style errors and never expose bearer tokens, exchanged tokens, client secrets, or raw issuer internals.
  • Route config supports at least action: echo, action: deny, and action: fail.
  • Route auth config supports issuer/audience/scope/resource checks and at least one denylist mechanism.
  • The e2e chart deploys the resource server and exposes values/schema for image, env, resources, and route config.
  • DevSpace local-test builds/deploys the resource-server image.
  • The tracing tutorial is updated so Jaeger screenshots can show plugin, issuer, and RS application spans.
  • Existing fake issuer and Keycloak e2e tests continue to pass after migration.
  • Documentation explains that this RS is a demo/e2e conformance harness, not a production resource server implementation.

Test Plan

  • Unit tests for route matching:

    • path prefix selection
    • method matching
    • * method support
    • no matching route behavior
  • Unit tests for actions:

    • echo
    • deny
    • fail
  • Unit tests for introspection:

    • stable JSON shape
    • header normalization
    • bearer summary
    • JWT claim extraction
    • trace context reporting
  • Unit tests for auth:

    • missing bearer
    • malformed bearer
    • unsigned demo JWT accepted only when configured
    • unsigned token rejected by oidc-jwt
    • Keycloak/OIDC JWT accepted with trusted issuer and valid JWKS signature
    • unknown issuer rejected
    • expired token rejected
    • invalid audience rejected
    • missing scope rejected
    • resource mismatch rejected
    • denylisted token/subject/scenario rejected
  • E2E tests:

    • fake issuer color scenarios still pass
    • unmatched pass-through preserves original bearer and is visible in RS introspection
    • non-preflight OPTIONS with bearer shows exchanged token in RS introspection
    • true CORS preflight bypasses token exchange
    • Keycloak exchanged token is accepted by the RS
    • at least one valid Keycloak token is rejected by the RS for RS-side authorization policy, such as audience/scope/resource mismatch

Notes

This builds on #9. The RS should use the Keycloak/OIDC/JWKS assumptions already introduced there rather than inventing a parallel issuer model.

If reusable token verification code is needed, extract it into a shared internal package that can be used by both the dashboard token verifier and the new resource server.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/configRuntime configuration, typed values, env wiringarea/e2eLocal demo, e2e chart, DevSpace local-test flowarea/observabilityTracing, metrics, admin/debug surfacesenhancementNew feature or requestpriority/mediumValuable but not blocking the nearest release

    Projects

    Status

    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions