@blamejs/blamejs-shop 0.5.12 → 0.5.13
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.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* b.auth.ciba.client — canonical per-primitive coverage for the CIBA
|
|
6
|
+
* (OpenID Connect Client-Initiated Backchannel Authentication) RP client:
|
|
7
|
+
* create()-time option validation, startAuthentication's backchannel POST +
|
|
8
|
+
* body assembly, pollToken's grant_type=ciba loop (authorization_pending /
|
|
9
|
+
* slow_down back-off / terminal errors), and parseNotification's ping/push
|
|
10
|
+
* callback authentication.
|
|
11
|
+
*
|
|
12
|
+
* Every flow drives the REAL consumer path against a localhost OIDC stub
|
|
13
|
+
* (discovery + JWKS + scriptable /bc-auth + /token) — never the network, no
|
|
14
|
+
* NODE_ENV bypass. The stub's key pair signs ES256 id_tokens so the composed
|
|
15
|
+
* inner OAuth client's id_token verification runs end-to-end.
|
|
16
|
+
*
|
|
17
|
+
* The auth_req_id-binding substitution defense (id_token bound to the flow's
|
|
18
|
+
* auth_req_id) has its own file (ciba-authreqid-binding.test.js); this file
|
|
19
|
+
* targets the error / adversarial / option-default / fail-closed branches.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
var http = require("http");
|
|
23
|
+
var nodeCrypto = require("crypto");
|
|
24
|
+
|
|
25
|
+
var helpers = require("../helpers");
|
|
26
|
+
var b = helpers.b;
|
|
27
|
+
var check = helpers.check;
|
|
28
|
+
|
|
29
|
+
var CLIENT_ID = "rp-ciba-cov";
|
|
30
|
+
var KID = "cov-es256";
|
|
31
|
+
// >= 32 chars — clears the CIBA §7.1.2 client_notification_token min-entropy guard.
|
|
32
|
+
var CNT = "client-notification-token-opaque-and-long-enough-for-ciba-min-entropy-guard";
|
|
33
|
+
var CLIENT_SECRET = "ciba-cov-client-secret-opaque-and-long-enough-for-basic-auth-padding-xxxxxxxx";
|
|
34
|
+
|
|
35
|
+
// The trusted signing key — its public JWK is served at /jwks so the inner
|
|
36
|
+
// OAuth client's verifyIdToken accepts tokens minted with `_signer`.
|
|
37
|
+
var _signer = _makeKeyPair(KID);
|
|
38
|
+
// A rogue key never published to /jwks — id_tokens it signs fail verification
|
|
39
|
+
// (drives the fail-closed auth-ciba/id-token-invalid branch).
|
|
40
|
+
var _rogue = _makeKeyPair("rogue-" + KID);
|
|
41
|
+
|
|
42
|
+
function _makeKeyPair(kid) {
|
|
43
|
+
var kp = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
44
|
+
var jwk = kp.publicKey.export({ format: "jwk" });
|
|
45
|
+
jwk.kid = kid; jwk.alg = "ES256"; jwk.use = "sig";
|
|
46
|
+
return { kp: kp, jwk: jwk };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function _b64url(x) { return Buffer.from(x).toString("base64url"); }
|
|
50
|
+
|
|
51
|
+
// Mint an ES256 id_token. `authReqIdClaim` (when defined) is written as the
|
|
52
|
+
// urn:openid:params:jwt:claim:auth_req_id binding claim. `signer` defaults to
|
|
53
|
+
// the trusted key; pass `_rogue` for a signature the JWKS can't verify.
|
|
54
|
+
function _mintIdToken(issuer, authReqIdClaim, signer) {
|
|
55
|
+
signer = signer || _signer;
|
|
56
|
+
var header = { alg: "ES256", kid: signer.jwk.kid, typ: "JWT" };
|
|
57
|
+
var now = Math.floor(Date.now() / 1000);
|
|
58
|
+
var payload = { iss: issuer, aud: CLIENT_ID, sub: "user-cov", iat: now, exp: now + 3600 };
|
|
59
|
+
if (authReqIdClaim !== undefined) payload["urn:openid:params:jwt:claim:auth_req_id"] = authReqIdClaim;
|
|
60
|
+
var input = _b64url(JSON.stringify(header)) + "." + _b64url(JSON.stringify(payload));
|
|
61
|
+
var sig = nodeCrypto.sign("sha256", Buffer.from(input),
|
|
62
|
+
{ key: signer.kp.privateKey, dsaEncoding: "ieee-p1363" });
|
|
63
|
+
return input + "." + _b64url(sig);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Localhost OIDC stub. `holder` scripts the per-request behavior:
|
|
67
|
+
// holder.discoveryOmit — "backchannel" | "token" drops that field from the
|
|
68
|
+
// discovery doc (drives ciba's no-*-endpoint refusals).
|
|
69
|
+
// holder.onBcAuth(body, res) / holder.onToken(body, res) — script /bc-auth
|
|
70
|
+
// and /token responses. Unset → sensible defaults.
|
|
71
|
+
// holder.bcAuthBodies / holder.tokenBodies — captured request bodies.
|
|
72
|
+
async function _withIdp(fn) {
|
|
73
|
+
var holder = {
|
|
74
|
+
issuer: null,
|
|
75
|
+
discoveryOmit: null,
|
|
76
|
+
onBcAuth: null,
|
|
77
|
+
onToken: null,
|
|
78
|
+
bcAuthBodies: [],
|
|
79
|
+
tokenBodies: [],
|
|
80
|
+
};
|
|
81
|
+
function _json(res, status, obj) {
|
|
82
|
+
var s = JSON.stringify(obj);
|
|
83
|
+
res.writeHead(status, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
84
|
+
res.end(s);
|
|
85
|
+
}
|
|
86
|
+
function _readBody(req, cb) {
|
|
87
|
+
var chunks = [];
|
|
88
|
+
req.on("data", function (c) { chunks.push(c); });
|
|
89
|
+
req.on("end", function () { cb(Buffer.concat(chunks).toString("utf8")); });
|
|
90
|
+
}
|
|
91
|
+
var server = http.createServer(function (req, res) {
|
|
92
|
+
var u = new URL(req.url, "http://localhost");
|
|
93
|
+
if (u.pathname === "/.well-known/openid-configuration") {
|
|
94
|
+
var doc = {
|
|
95
|
+
issuer: holder.issuer,
|
|
96
|
+
authorization_endpoint: holder.issuer + "/auth",
|
|
97
|
+
token_endpoint: holder.issuer + "/token",
|
|
98
|
+
backchannel_authentication_endpoint: holder.issuer + "/bc-auth",
|
|
99
|
+
jwks_uri: holder.issuer + "/jwks",
|
|
100
|
+
};
|
|
101
|
+
if (holder.discoveryOmit === "backchannel") delete doc.backchannel_authentication_endpoint;
|
|
102
|
+
if (holder.discoveryOmit === "token") delete doc.token_endpoint;
|
|
103
|
+
_json(res, 200, doc); return;
|
|
104
|
+
}
|
|
105
|
+
if (u.pathname === "/jwks") { _json(res, 200, { keys: [_signer.jwk] }); return; }
|
|
106
|
+
if (u.pathname === "/bc-auth") {
|
|
107
|
+
_readBody(req, function (body) {
|
|
108
|
+
holder.bcAuthBodies.push(body);
|
|
109
|
+
if (holder.onBcAuth) { holder.onBcAuth(body, res); return; }
|
|
110
|
+
_json(res, 200, { auth_req_id: "bc-default-req", expires_in: 300, interval: 5 });
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (u.pathname === "/token") {
|
|
115
|
+
_readBody(req, function (body) {
|
|
116
|
+
holder.tokenBodies.push(body);
|
|
117
|
+
if (holder.onToken) { holder.onToken(body, res); return; }
|
|
118
|
+
res.writeHead(404); res.end();
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
res.writeHead(404); res.end();
|
|
123
|
+
});
|
|
124
|
+
await new Promise(function (r) { server.listen(0, "127.0.0.1", r); });
|
|
125
|
+
holder.issuer = "http://127.0.0.1:" + server.address().port;
|
|
126
|
+
try { return await fn(holder.issuer, holder); }
|
|
127
|
+
finally { await new Promise(function (r) { server.close(function () { r(); }); }); }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Standard poll-mode client with explicit endpoints (no discovery).
|
|
131
|
+
function _pollClient(issuer, extra) {
|
|
132
|
+
return b.auth.ciba.client.create(Object.assign({
|
|
133
|
+
issuer: issuer,
|
|
134
|
+
clientId: CLIENT_ID,
|
|
135
|
+
clientSecret: CLIENT_SECRET,
|
|
136
|
+
tokenEndpoint: issuer + "/token",
|
|
137
|
+
backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
138
|
+
deliveryMode: "poll",
|
|
139
|
+
allowHttp: true,
|
|
140
|
+
allowInternal: true,
|
|
141
|
+
}, extra || {}));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The CIBA client's discovery + JWKS + POST traffic rides the shared
|
|
145
|
+
// b.httpClient keep-alive agent; its cached sockets (and the stub server)
|
|
146
|
+
// would otherwise hold the forked worker's event loop open past run(). Tear
|
|
147
|
+
// the pool down and poll until the TCP handles have actually closed.
|
|
148
|
+
async function _drainTcpHandles() {
|
|
149
|
+
b.httpClient._resetForTest();
|
|
150
|
+
if (typeof process.getActiveResourcesInfo !== "function") return;
|
|
151
|
+
await helpers.waitUntil(function () {
|
|
152
|
+
return process.getActiveResourcesInfo().filter(function (t) {
|
|
153
|
+
return t === "TCPSocketWrap" || t === "TCPServerWrap";
|
|
154
|
+
}).length === 0;
|
|
155
|
+
}, { timeoutMs: 5000, label: "ciba: TCP handle drain after _resetForTest" });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---- startAuthentication (happy path + body assembly) -----------------
|
|
159
|
+
|
|
160
|
+
async function _runStartAuthenticationHappy() {
|
|
161
|
+
await _withIdp(async function (issuer, holder) {
|
|
162
|
+
var ciba = _pollClient(issuer, { scope: ["openid", "profile"] });
|
|
163
|
+
holder.onBcAuth = function (body, res) {
|
|
164
|
+
var s = JSON.stringify({ auth_req_id: "req-happy-1", expires_in: 120, interval: 3 });
|
|
165
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
166
|
+
res.end(s);
|
|
167
|
+
};
|
|
168
|
+
var ticket = await ciba.startAuthentication({
|
|
169
|
+
loginHint: "alice@example.com",
|
|
170
|
+
bindingMessage: "Authorize wire transfer of $4,200",
|
|
171
|
+
acrValues: ["urn:mace:incommon:iap:silver"],
|
|
172
|
+
requestedExpiry: 90,
|
|
173
|
+
});
|
|
174
|
+
check("b.auth.ciba.client.startAuthentication: returns ticket with authReqId/expiresIn/interval",
|
|
175
|
+
ticket && ticket.authReqId === "req-happy-1" && ticket.expiresIn === 120 && ticket.interval === 3);
|
|
176
|
+
var sent = new URLSearchParams(holder.bcAuthBodies[0]);
|
|
177
|
+
check("startAuthentication: login_hint forwarded", sent.get("login_hint") === "alice@example.com");
|
|
178
|
+
check("startAuthentication: scope array joined", sent.get("scope") === "openid profile");
|
|
179
|
+
check("startAuthentication: binding_message forwarded",
|
|
180
|
+
sent.get("binding_message") === "Authorize wire transfer of $4,200");
|
|
181
|
+
check("startAuthentication: acr_values joined", sent.get("acr_values") === "urn:mace:incommon:iap:silver");
|
|
182
|
+
check("startAuthentication: requested_expiry forwarded", sent.get("requested_expiry") === "90");
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ---- pollToken (happy path) -------------------------------------------
|
|
187
|
+
|
|
188
|
+
async function _runPollTokenHappy() {
|
|
189
|
+
await _withIdp(async function (issuer, holder) {
|
|
190
|
+
var ciba = _pollClient(issuer);
|
|
191
|
+
var AUTH_REQ_ID = "req-poll-happy";
|
|
192
|
+
var polls = 0;
|
|
193
|
+
holder.onToken = function (body, res) {
|
|
194
|
+
polls += 1;
|
|
195
|
+
if (polls === 1) {
|
|
196
|
+
var pend = JSON.stringify({ error: "authorization_pending" });
|
|
197
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(pend) });
|
|
198
|
+
res.end(pend); return;
|
|
199
|
+
}
|
|
200
|
+
var tok = JSON.stringify({
|
|
201
|
+
access_token: "poll-at", token_type: "Bearer", expires_in: 3600, scope: "openid",
|
|
202
|
+
id_token: _mintIdToken(issuer, AUTH_REQ_ID),
|
|
203
|
+
});
|
|
204
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(tok) });
|
|
205
|
+
res.end(tok);
|
|
206
|
+
};
|
|
207
|
+
var pending = null;
|
|
208
|
+
try { await ciba.pollToken({ authReqId: AUTH_REQ_ID }); }
|
|
209
|
+
catch (e) { pending = e; }
|
|
210
|
+
check("b.auth.ciba.client.pollToken: authorization_pending surfaced as typed AuthError",
|
|
211
|
+
pending && pending.code === "auth-ciba/authorization_pending");
|
|
212
|
+
var tokens = await ciba.pollToken({ authReqId: AUTH_REQ_ID });
|
|
213
|
+
check("pollToken: tokens returned once approved",
|
|
214
|
+
tokens && tokens.accessToken === "poll-at" && tokens.expiresIn === 3600 &&
|
|
215
|
+
tokens.claims && tokens.claims.claims["urn:openid:params:jwt:claim:auth_req_id"] === AUTH_REQ_ID);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---- parseNotification (push happy path) ------------------------------
|
|
220
|
+
|
|
221
|
+
async function _runParseNotificationHappy() {
|
|
222
|
+
await _withIdp(async function (issuer) {
|
|
223
|
+
var ciba = b.auth.ciba.client.create({
|
|
224
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
225
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
226
|
+
deliveryMode: "push", clientNotificationToken: CNT, allowHttp: true, allowInternal: true,
|
|
227
|
+
});
|
|
228
|
+
var info = await ciba.parseNotification(
|
|
229
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
230
|
+
{ body: { auth_req_id: "req-push-1", access_token: "push-at",
|
|
231
|
+
id_token: _mintIdToken(issuer, "req-push-1") } });
|
|
232
|
+
check("b.auth.ciba.client.parseNotification: push callback surfaces bound tokens",
|
|
233
|
+
info && info.authReqId === "req-push-1" && info.accessToken === "push-at" &&
|
|
234
|
+
info.claims && info.claims.claims["urn:openid:params:jwt:claim:auth_req_id"] === "req-push-1");
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---- create() option validation (entry-point THROW tier) -------------
|
|
239
|
+
|
|
240
|
+
// create() rejects malformed configuration at the entry point (config-time
|
|
241
|
+
// tier — throw so the operator catches the typo at boot). None of these hit
|
|
242
|
+
// the network: the inner OAuth client is built lazily.
|
|
243
|
+
function _runCreateValidation() {
|
|
244
|
+
function _throws(label, opts, code) {
|
|
245
|
+
var err = null;
|
|
246
|
+
try { b.auth.ciba.client.create(opts); } catch (e) { err = e; }
|
|
247
|
+
check(label, err && err.code === code);
|
|
248
|
+
}
|
|
249
|
+
var base = { issuer: "https://idp.example.com", clientId: CLIENT_ID };
|
|
250
|
+
_throws("create: unknown clientAuth refused",
|
|
251
|
+
Object.assign({ clientAuth: "kerberos" }, base), "auth-ciba/bad-client-auth");
|
|
252
|
+
_throws("create: clientAuth=secret without clientSecret refused",
|
|
253
|
+
Object.assign({ clientAuth: "secret" }, base), "auth-ciba/no-client-secret");
|
|
254
|
+
_throws("create: clientAuth=jwt without clientAssertionSigner refused",
|
|
255
|
+
Object.assign({ clientAuth: "jwt" }, base), "auth-ciba/no-assertion-signer");
|
|
256
|
+
_throws("create: unknown deliveryMode refused",
|
|
257
|
+
Object.assign({ clientAuth: "secret", clientSecret: CLIENT_SECRET, deliveryMode: "carrier-pigeon" }, base),
|
|
258
|
+
"auth-ciba/bad-delivery-mode");
|
|
259
|
+
_throws("create: ping/push without clientNotificationToken refused",
|
|
260
|
+
Object.assign({ clientAuth: "secret", clientSecret: CLIENT_SECRET, deliveryMode: "ping" }, base),
|
|
261
|
+
"auth-ciba/no-notification-token");
|
|
262
|
+
_throws("create: sub-32-char clientNotificationToken refused (CIBA §7.1.2 min entropy)",
|
|
263
|
+
Object.assign({ clientAuth: "secret", clientSecret: CLIENT_SECRET, deliveryMode: "push",
|
|
264
|
+
clientNotificationToken: "short" }, base),
|
|
265
|
+
"auth-ciba/notification-token-too-short");
|
|
266
|
+
// Non-object opts + missing issuer / clientId also refuse at the top.
|
|
267
|
+
var e1 = null; try { b.auth.ciba.client.create(null); } catch (e) { e1 = e; }
|
|
268
|
+
check("create: non-object opts refused", e1 !== null);
|
|
269
|
+
var e2 = null; try { b.auth.ciba.client.create({ clientId: CLIENT_ID }); } catch (e) { e2 = e; }
|
|
270
|
+
check("create: missing issuer refused", e2 && e2.code === "auth-ciba/no-issuer");
|
|
271
|
+
var e3 = null; try { b.auth.ciba.client.create({ issuer: "https://idp.example.com" }); } catch (e) { e3 = e; }
|
|
272
|
+
check("create: missing clientId refused", e3 && e3.code === "auth-ciba/no-client-id");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ---- startAuthentication branch coverage ------------------------------
|
|
276
|
+
|
|
277
|
+
async function _runStartAuthenticationBranches() {
|
|
278
|
+
await _withIdp(async function (issuer, holder) {
|
|
279
|
+
// no user hint → refused before any network call.
|
|
280
|
+
var pollCiba = _pollClient(issuer);
|
|
281
|
+
var noHint = null;
|
|
282
|
+
try { await pollCiba.startAuthentication({ scope: "openid" }); }
|
|
283
|
+
catch (e) { noHint = e; }
|
|
284
|
+
check("startAuthentication: no login_hint/login_hint_token/id_token_hint refused",
|
|
285
|
+
noHint && noHint.code === "auth-ciba/no-user-hint");
|
|
286
|
+
|
|
287
|
+
// login_hint_token + id_token_hint + acr string + user_code + interval
|
|
288
|
+
// clamp (out-of-range interval falls back to the 5s default) +
|
|
289
|
+
// expires_in default (non-positive → 600s).
|
|
290
|
+
holder.onBcAuth = function (body, res) {
|
|
291
|
+
var s = JSON.stringify({ auth_req_id: "req-variants", expires_in: 0, interval: 9999 });
|
|
292
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
293
|
+
res.end(s);
|
|
294
|
+
};
|
|
295
|
+
var t = await pollCiba.startAuthentication({
|
|
296
|
+
loginHintToken: "lht-abc", idTokenHint: "idth-xyz",
|
|
297
|
+
acrValues: "urn:acr:single", userCode: "1234",
|
|
298
|
+
});
|
|
299
|
+
check("startAuthentication: out-of-range interval clamps to 5s default", t.interval === 5);
|
|
300
|
+
check("startAuthentication: non-positive expires_in clamps to 600s default", t.expiresIn === 600);
|
|
301
|
+
var sent = new URLSearchParams(holder.bcAuthBodies[holder.bcAuthBodies.length - 1]);
|
|
302
|
+
check("startAuthentication: login_hint_token + id_token_hint + acr string + user_code forwarded",
|
|
303
|
+
sent.get("login_hint_token") === "lht-abc" && sent.get("id_token_hint") === "idth-xyz" &&
|
|
304
|
+
sent.get("acr_values") === "urn:acr:single" && sent.get("user_code") === "1234");
|
|
305
|
+
|
|
306
|
+
// A 200 response whose JSON lacks auth_req_id → bad-response.
|
|
307
|
+
holder.onBcAuth = function (body, res) {
|
|
308
|
+
var s = JSON.stringify({ expires_in: 120, interval: 5 });
|
|
309
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
310
|
+
res.end(s);
|
|
311
|
+
};
|
|
312
|
+
var badResp = null;
|
|
313
|
+
try { await pollCiba.startAuthentication({ loginHint: "a@b.co" }); }
|
|
314
|
+
catch (e) { badResp = e; }
|
|
315
|
+
check("startAuthentication: response without auth_req_id refused",
|
|
316
|
+
badResp && badResp.code === "auth-ciba/bad-response");
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// clientAuth=jwt (assertion signer wired) and clientAuth=mtls both drive
|
|
321
|
+
// distinct request-body assembly in startAuthentication + pollToken.
|
|
322
|
+
async function _runClientAuthModes() {
|
|
323
|
+
await _withIdp(async function (issuer, holder) {
|
|
324
|
+
var assertions = [];
|
|
325
|
+
var jwtCiba = b.auth.ciba.client.create({
|
|
326
|
+
issuer: issuer, clientId: CLIENT_ID, clientAuth: "jwt",
|
|
327
|
+
clientAssertionSigner: async function (payload) {
|
|
328
|
+
assertions.push(payload); return "header.body.sig";
|
|
329
|
+
},
|
|
330
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
331
|
+
deliveryMode: "poll", allowHttp: true, allowInternal: true,
|
|
332
|
+
});
|
|
333
|
+
holder.onBcAuth = function (body, res) {
|
|
334
|
+
var s = JSON.stringify({ auth_req_id: "req-jwt", expires_in: 120, interval: 5 });
|
|
335
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
336
|
+
res.end(s);
|
|
337
|
+
};
|
|
338
|
+
await jwtCiba.startAuthentication({ loginHint: "jwt@example.com" });
|
|
339
|
+
var jwtBody = new URLSearchParams(holder.bcAuthBodies[holder.bcAuthBodies.length - 1]);
|
|
340
|
+
check("startAuthentication(jwt): client_assertion + assertion_type set",
|
|
341
|
+
jwtBody.get("client_assertion") === "header.body.sig" &&
|
|
342
|
+
jwtBody.get("client_assertion_type") === "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" &&
|
|
343
|
+
jwtBody.get("client_id") === CLIENT_ID);
|
|
344
|
+
|
|
345
|
+
holder.onToken = function (body, res) {
|
|
346
|
+
var s = JSON.stringify({ access_token: "jwt-at", token_type: "Bearer" });
|
|
347
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
348
|
+
res.end(s);
|
|
349
|
+
};
|
|
350
|
+
var jwtTok = await jwtCiba.pollToken({ authReqId: "req-jwt" });
|
|
351
|
+
var jwtPollBody = new URLSearchParams(holder.tokenBodies[holder.tokenBodies.length - 1]);
|
|
352
|
+
check("pollToken(jwt): client_assertion set + minimal token response nulls absent fields",
|
|
353
|
+
jwtPollBody.get("client_assertion") === "header.body.sig" &&
|
|
354
|
+
jwtTok.accessToken === "jwt-at" && jwtTok.refreshToken === null &&
|
|
355
|
+
jwtTok.scope === null && jwtTok.expiresIn === null && jwtTok.idToken === null);
|
|
356
|
+
check("startAuthentication(jwt): assertion payload carries iss/sub/aud/jti",
|
|
357
|
+
assertions.length >= 1 && assertions[0].iss === CLIENT_ID &&
|
|
358
|
+
assertions[0].sub === CLIENT_ID && typeof assertions[0].jti === "string");
|
|
359
|
+
|
|
360
|
+
// mtls — no secret, no signer; client_id set on both requests.
|
|
361
|
+
var mtlsCiba = b.auth.ciba.client.create({
|
|
362
|
+
issuer: issuer, clientId: CLIENT_ID, clientAuth: "mtls",
|
|
363
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
364
|
+
deliveryMode: "poll", allowHttp: true, allowInternal: true,
|
|
365
|
+
});
|
|
366
|
+
holder.onBcAuth = function (body, res) {
|
|
367
|
+
var s = JSON.stringify({ auth_req_id: "req-mtls", expires_in: 120 });
|
|
368
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
369
|
+
res.end(s);
|
|
370
|
+
};
|
|
371
|
+
await mtlsCiba.startAuthentication({ loginHint: "mtls@example.com" });
|
|
372
|
+
var mtlsBody = new URLSearchParams(holder.bcAuthBodies[holder.bcAuthBodies.length - 1]);
|
|
373
|
+
check("startAuthentication(mtls): client_id set, no client_assertion",
|
|
374
|
+
mtlsBody.get("client_id") === CLIENT_ID && mtlsBody.get("client_assertion") === null);
|
|
375
|
+
holder.onToken = function (body, res) {
|
|
376
|
+
var s = JSON.stringify({ access_token: "mtls-at" });
|
|
377
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
378
|
+
res.end(s);
|
|
379
|
+
};
|
|
380
|
+
await mtlsCiba.pollToken({ authReqId: "req-mtls" });
|
|
381
|
+
var mtlsPoll = new URLSearchParams(holder.tokenBodies[holder.tokenBodies.length - 1]);
|
|
382
|
+
check("pollToken(mtls): client_id set on token request", mtlsPoll.get("client_id") === CLIENT_ID);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ping/push modes forward the client_notification_token on the backchannel
|
|
387
|
+
// request so the IdP can authenticate its later callback.
|
|
388
|
+
async function _runNotificationTokenForwarding() {
|
|
389
|
+
await _withIdp(async function (issuer, holder) {
|
|
390
|
+
var pingCiba = b.auth.ciba.client.create({
|
|
391
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
392
|
+
backchannelAuthenticationEndpoint: issuer + "/bc-auth", tokenEndpoint: issuer + "/token",
|
|
393
|
+
deliveryMode: "ping", clientNotificationToken: CNT, allowHttp: true, allowInternal: true,
|
|
394
|
+
});
|
|
395
|
+
holder.onBcAuth = function (body, res) {
|
|
396
|
+
var s = JSON.stringify({ auth_req_id: "req-ping", expires_in: 120 });
|
|
397
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
398
|
+
res.end(s);
|
|
399
|
+
};
|
|
400
|
+
await pingCiba.startAuthentication({ loginHint: "ping@example.com" });
|
|
401
|
+
var sent = new URLSearchParams(holder.bcAuthBodies[holder.bcAuthBodies.length - 1]);
|
|
402
|
+
check("startAuthentication(ping): client_notification_token forwarded", sent.get("client_notification_token") === CNT);
|
|
403
|
+
|
|
404
|
+
// push mode drives the OTHER arm of the deliveryMode ping/push guard.
|
|
405
|
+
var pushCiba = b.auth.ciba.client.create({
|
|
406
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
407
|
+
backchannelAuthenticationEndpoint: issuer + "/bc-auth", tokenEndpoint: issuer + "/token",
|
|
408
|
+
deliveryMode: "push", clientNotificationToken: CNT, allowHttp: true, allowInternal: true,
|
|
409
|
+
});
|
|
410
|
+
holder.onBcAuth = function (body, res) {
|
|
411
|
+
var s = JSON.stringify({ auth_req_id: "req-push-start", expires_in: 120 });
|
|
412
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
413
|
+
res.end(s);
|
|
414
|
+
};
|
|
415
|
+
await pushCiba.startAuthentication({ loginHint: "push@example.com" });
|
|
416
|
+
var sentPush = new URLSearchParams(holder.bcAuthBodies[holder.bcAuthBodies.length - 1]);
|
|
417
|
+
check("startAuthentication(push): client_notification_token forwarded", sentPush.get("client_notification_token") === CNT);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ---- binding-message validation (§7.1 legibility rules) ---------------
|
|
422
|
+
|
|
423
|
+
async function _runBindingMessageValidation() {
|
|
424
|
+
await _withIdp(async function (issuer) {
|
|
425
|
+
var ciba = _pollClient(issuer);
|
|
426
|
+
async function _rejects(label, msg, code) {
|
|
427
|
+
var err = null;
|
|
428
|
+
try { await ciba.startAuthentication({ loginHint: "a@b.co", bindingMessage: msg }); }
|
|
429
|
+
catch (e) { err = e; }
|
|
430
|
+
check(label, err && err.code === code);
|
|
431
|
+
}
|
|
432
|
+
await _rejects("bindingMessage: non-string refused", 12345, "auth-ciba/bad-binding-message");
|
|
433
|
+
await _rejects("bindingMessage: over-length refused", "x".repeat(201), "auth-ciba/binding-message-too-long");
|
|
434
|
+
await _rejects("bindingMessage: control character refused", "approve" + String.fromCharCode(0x07) + "now", "auth-ciba/binding-message-control-chars");
|
|
435
|
+
await _rejects("bindingMessage: bidi override refused", "approve" + String.fromCharCode(0x202e) + "en", "auth-ciba/binding-message-control-chars");
|
|
436
|
+
await _rejects("bindingMessage: bidi isolate refused", "approve" + String.fromCharCode(0x2066) + "en", "auth-ciba/binding-message-control-chars");
|
|
437
|
+
await _rejects("bindingMessage: zero-width space refused", "approve" + String.fromCharCode(0x200b) + "now", "auth-ciba/binding-message-control-chars");
|
|
438
|
+
await _rejects("bindingMessage: DEL/C1 control refused", "approve" + String.fromCharCode(0x007f) + "now", "auth-ciba/binding-message-control-chars");
|
|
439
|
+
await _rejects("bindingMessage: BOM refused", "approve" + String.fromCharCode(0xfeff) + "now", "auth-ciba/binding-message-control-chars");
|
|
440
|
+
// A null bindingMessage is treated as "not supplied" (validator returns
|
|
441
|
+
// null; the field is simply omitted from the backchannel request).
|
|
442
|
+
var okNull = false;
|
|
443
|
+
try { okNull = !!(await ciba.startAuthentication({ loginHint: "a@b.co", bindingMessage: null })); }
|
|
444
|
+
catch (_e) { okNull = false; }
|
|
445
|
+
check("bindingMessage: null is accepted as 'not supplied'", okNull === true);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ---- _postForm IdP-error surfacing ------------------------------------
|
|
450
|
+
|
|
451
|
+
async function _runPostFormErrors() {
|
|
452
|
+
await _withIdp(async function (issuer, holder) {
|
|
453
|
+
var ciba = _pollClient(issuer);
|
|
454
|
+
// 4xx with structured OAuth error JSON → typed auth-ciba/<error>.
|
|
455
|
+
holder.onToken = function (body, res) {
|
|
456
|
+
var s = JSON.stringify({ error: "invalid_request", error_description: "malformed auth_req_id" });
|
|
457
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
458
|
+
res.end(s);
|
|
459
|
+
};
|
|
460
|
+
var typed = null;
|
|
461
|
+
try { await ciba.pollToken({ authReqId: "req-err" }); }
|
|
462
|
+
catch (e) { typed = e; }
|
|
463
|
+
check("_postForm: structured OAuth error JSON surfaced as typed AuthError",
|
|
464
|
+
typed && typed.code === "auth-ciba/invalid_request" &&
|
|
465
|
+
typed.statusCode === 400 && typed.cibaError && typed.cibaError.error === "invalid_request");
|
|
466
|
+
|
|
467
|
+
// 5xx with a NON-JSON body → falls back to http-<status> code.
|
|
468
|
+
holder.onToken = function (body, res) {
|
|
469
|
+
var s = "upstream unavailable";
|
|
470
|
+
res.writeHead(503, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(s) });
|
|
471
|
+
res.end(s);
|
|
472
|
+
};
|
|
473
|
+
var nonJson = null;
|
|
474
|
+
try { await ciba.pollToken({ authReqId: "req-503" }); }
|
|
475
|
+
catch (e) { nonJson = e; }
|
|
476
|
+
check("_postForm: non-JSON error body falls back to http-<status> code",
|
|
477
|
+
nonJson && nonJson.code === "auth-ciba/http-503" && nonJson.statusCode === 503);
|
|
478
|
+
|
|
479
|
+
// 200 with a non-JSON body → auth-ciba/bad-json.
|
|
480
|
+
holder.onToken = function (body, res) {
|
|
481
|
+
var s = "<html>not json</html>";
|
|
482
|
+
res.writeHead(200, { "Content-Type": "text/html", "Content-Length": Buffer.byteLength(s) });
|
|
483
|
+
res.end(s);
|
|
484
|
+
};
|
|
485
|
+
var badJson = null;
|
|
486
|
+
try { await ciba.pollToken({ authReqId: "req-badjson" }); }
|
|
487
|
+
catch (e) { badJson = e; }
|
|
488
|
+
check("_postForm: non-JSON 200 body refused as bad-json",
|
|
489
|
+
badJson && badJson.code === "auth-ciba/bad-json");
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ---- discovery-driven endpoint resolution -----------------------------
|
|
494
|
+
|
|
495
|
+
async function _runDiscovery() {
|
|
496
|
+
// backchannel + token endpoints resolved from the discovery document
|
|
497
|
+
// (no explicit endpoints configured).
|
|
498
|
+
await _withIdp(async function (issuer, holder) {
|
|
499
|
+
var ciba = b.auth.ciba.client.create({
|
|
500
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
501
|
+
deliveryMode: "poll", allowHttp: true, allowInternal: true,
|
|
502
|
+
});
|
|
503
|
+
holder.onBcAuth = function (body, res) {
|
|
504
|
+
var s = JSON.stringify({ auth_req_id: "req-disc", expires_in: 120 });
|
|
505
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
506
|
+
res.end(s);
|
|
507
|
+
};
|
|
508
|
+
var t = await ciba.startAuthentication({ loginHint: "disc@example.com" });
|
|
509
|
+
check("startAuthentication: backchannel endpoint resolved via discovery", t.authReqId === "req-disc");
|
|
510
|
+
check("startAuthentication: discovery hit the well-known backchannel path",
|
|
511
|
+
holder.bcAuthBodies.length === 1);
|
|
512
|
+
// pollToken on the same client resolves the token endpoint via discovery.
|
|
513
|
+
holder.onToken = function (body, res) {
|
|
514
|
+
var s = JSON.stringify({ access_token: "disc-at", token_type: "Bearer" });
|
|
515
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
516
|
+
res.end(s);
|
|
517
|
+
};
|
|
518
|
+
var dt = await ciba.pollToken({ authReqId: "req-disc" });
|
|
519
|
+
check("pollToken: token endpoint resolved via discovery", dt && dt.accessToken === "disc-at");
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// discovery doc without backchannel_authentication_endpoint → refused.
|
|
523
|
+
await _withIdp(async function (issuer, holder) {
|
|
524
|
+
holder.discoveryOmit = "backchannel";
|
|
525
|
+
var ciba = b.auth.ciba.client.create({
|
|
526
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
527
|
+
deliveryMode: "poll", allowHttp: true, allowInternal: true,
|
|
528
|
+
});
|
|
529
|
+
var err = null;
|
|
530
|
+
try { await ciba.startAuthentication({ loginHint: "a@b.co" }); }
|
|
531
|
+
catch (e) { err = e; }
|
|
532
|
+
check("startAuthentication: discovery lacking backchannel endpoint refused",
|
|
533
|
+
err && err.code === "auth-ciba/no-backchannel-endpoint");
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// discovery doc without token_endpoint → pollToken refused.
|
|
537
|
+
await _withIdp(async function (issuer, holder) {
|
|
538
|
+
holder.discoveryOmit = "token";
|
|
539
|
+
var ciba = b.auth.ciba.client.create({
|
|
540
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
541
|
+
backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
542
|
+
deliveryMode: "poll", allowHttp: true, allowInternal: true,
|
|
543
|
+
});
|
|
544
|
+
var err = null;
|
|
545
|
+
try { await ciba.pollToken({ authReqId: "req-x" }); }
|
|
546
|
+
catch (e) { err = e; }
|
|
547
|
+
check("pollToken: discovery lacking token endpoint refused",
|
|
548
|
+
err && err.code === "auth-ciba/no-token-endpoint");
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ---- pollToken adversarial + id_token verification --------------------
|
|
553
|
+
|
|
554
|
+
async function _runPollTokenAdversarial() {
|
|
555
|
+
await _withIdp(async function (issuer, holder) {
|
|
556
|
+
var ciba = _pollClient(issuer);
|
|
557
|
+
|
|
558
|
+
// missing authReqId refused at the entry (defensive-reader tier).
|
|
559
|
+
var noId = null;
|
|
560
|
+
try { await ciba.pollToken({}); } catch (e) { noId = e; }
|
|
561
|
+
check("pollToken: missing authReqId refused", noId && noId.code === "auth-ciba/no-auth-req-id");
|
|
562
|
+
var emptyId = null;
|
|
563
|
+
try { await ciba.pollToken({ authReqId: "" }); } catch (e) { emptyId = e; }
|
|
564
|
+
check("pollToken: empty authReqId refused", emptyId && emptyId.code === "auth-ciba/no-auth-req-id");
|
|
565
|
+
|
|
566
|
+
// id_token signed by an unpublished key → verification fails closed.
|
|
567
|
+
holder.onToken = function (body, res) {
|
|
568
|
+
var s = JSON.stringify({ access_token: "at", id_token: _mintIdToken(issuer, "req-forge", _rogue) });
|
|
569
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
570
|
+
res.end(s);
|
|
571
|
+
};
|
|
572
|
+
var forged = null;
|
|
573
|
+
try { await ciba.pollToken({ authReqId: "req-forge" }); }
|
|
574
|
+
catch (e) { forged = e; }
|
|
575
|
+
check("pollToken: id_token with unverifiable signature refused (fail-closed)",
|
|
576
|
+
forged && forged.code === "auth-ciba/id-token-invalid");
|
|
577
|
+
|
|
578
|
+
// id_token bound to a DIFFERENT auth_req_id → substitution refused.
|
|
579
|
+
holder.onToken = function (body, res) {
|
|
580
|
+
var s = JSON.stringify({ access_token: "at", id_token: _mintIdToken(issuer, "req-OTHER") });
|
|
581
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
582
|
+
res.end(s);
|
|
583
|
+
};
|
|
584
|
+
var mismatch = null;
|
|
585
|
+
try { await ciba.pollToken({ authReqId: "req-mine" }); }
|
|
586
|
+
catch (e) { mismatch = e; }
|
|
587
|
+
check("pollToken: id_token bound to another auth_req_id refused (substitution)",
|
|
588
|
+
mismatch && mismatch.code === "auth-ciba/id-token-authreqid-mismatch");
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// ---- slow_down back-off + terminal-error interval cleanup -------------
|
|
593
|
+
|
|
594
|
+
async function _runSlowDownAndTerminal() {
|
|
595
|
+
// entry seeded via startAuthentication → slow_down honors an IdP-suggested
|
|
596
|
+
// interval when it exceeds the minimum 5s bump.
|
|
597
|
+
await _withIdp(async function (issuer, holder) {
|
|
598
|
+
var ciba = _pollClient(issuer);
|
|
599
|
+
holder.onBcAuth = function (body, res) {
|
|
600
|
+
var s = JSON.stringify({ auth_req_id: "req-slow", expires_in: 300, interval: 5 });
|
|
601
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
602
|
+
res.end(s);
|
|
603
|
+
};
|
|
604
|
+
await ciba.startAuthentication({ loginHint: "slow@example.com" });
|
|
605
|
+
holder.onToken = function (body, res) {
|
|
606
|
+
var s = JSON.stringify({ error: "slow_down", interval: 20 });
|
|
607
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
608
|
+
res.end(s);
|
|
609
|
+
};
|
|
610
|
+
var slow = null;
|
|
611
|
+
try { await ciba.pollToken({ authReqId: "req-slow" }); }
|
|
612
|
+
catch (e) { slow = e; }
|
|
613
|
+
check("pollToken: slow_down honors IdP-suggested interval over the 5s minimum bump",
|
|
614
|
+
slow && slow.code === "auth-ciba/slow_down" && slow.nextIntervalSec === 20);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// entry seeded at the 300s ceiling → next bump is clamped to the max.
|
|
618
|
+
await _withIdp(async function (issuer, holder) {
|
|
619
|
+
var ciba = _pollClient(issuer);
|
|
620
|
+
holder.onBcAuth = function (body, res) {
|
|
621
|
+
var s = JSON.stringify({ auth_req_id: "req-cap", expires_in: 300, interval: 300 });
|
|
622
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
623
|
+
res.end(s);
|
|
624
|
+
};
|
|
625
|
+
await ciba.startAuthentication({ loginHint: "cap@example.com" });
|
|
626
|
+
holder.onToken = function (body, res) {
|
|
627
|
+
var s = JSON.stringify({ error: "slow_down" });
|
|
628
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
629
|
+
res.end(s);
|
|
630
|
+
};
|
|
631
|
+
var capped = null;
|
|
632
|
+
try { await ciba.pollToken({ authReqId: "req-cap" }); }
|
|
633
|
+
catch (e) { capped = e; }
|
|
634
|
+
check("pollToken: slow_down bump clamped to the 300s ceiling",
|
|
635
|
+
capped && capped.nextIntervalSec === 300);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
// slow_down for an unseeded authReqId → bumps from the 5s default.
|
|
639
|
+
await _withIdp(async function (issuer, holder) {
|
|
640
|
+
var ciba = _pollClient(issuer);
|
|
641
|
+
holder.onToken = function (body, res) {
|
|
642
|
+
var s = JSON.stringify({ error: "slow_down" });
|
|
643
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
644
|
+
res.end(s);
|
|
645
|
+
};
|
|
646
|
+
var unseeded = null;
|
|
647
|
+
try { await ciba.pollToken({ authReqId: "never-started" }); }
|
|
648
|
+
catch (e) { unseeded = e; }
|
|
649
|
+
check("pollToken: slow_down on an unseeded authReqId bumps from the 5s default",
|
|
650
|
+
unseeded && unseeded.nextIntervalSec === 10);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// terminal error clears the per-authReqId interval tracking.
|
|
654
|
+
await _withIdp(async function (issuer, holder) {
|
|
655
|
+
var ciba = _pollClient(issuer);
|
|
656
|
+
holder.onBcAuth = function (body, res) {
|
|
657
|
+
var s = JSON.stringify({ auth_req_id: "req-term", expires_in: 300, interval: 5 });
|
|
658
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
659
|
+
res.end(s);
|
|
660
|
+
};
|
|
661
|
+
await ciba.startAuthentication({ loginHint: "term@example.com" });
|
|
662
|
+
holder.onToken = function (body, res) {
|
|
663
|
+
var s = JSON.stringify({ error: "access_denied", error_description: "user rejected" });
|
|
664
|
+
res.writeHead(400, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
665
|
+
res.end(s);
|
|
666
|
+
};
|
|
667
|
+
var terminal = null;
|
|
668
|
+
try { await ciba.pollToken({ authReqId: "req-term" }); }
|
|
669
|
+
catch (e) { terminal = e; }
|
|
670
|
+
check("pollToken: terminal access_denied surfaced (interval entry cleared)",
|
|
671
|
+
terminal && terminal.code === "auth-ciba/access_denied");
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// A tiny expires_in makes the auth_req_id's interval entry lapse before the
|
|
676
|
+
// next startAuthentication runs, driving _purgeExpiredIntervals' delete arm.
|
|
677
|
+
async function _runIntervalPurge() {
|
|
678
|
+
await _withIdp(async function (issuer, holder) {
|
|
679
|
+
var ciba = _pollClient(issuer);
|
|
680
|
+
var n = 0;
|
|
681
|
+
holder.onBcAuth = function (body, res) {
|
|
682
|
+
n += 1;
|
|
683
|
+
// First request registers an entry that has already lapsed by the time
|
|
684
|
+
// the second request's opportunistic sweep runs (a full localhost
|
|
685
|
+
// round-trip easily exceeds a sub-millisecond expiry).
|
|
686
|
+
var expiresIn = n === 1 ? 0.0001 : 300;
|
|
687
|
+
var s = JSON.stringify({ auth_req_id: "req-purge-" + n, expires_in: expiresIn, interval: 5 });
|
|
688
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
689
|
+
res.end(s);
|
|
690
|
+
};
|
|
691
|
+
var first = await ciba.startAuthentication({ loginHint: "purge1@example.com" });
|
|
692
|
+
var second = await ciba.startAuthentication({ loginHint: "purge2@example.com" });
|
|
693
|
+
// A third register sees the (still-valid, 300s) req-purge-2 entry and
|
|
694
|
+
// leaves it in place — the sweep's non-expired arm.
|
|
695
|
+
var third = await ciba.startAuthentication({ loginHint: "purge3@example.com" });
|
|
696
|
+
check("startAuthentication: expired entries purged, live entries retained across registers",
|
|
697
|
+
first.authReqId === "req-purge-1" && second.authReqId === "req-purge-2" &&
|
|
698
|
+
third.authReqId === "req-purge-3");
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// ---- parseNotification authentication + shape defenses ----------------
|
|
703
|
+
|
|
704
|
+
function _pushClient(issuer) {
|
|
705
|
+
return b.auth.ciba.client.create({
|
|
706
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
707
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
708
|
+
deliveryMode: "push", clientNotificationToken: CNT, allowHttp: true, allowInternal: true,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async function _runParseNotificationErrors() {
|
|
713
|
+
await _withIdp(async function (issuer) {
|
|
714
|
+
var ciba = _pushClient(issuer);
|
|
715
|
+
async function _rejects(label, req, opts, code) {
|
|
716
|
+
var err = null;
|
|
717
|
+
try { await ciba.parseNotification(req, opts); } catch (e) { err = e; }
|
|
718
|
+
check(label, err && err.code === code);
|
|
719
|
+
}
|
|
720
|
+
await _rejects("parseNotification: req without headers refused",
|
|
721
|
+
{}, {}, "auth-ciba/bad-notification-req");
|
|
722
|
+
await _rejects("parseNotification: null req refused",
|
|
723
|
+
null, {}, "auth-ciba/bad-notification-req");
|
|
724
|
+
await _rejects("parseNotification: missing Authorization header refused",
|
|
725
|
+
{ headers: {} }, {}, "auth-ciba/missing-bearer");
|
|
726
|
+
await _rejects("parseNotification: non-Bearer scheme refused",
|
|
727
|
+
{ headers: { authorization: "Basic abc" } }, {}, "auth-ciba/missing-bearer");
|
|
728
|
+
await _rejects("parseNotification: empty bearer token refused",
|
|
729
|
+
{ headers: { authorization: "Bearer " } }, {}, "auth-ciba/bad-bearer");
|
|
730
|
+
await _rejects("parseNotification: wrong client_notification_token refused (timing-safe compare)",
|
|
731
|
+
{ headers: { authorization: "Bearer " + CNT + "-tampered" } },
|
|
732
|
+
{ body: { auth_req_id: "x" } }, "auth-ciba/wrong-bearer");
|
|
733
|
+
// Authenticated but malformed body shapes.
|
|
734
|
+
await _rejects("parseNotification: non-JSON string body refused",
|
|
735
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
736
|
+
{ body: "<<not json>>" }, "auth-ciba/bad-notification-body");
|
|
737
|
+
await _rejects("parseNotification: null body refused",
|
|
738
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
739
|
+
{ body: null }, "auth-ciba/no-notification-body");
|
|
740
|
+
await _rejects("parseNotification: body without auth_req_id refused",
|
|
741
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
742
|
+
{ body: { access_token: "at" } }, "auth-ciba/no-auth-req-id-in-body");
|
|
743
|
+
|
|
744
|
+
// A JSON *string* body is parsed then surfaced (middleware-parsed shape).
|
|
745
|
+
var info = await ciba.parseNotification(
|
|
746
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
747
|
+
{ body: JSON.stringify({ auth_req_id: "req-strbody", access_token: "str-at" }) });
|
|
748
|
+
check("parseNotification: JSON string body parsed + surfaced",
|
|
749
|
+
info && info.authReqId === "req-strbody" && info.accessToken === "str-at");
|
|
750
|
+
|
|
751
|
+
// RFC 7235 §2.1 — the auth-scheme token is ASCII case-insensitive. A
|
|
752
|
+
// spec-compliant sender using a lowercase or mixed-case "bearer" scheme
|
|
753
|
+
// with the correct token must authenticate, not be refused as missing.
|
|
754
|
+
var lowerScheme = await ciba.parseNotification(
|
|
755
|
+
{ headers: { authorization: "bearer " + CNT } },
|
|
756
|
+
{ body: { auth_req_id: "req-lower" } });
|
|
757
|
+
check("parseNotification: lowercase 'bearer' scheme authenticates (RFC 7235 case-insensitive)",
|
|
758
|
+
lowerScheme && lowerScheme.authReqId === "req-lower");
|
|
759
|
+
var mixedScheme = await ciba.parseNotification(
|
|
760
|
+
{ headers: { authorization: "BeArEr " + CNT } },
|
|
761
|
+
{ body: { auth_req_id: "req-mixed" } });
|
|
762
|
+
check("parseNotification: mixed-case 'BeArEr' scheme authenticates",
|
|
763
|
+
mixedScheme && mixedScheme.authReqId === "req-mixed");
|
|
764
|
+
|
|
765
|
+
// Push body carrying a forged (unverifiable) id_token → fail-closed.
|
|
766
|
+
var forged = null;
|
|
767
|
+
try {
|
|
768
|
+
await ciba.parseNotification(
|
|
769
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
770
|
+
{ body: { auth_req_id: "req-p", id_token: _mintIdToken(issuer, "req-p", _rogue) } });
|
|
771
|
+
} catch (e) { forged = e; }
|
|
772
|
+
check("parseNotification: forged pushed id_token refused (bearer authenticates caller, not token)",
|
|
773
|
+
forged && forged.code === "auth-ciba/id-token-invalid");
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ping mode: the callback body is just { auth_req_id } — no id_token, so the
|
|
778
|
+
// verified-claims field is null and the token fields default to null.
|
|
779
|
+
async function _runParseNotificationPing() {
|
|
780
|
+
await _withIdp(async function (issuer) {
|
|
781
|
+
var ciba = b.auth.ciba.client.create({
|
|
782
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
783
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
784
|
+
deliveryMode: "ping", clientNotificationToken: CNT, allowHttp: true, allowInternal: true,
|
|
785
|
+
});
|
|
786
|
+
// No opts arg → the handler reads req.body directly (popts defaults to {}).
|
|
787
|
+
var info = await ciba.parseNotification(
|
|
788
|
+
{ headers: { authorization: "Bearer " + CNT }, body: { auth_req_id: "req-ping-only" } });
|
|
789
|
+
check("parseNotification(ping): bare { auth_req_id } surfaces authReqId, null tokens/claims",
|
|
790
|
+
info && info.authReqId === "req-ping-only" && info.claims === null &&
|
|
791
|
+
info.accessToken === null && info.idToken === null && info.expiresIn === null);
|
|
792
|
+
|
|
793
|
+
// A numeric expires_in in a token-bearing body surfaces verbatim.
|
|
794
|
+
var withExp = await ciba.parseNotification(
|
|
795
|
+
{ headers: { authorization: "Bearer " + CNT } },
|
|
796
|
+
{ body: { auth_req_id: "req-ping-exp", access_token: "at", expires_in: 1800 } });
|
|
797
|
+
check("parseNotification: numeric expires_in surfaced", withExp.expiresIn === 1800);
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Option defaults + no-argument entry points (defensive-reader tier: the
|
|
802
|
+
// handlers coerce a missing opts object to {} then reject on the real gap).
|
|
803
|
+
async function _runOptionDefaults() {
|
|
804
|
+
await _withIdp(async function (issuer, holder) {
|
|
805
|
+
// deliveryMode defaults to "poll" when omitted.
|
|
806
|
+
var defCiba = b.auth.ciba.client.create({
|
|
807
|
+
issuer: issuer, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET,
|
|
808
|
+
tokenEndpoint: issuer + "/token", backchannelAuthenticationEndpoint: issuer + "/bc-auth",
|
|
809
|
+
allowHttp: true, allowInternal: true,
|
|
810
|
+
});
|
|
811
|
+
check("create: deliveryMode defaults to poll", defCiba.deliveryMode === "poll");
|
|
812
|
+
|
|
813
|
+
// startAuthentication() with no args → sopts coerced to {} → no-user-hint.
|
|
814
|
+
var noArgsStart = null;
|
|
815
|
+
try { await defCiba.startAuthentication(); } catch (e) { noArgsStart = e; }
|
|
816
|
+
check("startAuthentication: no-argument call refused (no user hint)",
|
|
817
|
+
noArgsStart && noArgsStart.code === "auth-ciba/no-user-hint");
|
|
818
|
+
|
|
819
|
+
// pollToken() with no args → popts coerced to {} → no-auth-req-id.
|
|
820
|
+
var noArgsPoll = null;
|
|
821
|
+
try { await defCiba.pollToken(); } catch (e) { noArgsPoll = e; }
|
|
822
|
+
check("pollToken: no-argument call refused (no authReqId)",
|
|
823
|
+
noArgsPoll && noArgsPoll.code === "auth-ciba/no-auth-req-id");
|
|
824
|
+
|
|
825
|
+
// A token response with NO access_token → accessToken defaults to null.
|
|
826
|
+
holder.onToken = function (body, res) {
|
|
827
|
+
var s = JSON.stringify({ refresh_token: "rt-only" });
|
|
828
|
+
res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(s) });
|
|
829
|
+
res.end(s);
|
|
830
|
+
};
|
|
831
|
+
var noAt = await defCiba.pollToken({ authReqId: "req-noat" });
|
|
832
|
+
check("pollToken: absent access_token defaults to null (refresh_token surfaced)",
|
|
833
|
+
noAt.accessToken === null && noAt.refreshToken === "rt-only");
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async function _runTests() {
|
|
838
|
+
await _runStartAuthenticationHappy();
|
|
839
|
+
await _runPollTokenHappy();
|
|
840
|
+
await _runParseNotificationHappy();
|
|
841
|
+
_runCreateValidation();
|
|
842
|
+
await _runStartAuthenticationBranches();
|
|
843
|
+
await _runClientAuthModes();
|
|
844
|
+
await _runNotificationTokenForwarding();
|
|
845
|
+
await _runBindingMessageValidation();
|
|
846
|
+
await _runPostFormErrors();
|
|
847
|
+
await _runDiscovery();
|
|
848
|
+
await _runPollTokenAdversarial();
|
|
849
|
+
await _runSlowDownAndTerminal();
|
|
850
|
+
await _runIntervalPurge();
|
|
851
|
+
await _runParseNotificationErrors();
|
|
852
|
+
await _runParseNotificationPing();
|
|
853
|
+
await _runOptionDefaults();
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async function run() {
|
|
857
|
+
try { await _runTests(); }
|
|
858
|
+
finally { await _drainTcpHandles(); }
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
module.exports = { run: run };
|
|
862
|
+
|
|
863
|
+
if (require.main === module) {
|
|
864
|
+
run().then(function () { console.log("OK ciba — " + helpers.getChecks() + " checks"); })
|
|
865
|
+
.catch(function (e) { console.error("FAIL:", (e && e.stack) || e); process.exit(1); });
|
|
866
|
+
}
|