openbadgeslib 4.6.0__tar.gz → 4.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/CONTRIBUTING.md +1 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/Changelog.txt +264 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/PKG-INFO +9 -8
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/README.md +2 -2
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_python_support.py +7 -6
- openbadgeslib-4.6.2/openbadgeslib/ob3/_context_validation.py +881 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/credential.py +20 -3
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/did.py +33 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/eudi.py +186 -10
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/ldp.py +317 -25
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/status.py +8 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/verifier.py +4 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/handler.py +7 -7
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/metadata.py +4 -4
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/offer.py +3 -3
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/util.py +1 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/verify.py +3 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/PKG-INFO +9 -8
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/SOURCES.txt +2 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/requires.txt +6 -5
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/pyproject.toml +83 -17
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_docs.py +25 -3
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_credential.py +16 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_did.py +32 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_eudi_sd_jwt.py +316 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_eudi_type_metadata.py +13 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_eudi_x5c.py +82 -2
- openbadgeslib-4.6.2/tests/test_ob3_ldp_context_validation.py +1729 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_ldp_ecdsa_sd.py +84 -10
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_python_support.py +1 -1
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/GOVERNANCE.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/LICENSE.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/MANIFEST.in +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/SECURITY.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/docs/README.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_jsonmodel.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_jws/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_jws/exceptions.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_jws/utils.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/_ob1_api.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/badge_model.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/baking.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/cli_common.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/config.ini.example +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/confparser.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/errors.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/issue.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/keys.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/logs.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/mail.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob1/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob1/badge.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob1/signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob1/verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob2/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob2/models.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob2/signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob2/verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/clr.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/clr_context_2.0.1.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/credentials-v2.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.1.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.2.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/ob-v3p0-context-3.0.3.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/ob-v3p0-extensions.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/security-data-integrity-v2.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/contexts/security-multikey-v1.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/publish.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/status_list.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/ob3/status_registry.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/__init__.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/codes.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/errors.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/formats.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/memory_store.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/nonce.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/reconcile.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/sqlite_store.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/oid4vci/store.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_cli.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_init.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_keygenerator.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_publish.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_status.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/openbadges_verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib/py.typed +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/dependency_links.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/entry_points.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/openbadgeslib.egg-info/top_level.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/setup.cfg +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/config1.ini +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/Dockerfile.ob3 +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/README.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/conftest.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/docker-compose.yml +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/relax-danubetech-checksums.awk +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conformance/test_official_validators.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/conftest.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/ob_schemas/README.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/ob_schemas/ob_v3p0_achievementcredential_schema.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/ob_schemas/ob_v3p0_profile_schema.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/ob_schemas/refresh.sh +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/README.md +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/credentials-examples-v2.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/doc-hash.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/key-pair.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/proof-config-hash.txt +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/fixtures/vc_di_eddsa/signed-credential.json +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/images/sample1.png +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/images/sample1.svg +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/images/userimage01.svg +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/ldp_helpers.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/logo Python Espan/314/203a.svg" +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/logo Python Espa/303/261a.svg" +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_b64url.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_baking.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_cli_json.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_cli_smoke.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_confparser.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_eddsa.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_errors.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_examples.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_issue.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_issue_credential.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_jsonmodel.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_jws.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_key_operation.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_keys.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_mail.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob1_badge_io.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob1_deprecation.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob1_signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob1_verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob2_cli.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob2_error_branches.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob2_models.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob2_signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob2_verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_clr.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_conformance_schema.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_contexts.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_did_doc.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_endorsement.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_eudi_oid4vci.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_ldp.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_ldp_cli.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_ldp_errors.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_ldp_sign.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_publish_cli.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_signer.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_signer_cli_ldp.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_status.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_status_list.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_status_registry.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_ob3_verifier.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_oid4vci_config.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_oid4vci_protocol.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_oid4vci_reservations.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_oid4vci_store.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_openbadges_cli.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_public_api.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_sign_ecc.pem +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_sign_rsa.pem +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_signer_batch.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_util.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_verifier_cli_coverage.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_verify.py +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_verify_ecc.pem +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/test_verify_rsa.pem +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/withoutxmlheader.svg +0 -0
- {openbadgeslib-4.6.0 → openbadgeslib-4.6.2}/tests/withxmlheader.svg +0 -0
|
@@ -85,7 +85,7 @@ policy is deliberately simple and predictable:
|
|
|
85
85
|
- A new CPython minor is **added** to the CI matrix and the classifiers once it
|
|
86
86
|
has a stable (non-pre) release, so downstreams can adopt it early.
|
|
87
87
|
- The current matrix is **3.10–3.15**, and `requires-python = ">=3.10"`.
|
|
88
|
-
Python **3.10 is deprecated** since 4.6.0 (EOL 2026-10-
|
|
88
|
+
Python **3.10 is deprecated** since 4.6.0 (EOL 2026-10-01) and is dropped in
|
|
89
89
|
5.0, which will require Python 3.11+
|
|
90
90
|
([#242](https://github.com/luisgf/openbadgeslib/issues/242)).
|
|
91
91
|
|
|
@@ -4,6 +4,270 @@ OpenBadgesLib - Changelog
|
|
|
4
4
|
Newest first. Dates are ISO 8601 (YYYY-MM-DD).
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
* v4.6.2 - 2026-10-16
|
|
8
|
+
|
|
9
|
+
- security(deps): the openvc-core floor of `[eudi]`, `[ldp-sd]` and
|
|
10
|
+
`[oid4vci]` rises to 1.35.0. Two behaviours below it reach this
|
|
11
|
+
library's public API. An SD-JWT VC whose issuer JWT carries `aud`
|
|
12
|
+
verified for any relying party, so a badge its issuer restricted to one
|
|
13
|
+
verifier was honoured by all (RFC 7519 §4.1.3); 1.35.0 enforces it, and
|
|
14
|
+
`verify_badge_sd_jwt(audience=…)` now also gates it. And
|
|
15
|
+
`status_list_token_resolver(leeway_s=…)` took any number, so an infinite
|
|
16
|
+
leeway made an expired status-list token verify as current; 1.35.0
|
|
17
|
+
accepts whole seconds from 0 to 86400 only. Neither is an advisory in
|
|
18
|
+
openvc-core. The run-time guard of the ecdsa-sd-2023 verify path stays at
|
|
19
|
+
1.33.2, the security minimum of that path. `[ldp]` and `[ldp-sd]` also cap
|
|
20
|
+
pyld below 4: pyld 4.0 (unreleased, breaking) drops statements from the
|
|
21
|
+
signed dataset that 3.x keeps, an IRI with two `#` or a literal with an
|
|
22
|
+
invalid `@language`, which context validation does not refuse, so the
|
|
23
|
+
credential would still show what the proof no longer covers.
|
|
24
|
+
|
|
25
|
+
- security(eudi): `verify_badge_sd_jwt` enforces the badge's own
|
|
26
|
+
`validFrom` and `validUntil` on both trust paths (pinned key and X.509
|
|
27
|
+
trust anchors), and `issue_badge_sd_jwt` writes the credential's
|
|
28
|
+
`validUntil` as the issuer JWT's `exp`, and a `validFrom` still ahead as
|
|
29
|
+
`nbf`. A badge is a flat IETF SD-JWT VC, and openvc-core reads
|
|
30
|
+
`validFrom` / `validUntil` as a validity window only for a W3C VCDM
|
|
31
|
+
payload, so a badge past its `validUntil` verified as unexpired unless
|
|
32
|
+
`expires_in_s` happened to be passed; badges issued by other
|
|
33
|
+
implementations with only a `validUntil` were affected the same way. The
|
|
34
|
+
earlier of `validUntil` and `expires_in_s` wins, a window value that is
|
|
35
|
+
not an ISO 8601 timestamp with an offset (as `datetime.fromisoformat`
|
|
36
|
+
reads it, which is stricter before Python 3.11) fails closed, and the
|
|
37
|
+
leeway is the 60 seconds the other OB 3.0 verifiers use.
|
|
38
|
+
`issue_badge_sd_jwt` now refuses, with `EudiError`, to make `name`,
|
|
39
|
+
`achievement`, `validFrom` or `validUntil` selectively disclosable: a
|
|
40
|
+
holder could withhold the disclosure and present an expired badge as one
|
|
41
|
+
that never expires, and the stripped presentation still verified. Only
|
|
42
|
+
the recipient (`credentialSubject`) is meant to be withheld. A bare string
|
|
43
|
+
for `disclosable=` is refused too (it was read one character at a time,
|
|
44
|
+
which left the recipient in the clear), and `expires_in_s` must be a
|
|
45
|
+
number within about 317 years of now. Nothing can detect a withheld
|
|
46
|
+
disclosure on a badge that already exists, and an existing badge has no
|
|
47
|
+
`exp`/`nbf` for other verifiers: re-issue badges issued with a
|
|
48
|
+
`disclosable=` that named one of those claims, and any badge that must
|
|
49
|
+
stop verifying at its `validUntil` everywhere. The Type Metadata document
|
|
50
|
+
is unchanged, byte for byte (a change would break the `vct#integrity` pin
|
|
51
|
+
of every badge already issued), and a test now pins its hash.
|
|
52
|
+
With `x5c_trust_anchors`, `verify_badge_sd_jwt` no longer accepts a plain
|
|
53
|
+
JWT (a token with no `~`): openvc-core's `verify_credential` takes one
|
|
54
|
+
and then ignored `expected_vct` and `require_key_binding` and returned
|
|
55
|
+
another type, so a JWT signed by an anchored issuer passed as a badge;
|
|
56
|
+
the pinned-key path always refused it. `status_list_token_resolver`
|
|
57
|
+
validates `leeway_s` when it is built, so a bad value fails there rather
|
|
58
|
+
than on the first status check of a later badge. A hostile issuer JWT
|
|
59
|
+
header nested deeper than the JSON parser recurses now fails as
|
|
60
|
+
`EudiError` instead of escaping `verify_badge_sd_jwt(x5c_trust_anchors=…)`
|
|
61
|
+
as a bare `RecursionError`, and a window value too long to be a timestamp
|
|
62
|
+
is not echoed back in the error.
|
|
63
|
+
|
|
64
|
+
- fix(did): a `did:web` document nested deeper than the JSON parser
|
|
65
|
+
recurses, or deeper than 64 levels (a DID document is a handful), is
|
|
66
|
+
reported as the usual `OB3VerificationError` instead of escaping
|
|
67
|
+
`resolve_did`, `fetch_did_web_document` and the verifiers that resolve an
|
|
68
|
+
issuer's DID as a bare `RecursionError`. CPython 3.14 and later parse
|
|
69
|
+
nesting far past the recursion limit, so the parser alone no longer bounds
|
|
70
|
+
what the later `repr()` and `json.dumps` of the document survive.
|
|
71
|
+
|
|
72
|
+
* v4.6.1 - 2026-10-14
|
|
73
|
+
|
|
74
|
+
- security(ldp): `OB3LdpVerifier` reads only JSON its Data Integrity proof
|
|
75
|
+
actually covers (GHSA-rp5q-966c-5592; affects 3.2.0–4.6.0, the native
|
|
76
|
+
eddsa-rdfc-2022 path). The proof signs the RDF dataset the credential
|
|
77
|
+
expands to, while the returned credential and every policy check
|
|
78
|
+
(validity window, `credentialStatus`, recipient, issuer binding,
|
|
79
|
+
`proof.expires`) read the JSON, and nothing tied the two together. Many
|
|
80
|
+
JSON documents expand to the same dataset, so an expired or revoked
|
|
81
|
+
badge, or one showing forged content, verified with its genuine
|
|
82
|
+
signature, including on the DID-resolved, issuer-bound path. `verify`
|
|
83
|
+
now applies VC Data Integrity §2.4.1 context validation before it builds
|
|
84
|
+
the credential or applies any policy, for both cryptosuites and ahead of
|
|
85
|
+
the ecdsa-sd-2023 delegation: `@context` starts with the VC 2.0 URL and
|
|
86
|
+
names only bundled contexts, each context document once and at most
|
|
87
|
+
eight entries in all, so a padded `@context` cannot buy seconds of
|
|
88
|
+
JSON-LD processing before any key is resolved, and no inline context
|
|
89
|
+
object, not even a bare `@vocab` (OB 3.0 credentials name only
|
|
90
|
+
published contexts; a holder-chosen vocabulary would only add spellings
|
|
91
|
+
of signed members to refuse). Those bounds hold for every `@context` the
|
|
92
|
+
JSON-LD processor applies and are checked before it runs: below the top
|
|
93
|
+
level only an embedded endorsement credential may carry one, held to
|
|
94
|
+
the same rules, and any other is refused, even inside an `@json`
|
|
95
|
+
literal such as `_sd`; and since each embedded credential is validated
|
|
96
|
+
again on its own, at most 64 may be embedded, 8 deep, with at most
|
|
97
|
+
256 KiB of their JSON in all (a nested one counted once per credential
|
|
98
|
+
holding it). The document and its proof configuration must each be the
|
|
99
|
+
compacted form of their own expansion, with every member (and `type`
|
|
100
|
+
value) a bundled context defines spelled by its term; and nothing may
|
|
101
|
+
be dropped on the way to RDF ("safe mode"). That
|
|
102
|
+
refuses a member re-keyed to its IRI or aliased by an inline context, a
|
|
103
|
+
value its term cannot carry (an untyped `validUntil`/`validFrom` or
|
|
104
|
+
proof `expires`, or a literal `credentialStatus`: compaction itself
|
|
105
|
+
keeps such a value under the full IRI, out of sight of the policy,
|
|
106
|
+
which reads the term), `@included`, `@reverse`, `@index`, `@direction`, a
|
|
107
|
+
top-level `@graph`, a second description of the credential's own node,
|
|
108
|
+
an array nested inside an array (expansion flattens `[[S]]` into the
|
|
109
|
+
same signed dataset as `S`, while the status checker reads only objects,
|
|
110
|
+
so a wrapped `credentialStatus` skipped the revocation check), and any
|
|
111
|
+
term, type or node id that is undefined or not an absolute IRI. Every
|
|
112
|
+
`credentialStatus` entry must also be a JSON object: a bare status
|
|
113
|
+
reference, which no checker can read, is refused rather than treated as
|
|
114
|
+
no status. Embedded endorsement credentials are validated against their
|
|
115
|
+
own `@context`. `OB3LdpSigner.sign` checks what it produced and raises
|
|
116
|
+
`ErrorSigningFile` rather than emit a document its proof does not fully
|
|
117
|
+
cover (e.g. a recipient id without a scheme), or one whose
|
|
118
|
+
`sign_to_json` text exceeds the 256 KiB verifiers accept. Now refused
|
|
119
|
+
because part of them was never signed: credentials with an undefined
|
|
120
|
+
term or type, or a relative or malformed id (pyld >= 3.1 signed an
|
|
121
|
+
undefined type or a relative id as `http://example.org/base/...`),
|
|
122
|
+
including the shape openbadgeslib 3.6.0 to 3.14.0 issued, a
|
|
123
|
+
`credentialSchema` typed `1EdTechJsonSchemaValidator2019` without the
|
|
124
|
+
extensions context (#239): re-issue such badges. The size limit is still
|
|
125
|
+
applied once, to the document as received. The low-level
|
|
126
|
+
`verify_data_integrity_proof` / `add_data_integrity_proof` are unchanged
|
|
127
|
+
for eddsa-rdfc-2022 (CLR and the W3C vectors use them): they verify or
|
|
128
|
+
sign the RDF dataset only, so do not read validity, status, subject or
|
|
129
|
+
issuer from the JSON after `verify_data_integrity_proof` alone; verify
|
|
130
|
+
OB 3.0 credentials with `OB3LdpVerifier`.
|
|
131
|
+
|
|
132
|
+
- security(ldp): `OB3LdpVerifier` refuses a member the policy reads
|
|
133
|
+
spelled by its full IRI even where no bundled context defines a term
|
|
134
|
+
for it. OB 3.0 was first published on VCDM 1.1, so issuers signed
|
|
135
|
+
eddsa-rdfc-2022 badges carrying `issuanceDate`/`expirationDate` (and
|
|
136
|
+
perhaps a `StatusList2021Entry`); those are refused as issued, since the
|
|
137
|
+
VC 1.1 context is not bundled, but a holder could re-compact one onto
|
|
138
|
+
`[VC 2.0, OB 3.0]` with the dataset and the signature unchanged:
|
|
139
|
+
compaction then spelled those members by full IRI, and the model saw no
|
|
140
|
+
expiry, no not-before and a status entry type it did not know. Every IRI
|
|
141
|
+
the policy reads (the validity window of both VC data models,
|
|
142
|
+
`proof.expires`, `credentialStatus`, `credentialSchema`,
|
|
143
|
+
`credentialSubject`, `issuer`, and the Bitstring and StatusList2021
|
|
144
|
+
entry types and members) must now be spelled by the one key the policy
|
|
145
|
+
reads it under, from a fixed table rather than from the bundled
|
|
146
|
+
contexts, so an alias a future context gave one is refused too. VC 1.1
|
|
147
|
+
is still not bundled: such badges stay unverifiable; re-issue them on
|
|
148
|
+
VC 2.0.
|
|
149
|
+
|
|
150
|
+
- security(ldp): `OB3LdpVerifier` refuses a `credentialStatus` entry that
|
|
151
|
+
is also described outside `credentialStatus`. RDF lets a node referenced
|
|
152
|
+
from two properties be described under either, so when the issuer also
|
|
153
|
+
named its status entry from another property (`relatedResource`, a
|
|
154
|
+
nested node's or an embedded endorsement's own `credentialStatus`), a
|
|
155
|
+
holder could move a signed member such as `statusSize: 2` there with the
|
|
156
|
+
dataset and the signature unchanged, and the status check read the wrong
|
|
157
|
+
bit and reported a revoked badge as valid. A node directly under the
|
|
158
|
+
top-level `credentialStatus` whose id is described anywhere else in the
|
|
159
|
+
expanded document is now refused, unless that description is an exact
|
|
160
|
+
copy of the entry (as when an embedded endorsement repeats the outer
|
|
161
|
+
credential's `credentialStatus` verbatim). An issuer-signed credential
|
|
162
|
+
that describes its status entry under a second property with more than
|
|
163
|
+
its id is refused too: re-issue it naming the entry by id only.
|
|
164
|
+
|
|
165
|
+
- security(ldp): an ecdsa-sd-2023 presentation passes `OB3LdpVerifier`
|
|
166
|
+
only if every `validUntil` and `credentialStatus` statement it discloses
|
|
167
|
+
is mandatory. The #328 check saw only that the two keys were there, so
|
|
168
|
+
with status left selective a holder could disclose one status entry of
|
|
169
|
+
two (the clear one), or an entry without its `statusSize`, and pass the
|
|
170
|
+
status check. A verifier cannot count what a holder withheld, only rely
|
|
171
|
+
on the mandatory statements the issuer's base signature covers as a
|
|
172
|
+
whole, so issuers must list `/validUntil` and `/credentialStatus` as
|
|
173
|
+
mandatory pointers; presentations of credentials that left them
|
|
174
|
+
selective are now refused even when fully disclosed. The limit no
|
|
175
|
+
verifier can lift: what an issuer leaves selective stays hideable, so a
|
|
176
|
+
mandatory `/credentialStatus/0` alone leaves `/credentialStatus/1`
|
|
177
|
+
hideable; mark `/credentialStatus` whole. The check indexes the
|
|
178
|
+
disclosed statements with openvc-core's own ecdsa-sd-2023 helpers and
|
|
179
|
+
fails closed if they are missing. `verify_data_integrity_proof` still
|
|
180
|
+
checks the proof alone.
|
|
181
|
+
- `validFrom` is held to the same rule as `validUntil` and
|
|
182
|
+
`credentialStatus`: a presentation that omits it, or whose issuer
|
|
183
|
+
left `/validFrom` selective, is refused. Otherwise the holder of a
|
|
184
|
+
badge that is not valid yet could withhold its `validFrom`, which the
|
|
185
|
+
model then took as "now", and present it as valid. Issuers list
|
|
186
|
+
`/validFrom`, `/validUntil` and `/credentialStatus` as mandatory
|
|
187
|
+
pointers.
|
|
188
|
+
|
|
189
|
+
- fix(ob3): a `credentialStatus` entry whose `statusPurpose` or `type`,
|
|
190
|
+
or a credential whose `type`, is not a string (a holder can respell a
|
|
191
|
+
signed `"revocation"` as `["revocation"]` in a Data Integrity credential
|
|
192
|
+
without changing what is signed; an issuer can sign a language-tagged
|
|
193
|
+
value) fails with `OB3VerificationError` instead of a bare `TypeError`,
|
|
194
|
+
and before the status list is fetched.
|
|
195
|
+
|
|
196
|
+
- security(deps): raise the `[ldp]` pyld floor to `>=3.3.0`, the release
|
|
197
|
+
the suite is tested against, and declare the same floor in `[ldp-sd]`
|
|
198
|
+
(openvc-core's own `[data-integrity]` extra admits pyld 2.x, and
|
|
199
|
+
`OB3LdpVerifier` validates with pyld before it delegates). Context
|
|
200
|
+
validation compares the JSON with the compacted form pyld derives, and
|
|
201
|
+
pyld 2.x compacts some terms differently (an `Address.geo` comes back as
|
|
202
|
+
`GeoCoordinates`), so genuine credentials such as 1EdTech's "Complete
|
|
203
|
+
OpenBadgeCredential" example would be refused. pyld before 3.3 also
|
|
204
|
+
canonicalizes an integral float (`3.0`) as `"3.0E0"`, where 3.3 and
|
|
205
|
+
jsonld.js give `"3"`, so a Data Integrity proof made on one did not
|
|
206
|
+
verify on the other.
|
|
207
|
+
|
|
208
|
+
- security(deps): raise the openvc-core floors to `>=1.33.2` in the
|
|
209
|
+
`[eudi]`, `[ldp-sd]` and `[oid4vci]` extras, the release the suite is
|
|
210
|
+
tested against. openvc-core 1.30.2 closes the same JSON-vs-dataset gap
|
|
211
|
+
in the ecdsa-sd-2023 suite `[ldp-sd]` delegates to
|
|
212
|
+
(GHSA-vrxg-57v4-85mc), and 1.33.2 its follow-ups (GHSA-h9rq-wxcw-4rjc:
|
|
213
|
+
nested arrays, re-spelled policy terms, a derived proof that merges
|
|
214
|
+
blank nodes). The ecdsa-sd-2023 verify (`OB3LdpVerifier`
|
|
215
|
+
and `verify_data_integrity_proof` alike) now also refuses, at run time,
|
|
216
|
+
an installed openvc-core older than 1.33.2 or with no readable version
|
|
217
|
+
(`OB3VerificationError`), so a stale environment fails closed. Behaviour
|
|
218
|
+
the new floor brings: `verify_badge_sd_jwt` on a presentation carrying a
|
|
219
|
+
Key Binding JWT, called with only `audience` or only `nonce` (or one of
|
|
220
|
+
them empty), raises `EudiError` instead of reporting `key_bound=True`
|
|
221
|
+
for a KB-JWT never bound to this verifier (openvc-core 1.30.0); and an
|
|
222
|
+
ecdsa-sd-2023 document with an undefined term or an inline `@context`
|
|
223
|
+
that defines terms is rejected. CI's `eudi` job now runs the `[eudi]`
|
|
224
|
+
and `[ldp-sd]` suites at that exact floor and fails if the extras'
|
|
225
|
+
floors disagree.
|
|
226
|
+
|
|
227
|
+
- security(deps): raise the PyJWT[crypto] floor to `>=2.15.1`. The OB3
|
|
228
|
+
JWT-VC verifier hands an untrusted token to
|
|
229
|
+
`jwt.get_unverified_header()`, to an unverified `jwt.decode()` peek that
|
|
230
|
+
reads the issuer before its key is resolved, and to the
|
|
231
|
+
signature-verifying `jwt.decode()`, so these PyJWT advisories are on a
|
|
232
|
+
reachable path: a deeply nested header (2.14.0, GHSA-8wjv-2p76-3863,
|
|
233
|
+
which affects the old 2.13 floor) or payload (2.15.0,
|
|
234
|
+
GHSA-42vr-xj54-vc7v, through the unverified peek) escaped as a bare
|
|
235
|
+
`RecursionError` instead of an `OB3VerificationError`; and characters
|
|
236
|
+
outside the base64url alphabet were accepted in a segment, so one
|
|
237
|
+
signature verified under many spellings of a token (2.14.0,
|
|
238
|
+
GHSA-hxm8-2xgr-2p9m; its revocation bypass needs a consumer that indexes
|
|
239
|
+
revocation by token text, which openbadgeslib does not do). 2.15.1
|
|
240
|
+
rather than 2.15.0 because 2.14.0 and 2.15.0 also refused canonical `=`
|
|
241
|
+
padding (PyJWT #1209). The HMAC key-confusion, options-dict and
|
|
242
|
+
`PyJWKClient` advisories are not reachable: no HS* algorithm is ever
|
|
243
|
+
allowed, every call passes a fresh options dict, and `PyJWKClient` is
|
|
244
|
+
not used. `environment.yml`, the README and the Installation page
|
|
245
|
+
follow, and a docs test now pins their PyJWT floor to `pyproject.toml`.
|
|
246
|
+
|
|
247
|
+
- fix: the Python 3.10 deprecation gives the real CPython end-of-life
|
|
248
|
+
date, 2026-10-01 (PEP 619); v4.6.0 said 2026-10-31. The
|
|
249
|
+
`DeprecationWarning` now reads "(CPython end of life: 2026-10-01)", and
|
|
250
|
+
the README, Installation, Upgrading and contributing docs follow. Its
|
|
251
|
+
prefix is unchanged, so the documented
|
|
252
|
+
`ignore:Python 3.10 is deprecated in openbadgeslib:DeprecationWarning`
|
|
253
|
+
filter still matches, and 3.10 stays supported through 4.x.
|
|
254
|
+
|
|
255
|
+
- fix(ob3): integral `creditsAvailable` / `creditsEarned` values are
|
|
256
|
+
emitted as JSON integers (`3`, not `3.0`). Both are `xsd:float` terms,
|
|
257
|
+
and JSON-LD gives an integral number the integer lexical form (`"3"`),
|
|
258
|
+
which jsonld.js and pyld 3.3 sign; pyld before 3.3 signed the float
|
|
259
|
+
`3.0` as `"3.0E0"`, so a Data Integrity badge issued with integral float
|
|
260
|
+
credits verified only on the pyld version it was signed with. New badges
|
|
261
|
+
now canonicalize the same everywhere. Data Integrity badges already
|
|
262
|
+
issued with integral float credits on pyld <= 3.1 do not verify on pyld
|
|
263
|
+
3.3 or jsonld.js-based verifiers; re-issue them. The model still reads
|
|
264
|
+
the values back as floats.
|
|
265
|
+
|
|
266
|
+
- fix(ldp): a deeply nested Data Integrity document fails with
|
|
267
|
+
`OB3VerificationError` instead of escaping `OB3LdpVerifier.verify` (and
|
|
268
|
+
the issuer-DID peek in `verify_badge`) as a bare `RecursionError`.
|
|
269
|
+
|
|
270
|
+
|
|
7
271
|
* v4.6.0 - 2026-10-13
|
|
8
272
|
|
|
9
273
|
- feat: Python 3.15 is officially supported (#243). It joins the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openbadgeslib
|
|
3
|
-
Version: 4.6.
|
|
3
|
+
Version: 4.6.2
|
|
4
4
|
Summary: Production-ready Python library & CLI for Open Badges 3.0 (W3C Verifiable Credentials): issue, verify, and revoke JWT-VC credentials with Bitstring Status Lists and did:web — plus strict OB 2.0 and legacy OB 1.0.
|
|
5
5
|
Author-email: Luis González Fernández <luisgf@luisgf.es>, Jesús Cea Avión <jcea@jcea.es>
|
|
6
6
|
License: LGPLv3
|
|
@@ -31,17 +31,18 @@ Requires-Python: >=3.10
|
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
License-File: LICENSE.txt
|
|
33
33
|
Requires-Dist: pypng>=0.20220715.0
|
|
34
|
-
Requires-Dist: PyJWT[crypto]>=2.
|
|
34
|
+
Requires-Dist: PyJWT[crypto]>=2.15.1
|
|
35
35
|
Requires-Dist: cryptography>=45
|
|
36
36
|
Requires-Dist: defusedxml>=0.7
|
|
37
37
|
Provides-Extra: ldp
|
|
38
|
-
Requires-Dist: pyld
|
|
38
|
+
Requires-Dist: pyld<4,>=3.3.0; extra == "ldp"
|
|
39
39
|
Provides-Extra: eudi
|
|
40
|
-
Requires-Dist: openvc-core<2,>=1.
|
|
40
|
+
Requires-Dist: openvc-core<2,>=1.35.0; extra == "eudi"
|
|
41
41
|
Provides-Extra: ldp-sd
|
|
42
|
-
Requires-Dist: openvc-core[data-integrity]<2,>=1.
|
|
42
|
+
Requires-Dist: openvc-core[data-integrity]<2,>=1.35.0; extra == "ldp-sd"
|
|
43
|
+
Requires-Dist: pyld<4,>=3.3.0; extra == "ldp-sd"
|
|
43
44
|
Provides-Extra: oid4vci
|
|
44
|
-
Requires-Dist: openvc-core<2,>=1.
|
|
45
|
+
Requires-Dist: openvc-core<2,>=1.35.0; extra == "oid4vci"
|
|
45
46
|
Provides-Extra: dev
|
|
46
47
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
47
48
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
@@ -69,7 +70,7 @@ with W3C Bitstring Status Lists and `did:web`. It also ships strict
|
|
|
69
70
|
**OpenBadges 2.0** (JWS / hosted assertions) and a frozen **OpenBadges 1.0**
|
|
70
71
|
legacy format, selected with `-V {1,2,3}` (default `3`).
|
|
71
72
|
|
|
72
|
-
> **4.0.0 is released** (2026-07-22). It groups the breaking changes so upgrades stay rare: every CLI unifies on one `0/1/2` exit-code contract (a valid-but-untrusted badge now exits `2`, and the 255s are gone), and the legacy pycryptodome/python-ecdsa compat shim is removed — a live key **object** from either is no longer accepted, though key *files* are plain PEM and round-trip unchanged. OpenBadges 1.0 and the `openbadges-<command>` scripts stay, and a new unified `openbadges <command>` front-end joins them. **Python 3.10 stays supported throughout 4.x** (`requires-python >= 3.10`; CI tests 3.10–3.15) but is deprecated since 4.6.0
|
|
73
|
+
> **4.0.0 is released** (2026-07-22). It groups the breaking changes so upgrades stay rare: every CLI unifies on one `0/1/2` exit-code contract (a valid-but-untrusted badge now exits `2`, and the 255s are gone), and the legacy pycryptodome/python-ecdsa compat shim is removed — a live key **object** from either is no longer accepted, though key *files* are plain PEM and round-trip unchanged. OpenBadges 1.0 and the `openbadges-<command>` scripts stay, and a new unified `openbadges <command>` front-end joins them. **Python 3.10 stays supported throughout 4.x** (`requires-python >= 3.10`; CI tests 3.10–3.15) but is deprecated since 4.6.0 (it reached end of life on 2026-10-01); 5.0 drops it and requires Python 3.11+. See **[Upgrading to 4.0](https://github.com/luisgf/openbadgeslib/wiki/Upgrading-to-4.0)** before pinning a major.
|
|
73
74
|
|
|
74
75
|
## Features
|
|
75
76
|
|
|
@@ -147,7 +148,7 @@ all three OB versions.
|
|
|
147
148
|
openbadgeslib 5.0 will require Python 3.11 or newer
|
|
148
149
|
([#242](https://github.com/luisgf/openbadgeslib/issues/242)).
|
|
149
150
|
- [pypng](https://pypi.org/project/pypng/) >= 0.20220715.0
|
|
150
|
-
- [PyJWT[crypto]](https://pypi.org/project/PyJWT/) >= 2.
|
|
151
|
+
- [PyJWT[crypto]](https://pypi.org/project/PyJWT/) >= 2.15.1
|
|
151
152
|
- [cryptography](https://pypi.org/project/cryptography/) >= 45
|
|
152
153
|
- [defusedxml](https://pypi.org/project/defusedxml/) >= 0.7
|
|
153
154
|
|
|
@@ -13,7 +13,7 @@ with W3C Bitstring Status Lists and `did:web`. It also ships strict
|
|
|
13
13
|
**OpenBadges 2.0** (JWS / hosted assertions) and a frozen **OpenBadges 1.0**
|
|
14
14
|
legacy format, selected with `-V {1,2,3}` (default `3`).
|
|
15
15
|
|
|
16
|
-
> **4.0.0 is released** (2026-07-22). It groups the breaking changes so upgrades stay rare: every CLI unifies on one `0/1/2` exit-code contract (a valid-but-untrusted badge now exits `2`, and the 255s are gone), and the legacy pycryptodome/python-ecdsa compat shim is removed — a live key **object** from either is no longer accepted, though key *files* are plain PEM and round-trip unchanged. OpenBadges 1.0 and the `openbadges-<command>` scripts stay, and a new unified `openbadges <command>` front-end joins them. **Python 3.10 stays supported throughout 4.x** (`requires-python >= 3.10`; CI tests 3.10–3.15) but is deprecated since 4.6.0
|
|
16
|
+
> **4.0.0 is released** (2026-07-22). It groups the breaking changes so upgrades stay rare: every CLI unifies on one `0/1/2` exit-code contract (a valid-but-untrusted badge now exits `2`, and the 255s are gone), and the legacy pycryptodome/python-ecdsa compat shim is removed — a live key **object** from either is no longer accepted, though key *files* are plain PEM and round-trip unchanged. OpenBadges 1.0 and the `openbadges-<command>` scripts stay, and a new unified `openbadges <command>` front-end joins them. **Python 3.10 stays supported throughout 4.x** (`requires-python >= 3.10`; CI tests 3.10–3.15) but is deprecated since 4.6.0 (it reached end of life on 2026-10-01); 5.0 drops it and requires Python 3.11+. See **[Upgrading to 4.0](https://github.com/luisgf/openbadgeslib/wiki/Upgrading-to-4.0)** before pinning a major.
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
@@ -91,7 +91,7 @@ all three OB versions.
|
|
|
91
91
|
openbadgeslib 5.0 will require Python 3.11 or newer
|
|
92
92
|
([#242](https://github.com/luisgf/openbadgeslib/issues/242)).
|
|
93
93
|
- [pypng](https://pypi.org/project/pypng/) >= 0.20220715.0
|
|
94
|
-
- [PyJWT[crypto]](https://pypi.org/project/PyJWT/) >= 2.
|
|
94
|
+
- [PyJWT[crypto]](https://pypi.org/project/PyJWT/) >= 2.15.1
|
|
95
95
|
- [cryptography](https://pypi.org/project/cryptography/) >= 45
|
|
96
96
|
- [defusedxml](https://pypi.org/project/defusedxml/) >= 0.7
|
|
97
97
|
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
# The runtime side of the "Supported Python versions" policy (CONTRIBUTING.md):
|
|
23
|
-
# a CPython that is still supported but
|
|
24
|
-
# deprecated — importing openbadgeslib on it emits a
|
|
25
|
-
# is dropped only at the next major release.
|
|
23
|
+
# a CPython that is still supported but past (or in its final months before)
|
|
24
|
+
# end of life is deprecated — importing openbadgeslib on it emits a
|
|
25
|
+
# DeprecationWarning — and is dropped only at the next major release.
|
|
26
26
|
#
|
|
27
|
-
# Python 3.10
|
|
28
|
-
#
|
|
27
|
+
# Python 3.10 reached CPython end of life on 2026-10-01 (PEP 619); it is
|
|
28
|
+
# deprecated since 4.6.0 and dropped in 5.0 (#242). That drop raises
|
|
29
|
+
# requires-python, the mypy python_version and DEPRECATED_BELOW together,
|
|
29
30
|
# rewording DEPRECATION_MESSAGE for the next deprecated version — or deletes
|
|
30
31
|
# this module (and its call in __init__) if none is pending removal.
|
|
31
32
|
|
|
@@ -38,7 +39,7 @@ DEPRECATED_BELOW = (3, 11)
|
|
|
38
39
|
|
|
39
40
|
DEPRECATION_MESSAGE = (
|
|
40
41
|
'Python 3.10 is deprecated in openbadgeslib (CPython end of life: '
|
|
41
|
-
'2026-10-
|
|
42
|
+
'2026-10-01); openbadgeslib 5.0 will require Python 3.11 or newer.')
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
def warn_if_deprecated(version_info: Tuple[object, ...] = sys.version_info) -> None:
|