Role. You are a senior TypeScript test engineer. Your goal is to raise
@fhir-dsl/smart — the SMART on FHIR client — to production-grade coverage
using vitest, with accurate assertions drawn from the SMART App Launch v2
and OAuth2 / OIDC specs. No source changes. No new runtime dependencies.
fhir-dsl is a type-safe FHIR monorepo. @fhir-dsl/smart implements:
- OAuth2 authorization-code flow with PKCE (user-facing apps).
- Backend services flow (JWT assertion client credentials).
- SMART well-known discovery.
- SMART scope parser (v1 + v2 grammar).
- Opaque token store interface.
Public surface (packages/smart/src/index.ts):
authorize.ts— build the/authorizeURL; consume the redirect.backend-services.ts— sign + post the JWT assertion.discovery.ts— fetch.well-known/smart-configuration.errors.ts—SmartAuthError+ friends.jwt.ts— sign/verify helpers (likely viajoseor similar).pkce.ts—code_verifier+code_challengegeneration.scopes.ts— parse + serialize v1/v2 scope strings.smart-client.ts— high-level orchestration.token-store.ts— interface + default in-memory impl.types.ts— config / token / scope types.
packages/smart/src/index.tspackages/smart/src/authorize.tspackages/smart/src/backend-services.tspackages/smart/src/discovery.tspackages/smart/src/pkce.tspackages/smart/src/scopes.tspackages/smart/src/smart-client.tspackages/smart/src/jwt.ts- All
*.test.tsinpackages/smart/src/
authorize.test.ts— URL construction.backend-services.test.ts— JWT assertion build.discovery.test.ts—.well-knownparse.pkce.test.ts— verifier/challenge shape.scopes.test.ts— scope parse.smart-client.test.ts— top-level orchestration.
Write tests in packages/smart/test/.
code_verifierconforms to RFC 7636 §4.1: length 43–128, unreserved chars only ([A-Z a-z 0-9 \- . _ ~]).code_challengeisBASE64URL(SHA256(verifier))— compute the expected value for a fixed verifier and assert byte-identical.code_challenge_methodisS256(neverplain).- Randomness: across 100 calls, no two verifiers are equal.
authorizeURL contains all required OAuth2 params:response_type=code,client_id,redirect_uri,scope,state,aud(SMART-specific),code_challenge,code_challenge_method=S256.stateis random per call and round-trip safe (base64url, no padding).audis the FHIR server base URL exactly as supplied (no trailing slash manipulation that would break the SMART spec).- When the config supplies
launch, it's appended as a scope component (launch/patient,launch/encounter).
/.well-known/smart-configurationis fetched (not/metadata), and a response missingauthorization_endpointortoken_endpointthrows a descriptive error.- A response advertising
code_challenge_methods_supportedwithoutS256is rejected. - A 404 response surfaces a
SmartAuthError(not a genericFhirRequestError).
- JWT header:
alg∈ {RS384,ES384} (per SMART v2),typ: "JWT",kidmatches the supplied key. Assertion is rejected ifalg: "none". - JWT payload claims:
iss=sub=client_id,aud= token endpoint URL,jtiunique per call,exp≤ 5 minutes fromiat. - Token exchange POST body contains
grant_type=client_credentials,client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer,client_assertion=<JWT>,scope=<supplied>. - Server returning
invalid_clientsurfaces asSmartAuthErrorwith the OAuth error code exposed.
- POST to
token_endpointwithgrant_type=authorization_code,code=<supplied>,redirect_uri,client_id,code_verifier. - Successful response is decoded into
{ access_token, id_token?, refresh_token?, scope, expires_in }and stored via theTokenStore. - Refresh path: using
refresh_tokenpostsgrant_type=refresh_token; rotated refresh tokens replace the stored one.
- v1:
patient/Observation.read,user/*.write,openid,profile,fhirUser,launch,launch/patient,offline_access— each parses to the right shape. - v2:
patient.Observation.rs,user.*.cruds,patient.Observation.rs?category=http://loinc.org|vital-signs— each parses and the search-param qualifier is retained. - Round-trip:
serialize(parse(s)) === sfor all valid inputs. - Invalid scope strings produce an error that names the offending token.
- The default in-memory store supports
get,set,clear, and is keyed per user/session as documented (check the source for the exact key discipline). setfollowed by an expiredexpleadsgetto returnundefined(if the implementation does TTL checks; if not, mark the test .skip with a note).
- SMART App Launch v2: https://hl7.org/fhir/smart-app-launch/STU2/. Sections: Scopes and Launch Context, Backend Services, App Launch.
- OAuth2 authorization code: RFC 6749 https://datatracker.ietf.org/doc/html/rfc6749.
- PKCE: RFC 7636 https://datatracker.ietf.org/doc/html/rfc7636.
- JWT (compact): RFC 7519 https://datatracker.ietf.org/doc/html/rfc7519.
- OAuth 2.0 Client Assertion: RFC 7523 https://datatracker.ietf.org/doc/html/rfc7523.
- OIDC discovery: https://openid.net/specs/openid-connect-discovery-1_0.html.
- SMART scopes v2 grammar — see the v2 spec section "SMART on FHIR Access Scopes" — required reading before writing the scope-parser tests.
- vitest
globals: true. Usevi.useFakeTimers()whenexp/iatare asserted. - Mock
fetchat the module boundary; no real HTTP. - Generate test keypairs with
jose(already a transitive dep if used byjwt.ts) inside the test — don't commit keys. - Scope parser tests should use table-driven assertions (
it.each([...])).
- Read source + existing tests.
- Organize new tests by area:
pkce.test.ts,authorize-url.test.ts,discovery.test.ts,backend-services-jwt.test.ts,token-exchange.test.ts,scopes-v2.test.ts,token-store.test.ts. - Gates:
pnpm test pnpm lint pnpm -r typecheck
- Every scenario above has ≥1 test.
- All three gates green.
- No network calls.
- No source changes.
- Adding support for new grant types.
- Testing arbitrary JWT algorithms beyond what the package implements.
- FHIR data access after the token exchange — that lives in
core.