Repository navigation
Validate Open Badges against bundled schemas chosen by type, never credentialSchema - #81
Merged
Merged
Conversation
Adds the four credential schemas embedded in the Open Badges 3.0 specification appendix as TypeScript modules under `src/openbadges/schema/json-schema/`: AchievementCredential (E.2.1.2), AchievementCredentialv1p1 (E.2.1.34), EndorsementCredential (E.2.1.7) and EndorsementCredentialv1p1 (E.2.1.36). The VC 1.1-only `v1p1` schemas are published only in the spec, so the spec is the source for all four. Nothing uses them yet. - `scripts/refresh-ob-schemas.mjs` regenerates the modules by hand from the spec HTML, recording the source, fetch date and SHA-256. With `--fixtures` it also downloads the purl-published twins. Nothing in build, test or CI fetches. - Tests pin each module's `$id` and SHA-256, check each compiles with Ajv2020 without strict-mode output, and compare each with its purl twin (committed under `test/openbadges/schema-files/purl/`) after rewriting draft 2020-12 tuple syntax to 2019-09 form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… credentialSchema `obv3SchemaCheck` validated a credential against `credentialSchema[0].id` whenever one was present, before choosing by type, so an Open Badges credential naming a permissive schema of its own passed the check. It also fetched every schema it used. The check now validates against the vendored Open Badges 3.0 schema for the credential's type and VC version, and never reads `credentialSchema`: - OpenBadgeCredential: E.2.1.2 AchievementCredential (VC 2.0) or E.2.1.34 AchievementCredentialv1p1 (VC 1.1). - EndorsementCredential: E.2.1.7 EndorsementCredential (VC 2.0) or E.2.1.36 EndorsementCredentialv1p1 (VC 1.1). VC 1.1 credentials are no longer checked against the either-version "Any" schema. Each schema is compiled once per process with Ajv2020 (`strictTuples: false`), and nothing is fetched or cached, so the check no longer uses `fetchJson` or `cacheService`. - The check moves to `src/openbadges/schema/`; `src/suites/schema/`, its cached schema loader and the internal `schema.obv3` suite are removed. - Success and failure messages name the spec section of the schema used. - Tests cover each schema, the VC 1.1-only error set, a regression test for the `credentialSchema` bypass with zero HTTP requests, the upstream schema's rejection of `image.type: ['Image']`, and compiling once across verifiers. Specs that served a stand-in schema over a fake HTTP service now run against the real one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t the credential The Open Badges JSON Schema check no longer follows `credentialSchema`, so a declaration that disagrees with the credential would go unseen. A new non-fatal check, `schema.obv3.declared`, compares each declared entry with the schema the credential is validated against: - `OB_DECLARED_SCHEMA_MISMATCH` — a known Open Badges 3.0 schema URL for the other OB credential type, or one that does not accept the credential's VC version. - `OB_DECLARED_SCHEMA_NOT_CHECKED` — a URL that is not a known Open Badges 3.0 schema, even alongside one that fits. It is not fetched. Known URLs are the eight purl-published schemas (`json/` and `json-ld/`, strict and "Any") and the four `$id`s of the schemas embedded in the spec. A declared URL fits when it is for the credential's OB type and accepts its VC version. Each problem's `instance` points at the entry. The check succeeds when every entry fits and is skipped when nothing is declared; it never changes `verified`. `obv3DeclaredSchemaCheck` runs after `obv3SchemaCheck` in `openBadgesSchemaSuite` and `openBadgesSuite`, and is exported from `/openbadges`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and apply them to endorsements BREAKING: `openBadgesSuite`, `openBadgesSchemaSuite`, `obv3SchemaCheck` and `obv3DeclaredSchemaCheck` are no longer exported from `@digitalcredentials/verifier-core/openbadges`. Import them from the new `@digitalcredentials/verifier-core/openbadges/schema` entry point. Everything that loads AJV and the bundled Open Badges 3.0 schemas now sits behind its own subpath, so importing `/openbadges` for the semantic suite, recognizers, Zod schemas or problem types loads neither. That matters where bundlers do not tree-shake (Metro), and under a Content Security Policy that forbids `unsafe-eval`. `openBadgesSchemaSuite` and `openBadgesSuite` now apply to EndorsementCredential as well as OpenBadgeCredential, so an endorsement is validated against its schema. In `openBadgesSuite` the semantic checks skip an endorsement. `openBadgesSemanticSuite` still applies to OpenBadgeCredential only. - Tests: both subpaths resolve through `package.json#exports` and expose only their own symbols; `/openbadges` imports with every AJV module mocked to throw; endorsements pass E.2.1.7 through both schema suites with the semantic checks skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… subpath - README: the Open Badges section splits the two subpaths, adds an "Import from" and "Applies to" table, and replaces the fetch-and-cache paragraph with the bundled schemas, the `schema.obv3.declared` check, the CSP note, the `image.type: ['Image']` caveat and the maintainers' refresh script. Import examples use `/openbadges/schema` for `openBadgesSuite`; the migration section says so, and that `credentialSchema` is no longer followed. Schemas leave the list of URLs verification fetches. - CHANGELOG 2.0.0: the JSON Schema breaking change and the Open Badges and package `exports` entries describe the new subpath, endorsements and the declared-schema check. - `docs/architecture.md`: source and test trees, `scripts/`, the `openbadges/schema/` → `openbadges/` dependency direction, the new problem types, the AJV dependency row and the known gap. - `docs/api/verification-results.md`: `schema.obv3.declared` in the id listings and the subpath in the phase table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #80 — review and merge that first.
Why
The Open Badges JSON Schema check validated a credential against
credentialSchema[0].idwhenever one was present, before choosing by type, so an Open Badges credential naming a permissive schema of its own passed the check. It also fetched every schema it used, including URLs the issuer chose. 2.0.0 is unreleased, so this is the moment to fix both, and to move the export, which would be breaking later.What changed
Bundled schemas, no fetching. The four credential schemas in the Open Badges 3.0 spec appendix are vendored as TypeScript modules.
schema.obv3.jsonvalidates against the one for the credential's type and VC version:OpenBadgeCredential, VC 2.0OpenBadgeCredential, VC 1.1EndorsementCredential, VC 2.0EndorsementCredential, VC 1.1VC 1.1 is no longer checked against the either-version "Any" schema. Schemas compile once per process (Ajv2020,
strictTuples: false). The check no longer usesfetchJsonorcacheService, and the cached schema loader is removed.credentialSchemanever chooses the schema. A new non-fatal check,schema.obv3.declared, reports a declared schema that does not fit:OB_DECLARED_SCHEMA_MISMATCH(a known OB schema for the other type or VC version) orOB_DECLARED_SCHEMA_NOT_CHECKED(an unknown URL — not fetched). Neither check changesverified.BREAKING: new subpath
@digitalcredentials/verifier-core/openbadges/schemaexportsopenBadgesSuite,openBadgesSchemaSuite,obv3SchemaCheckandobv3DeclaredSchemaCheck./openbadgeskeeps the semantic suite, recognizers, Zod schemas and problem types, and no longer loads AJV (relevant for Metro and for CSPs withoutunsafe-eval).Endorsements now reach the schema suites; in
openBadgesSuitethe semantic checks skip them.openBadgesSemanticSuitestays OpenBadgeCredential-only.Refresh is hand-run:
node scripts/refresh-ob-schemas.mjs [--fixtures]. Nothing in build, test or CI fetches. Tests pin each schema's SHA-256 and compare it with the purl-published schema after normalising draft 2020-12 tuple syntax.Docs: README Open Badges section and migration note, CHANGELOG 2.0.0,
docs/architecture.md,docs/api/verification-results.md.Worth knowing
image.type: ['Image']. That is upstream schema behaviour; a test pins it and the README documents it.openBadgesSuitefrom/openbadges(e.g. transaction-service's suite resolver) change that import when they move to 2.0.Validation
pnpm run build: pass.pnpm run test(lint + 1032 Node tests, up from 1003): pass.pnpm exec tsc -p tsconfig.dev.json: the same 6 pre-existing errors as the base, none new./openbadges.npm pack --dry-run: shipsdist/openbadges/schema/**, no test fixtures or scripts.Deferred
credentialSchema" suite.🤖 Generated with Claude Code