Validate the purpose and audience of a token

An API accepts a correctly signed token intended for another service. Repair claim validation after the trusted signature verifier has finished.

Requires Node.js 22 or newer. No package installation or server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit token-policy.mjs, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same cases.mjs with the same fixtures; locally Node runs your code directly. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Read the wrong-audience case
Run the starter. A valid signature alone does not tell this API whether it is the intended recipient.

2. Bind issuer, audience and purpose
Require the exact issuer and access-token purpose. aud may be a string or an array of strings; require invoices-api as a full member, not a substring.

3. Check lifetime and subject
Check numeric time claims without coercion. exp equal to now is expired; a future nbf is not yet valid. Require a nonempty subject string.

4. Test valid alternatives
Both a valid string audience and an allowed audience in a list must work. Keep signature verification outside this claims function and before it.

SUPPLIED CONTRACT
acceptClaims(claims, now) returns a boolean. A trusted verifier already enforces signature and algorithm rules. This fixture’s API contract requires exact iss=https://id.example; aud as a string or all-string array containing the whole identifier invoices-api; its issuer-specific token_use=access; a string sub containing at least one non-whitespace character; integer exp greater than now; and optional integer nbf no greater than now. now is a trusted integer Unix time and allowed skew is zero.

REFERENCE
reference.mjs contains an explained solution: At now=1000, exp=1100 passes and exp=1000 is expired. nbf=1001 fails; nbf=1000 is permitted. invoices-api in an audience array passes, while invoices-api-admin does not. The string "1100" is malformed under this contract rather than coerced. Subject identity is otherwise preserved exactly; trim is used only to reject whitespace-only values, not to rewrite a subject identifier.

SCOPE
This exercise validates a deliberately strict claims profile only. token_use is a provider-specific convention here; integer-only NumericDate values and zero skew are fixture decisions, not requirements for every JWT deployment. It does not parse or verify JWTs, implement OAuth/OIDC, select signing keys or model revocation. A real integration uses the issuer’s documented access-token profile and a maintained protocol library before applying its application-specific checks.

NEXT TASK
Can your API accidentally accept an ID token or a token minted for an adjacent service?

SOURCE
https://www.rfc-editor.org/rfc/rfc8725.html
