Skip to content

API security: SMART 2.2 readiness and authorization semantics test suite #8

Description

@cursor

Summary

HAIStack provides SMART scope parsing, JWT validation, backend-service client assertions, and a deny-by-default policy engine — but authorization semantics are under-tested and partially stubbed. A successful OAuth/SMART token exchange is not an authorization test. We need explicit, CI-enforced scenarios covering restricted principals, patient-compartment boundaries, denied resources, search-result filtering, token expiry, backend-service clients, and policy narrowing of apparently valid scopes.

Problem / gap

What we have today

pkg/smart (v1 — done per roadmap):

  • Scope parsing and matching (patient|user|system read/write)
  • JWT claim validation (iss/aud/exp/nbf)
  • Backend-service client assertions with mandatory jti replay protection
  • AuthAdapter translating SMART inputs into pkg/auth principals

pkg/auth (v1 — done per roadmap):

  • Principals, roles, permissions, tenant/device context
  • Deny-by-default JSON/YAML policy DSL
  • Resource, view, AI tool, sync, and module install checks
  • CheckPatientScopeexplicitly documented as a stub in places
  • AuditingEngine for optional allow/deny event emission

pkg/http:

  • Pluggable PrincipalResolver + AuthChecker
  • Auth disabled by default when hooks are nil
  • No built-in OAuth2/SMART token endpoint or .well-known/smart-configuration server routes

pkg/client:

  • SMART discovery, auth-code + PKCE, backend client assertion token exchange
  • No SMART 2.2-specific scope features or test vectors

Testing:

  • Unit tests in pkg/auth/auth_test.go cover some patient-scope cases
  • No integrated authorization scenario matrix across HTTP → search → view → AI paths
  • No Inferno SMART App Launch tests in CI
  • No tests for "scope grants access but policy denies" (SMART's explicit warning)

What is missing

  1. Semantic authorization test suite — not OAuth flow tests, but access-control outcome tests:

    • User with restricted role cannot read/write denied resource types
    • Patient-scoped principal can only access their compartment
    • Patient-scoped search returns filtered results (not full index)
    • Direct read of out-of-scope resource returns 403/404 as appropriate
    • Token past exp rejected; nbf in future rejected
    • Backend-service client (system/*.read) vs user context behave differently
    • Policy rule narrows scope (e.g. patient/*.read granted but policy allows only Observation)
    • Consent/policy change mid-session affects subsequent requests
    • View execution (pkg/view) and AI tool calls (pkg/ai) respect same policy as REST
  2. Patient compartment completeness

    • CheckPatientScope stub must become production-grade for edge deployments
    • Search layer must apply patient-scope filters consistently (not just read path)
    • _include / _revinclude must not leak out-of-scope resources
  3. SMART 2.2 readiness

    • Evaluate SMART 2.2 granular scopes, context parameters, and ecosystem support
    • Document target SMART version and compatibility matrix
    • Add test vectors for 2.2 scope formats where adopted
  4. Server-side SMART surface (host contract)

    • HAIStack may not own OAuth servers, but should document and optionally provide:
      • .well-known/smart-configuration response shape for pkg/http hosts
      • Example PrincipalResolver + AuthChecker wiring for SMART Bearer tokens
      • Reference integration test harness (mock auth server + real policy enforcement)
  5. Negative test catalogue

    • Documented matrix of "principal X, action Y, resource Z → expect deny"
    • Golden OperationOutcome responses for authorization failures

Why this matters for HAIStack

  • Core value proposition: "Safe AI access" (pkg/ai) and permissioned views (pkg/view) depend entirely on auth — a token parsing success that over-authorizes defeats the product story.
  • Edge/offline deployments: May not use a full IdP; device trust + local policy must still enforce compartment boundaries.
  • SMART ecosystem reality: SMART warns that underlying policy may narrow apparently valid scopes — HAIStack's policy DSL is exactly that layer, but it is untested as a system.
  • Integration partners: Will ask for evidence beyond "we parse scopes" — they need scenario-based proof.

Proposed scope

Phase 1 — Authorization scenario test kit

  • Create pkg/testkit/authztest/ (or extend pkg/testkit) with:
    • Principal fixtures (restricted clinician, patient-scoped user, backend service, device, admin)
    • Policy fixtures (narrowing rules, deny-by-default, tenant isolation)
    • Scenario runner: given principal + request → assert allow/deny + OperationOutcome
  • Scenarios must cover:
    • CRUD on allowed vs denied resource types
    • Patient compartment boundary (read, search, history)
    • Search result filtering for scoped principals
    • _include / _revinclude leakage prevention
    • View execution authorization (pkg/view)
    • AI tool authorization (pkg/ai)
    • Sync push authorization (pkg/sync device trust)
    • Module install authorization (pkg/modules)

Phase 2 — Token and SMART semantics

  • Expand pkg/smart tests:
    • Expired token (exp in past)
    • Not-yet-valid token (nbf in future)
    • Wrong aud / iss
    • Backend assertion jti replay (already partially covered — ensure HTTP-integrated)
    • Scope vs policy conflict: token allows, policy denies → deny wins
  • Add SMART 2.2 scope parsing tests (or document deferral with gap analysis)
  • Add examples/smart-authz runnable demo wiring mock tokens → policy → HTTP

Phase 3 — Patient scope hardening

  • Replace CheckPatientScope stub with production implementation:
    • Enforce on resource read/write/delete
    • Enforce on search (query rewriting or post-filter with correct total counts)
    • Enforce on bundle transaction entries
    • Enforce on _history
  • Integration tests via pkg/http with AuthMiddleware enabled

Phase 4 — Documentation and host contract

  • Document authorization architecture: SMART token → smart.AuthAdapterauth.PolicyEngine → data paths
  • Document that OAuth success ≠ authorization; list required scenario tests for hosts
  • Provide reference PrincipalResolver implementation for Bearer JWT + SMART scopes
  • Optional: SMART metadata helper for hosts serving .well-known/smart-configuration

Phase 5 — External conformance (stretch)

  • Evaluate Inferno SMART App Launch tests against reference pkg/http deployment
  • Add optional CI job (may require secrets/mock IdP)

Acceptance criteria

  • ≥30 documented authorization scenarios in CI, all passing
  • Every data path (REST, search, view, AI, sync) has at least one deny scenario tested
  • Patient-scoped search cannot return out-of-compartment resources
  • Policy deny overrides SMART scope allow (tested and documented)
  • Token expiry and replay scenarios tested end-to-end through HTTP adapter
  • pkg/auth patient scope no longer marked as stub in docs
  • examples/smart-authz demonstrates restricted vs unrestricted principals

Out of scope (for this issue)

  • Building a full OAuth2/OIDC authorization server
  • EHR/standalone SMART launch UI orchestration
  • Dynamic client registration server
  • Refresh token lifecycle management

Affected packages / files

Area Path
Policy engine pkg/auth/
SMART adapters pkg/smart/
HTTP middleware pkg/http/
Search pkg/search/
Views pkg/view/auth.go
AI tools pkg/ai/
Sync pkg/sync/, pkg/modules/auth.go
Test kit pkg/testkit/
Examples examples/ai-authz, new examples/smart-authz

References

  • SMART App Launch 2.2
  • SMART guidance: underlying policy may narrow apparently valid scopes
  • HAIStack pkg/smart/README.md — v1 scope and explicit out-of-scope items
  • HAIStack pkg/auth/README.md — patient-scope stub note

Related issues

  • Conformance artefacts (IG + examples)
  • Bulk Data (backend-service client authorization for export)
  • Benchmark harness (concurrent authz under load)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions