Repository: panva/jose Branch: main Commit: a18254b74070 Files: 309 Total size: 1014.6 KB Directory structure: gitextract_gemcyrai/ ├── .electron_flags.sh ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ └── config.yml │ ├── dependabot.yml │ └── workflows/ │ ├── build.yml │ ├── lock.yml │ ├── release.yml │ ├── retry.yml │ └── test.yml ├── .gitignore ├── .node_flags.sh ├── .prettierignore ├── .prettierrc.json ├── .versionrc.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── ava.config.mjs ├── cookbook/ │ ├── jwe.mjs │ └── jws.mjs ├── docs/ │ ├── README.md │ ├── jwe/ │ │ ├── compact/ │ │ │ ├── decrypt/ │ │ │ │ ├── README.md │ │ │ │ ├── functions/ │ │ │ │ │ └── compactDecrypt.md │ │ │ │ └── interfaces/ │ │ │ │ └── CompactDecryptGetKey.md │ │ │ └── encrypt/ │ │ │ ├── README.md │ │ │ └── classes/ │ │ │ └── CompactEncrypt.md │ │ ├── flattened/ │ │ │ ├── decrypt/ │ │ │ │ ├── README.md │ │ │ │ ├── functions/ │ │ │ │ │ └── flattenedDecrypt.md │ │ │ │ └── interfaces/ │ │ │ │ └── FlattenedDecryptGetKey.md │ │ │ └── encrypt/ │ │ │ ├── README.md │ │ │ └── classes/ │ │ │ └── FlattenedEncrypt.md │ │ └── general/ │ │ ├── decrypt/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── generalDecrypt.md │ │ │ └── interfaces/ │ │ │ └── GeneralDecryptGetKey.md │ │ └── encrypt/ │ │ ├── README.md │ │ ├── classes/ │ │ │ └── GeneralEncrypt.md │ │ └── interfaces/ │ │ └── Recipient.md │ ├── jwk/ │ │ ├── embedded/ │ │ │ ├── README.md │ │ │ └── functions/ │ │ │ └── EmbeddedJWK.md │ │ └── thumbprint/ │ │ ├── README.md │ │ └── functions/ │ │ ├── calculateJwkThumbprint.md │ │ └── calculateJwkThumbprintUri.md │ ├── jwks/ │ │ ├── local/ │ │ │ ├── README.md │ │ │ └── functions/ │ │ │ └── createLocalJWKSet.md │ │ └── remote/ │ │ ├── README.md │ │ ├── functions/ │ │ │ └── createRemoteJWKSet.md │ │ ├── interfaces/ │ │ │ ├── ExportedJWKSCache.md │ │ │ └── RemoteJWKSetOptions.md │ │ ├── type-aliases/ │ │ │ ├── FetchImplementation.md │ │ │ └── JWKSCacheInput.md │ │ └── variables/ │ │ ├── customFetch.md │ │ └── jwksCache.md │ ├── jws/ │ │ ├── compact/ │ │ │ ├── sign/ │ │ │ │ ├── README.md │ │ │ │ └── classes/ │ │ │ │ └── CompactSign.md │ │ │ └── verify/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── compactVerify.md │ │ │ └── interfaces/ │ │ │ └── CompactVerifyGetKey.md │ │ ├── flattened/ │ │ │ ├── sign/ │ │ │ │ ├── README.md │ │ │ │ └── classes/ │ │ │ │ └── FlattenedSign.md │ │ │ └── verify/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── flattenedVerify.md │ │ │ └── interfaces/ │ │ │ └── FlattenedVerifyGetKey.md │ │ └── general/ │ │ ├── sign/ │ │ │ ├── README.md │ │ │ ├── classes/ │ │ │ │ └── GeneralSign.md │ │ │ └── interfaces/ │ │ │ └── Signature.md │ │ └── verify/ │ │ ├── README.md │ │ ├── functions/ │ │ │ └── generalVerify.md │ │ └── interfaces/ │ │ └── GeneralVerifyGetKey.md │ ├── jwt/ │ │ ├── decrypt/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── jwtDecrypt.md │ │ │ └── interfaces/ │ │ │ ├── JWTDecryptGetKey.md │ │ │ └── JWTDecryptOptions.md │ │ ├── encrypt/ │ │ │ ├── README.md │ │ │ └── classes/ │ │ │ └── EncryptJWT.md │ │ ├── sign/ │ │ │ ├── README.md │ │ │ └── classes/ │ │ │ └── SignJWT.md │ │ ├── unsecured/ │ │ │ ├── README.md │ │ │ ├── classes/ │ │ │ │ └── UnsecuredJWT.md │ │ │ └── interfaces/ │ │ │ └── UnsecuredResult.md │ │ └── verify/ │ │ ├── README.md │ │ ├── functions/ │ │ │ └── jwtVerify.md │ │ └── interfaces/ │ │ ├── JWTVerifyGetKey.md │ │ └── JWTVerifyOptions.md │ ├── key/ │ │ ├── export/ │ │ │ ├── README.md │ │ │ └── functions/ │ │ │ ├── exportJWK.md │ │ │ ├── exportPKCS8.md │ │ │ └── exportSPKI.md │ │ ├── generate_key_pair/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── generateKeyPair.md │ │ │ └── interfaces/ │ │ │ ├── GenerateKeyPairOptions.md │ │ │ └── GenerateKeyPairResult.md │ │ ├── generate_secret/ │ │ │ ├── README.md │ │ │ ├── functions/ │ │ │ │ └── generateSecret.md │ │ │ └── interfaces/ │ │ │ └── GenerateSecretOptions.md │ │ └── import/ │ │ ├── README.md │ │ ├── functions/ │ │ │ ├── importJWK.md │ │ │ ├── importPKCS8.md │ │ │ ├── importSPKI.md │ │ │ └── importX509.md │ │ └── interfaces/ │ │ └── KeyImportOptions.md │ ├── types/ │ │ ├── README.md │ │ ├── interfaces/ │ │ │ ├── CompactDecryptResult.md │ │ │ ├── CompactJWEHeaderParameters.md │ │ │ ├── CompactJWSHeaderParameters.md │ │ │ ├── CompactVerifyResult.md │ │ │ ├── CritOption.md │ │ │ ├── DecryptOptions.md │ │ │ ├── EncryptOptions.md │ │ │ ├── FlattenedDecryptResult.md │ │ │ ├── FlattenedJWE.md │ │ │ ├── FlattenedJWS.md │ │ │ ├── FlattenedJWSInput.md │ │ │ ├── FlattenedVerifyResult.md │ │ │ ├── GeneralDecryptResult.md │ │ │ ├── GeneralJWE.md │ │ │ ├── GeneralJWS.md │ │ │ ├── GeneralJWSInput.md │ │ │ ├── GeneralVerifyResult.md │ │ │ ├── GetKeyFunction.md │ │ │ ├── JSONWebKeySet.md │ │ │ ├── JWEHeaderParameters.md │ │ │ ├── JWEKeyManagementHeaderParameters.md │ │ │ ├── JWK.md │ │ │ ├── JWKParameters.md │ │ │ ├── JWK_AKP_Private.md │ │ │ ├── JWK_AKP_Public.md │ │ │ ├── JWK_EC_Private.md │ │ │ ├── JWK_EC_Public.md │ │ │ ├── JWK_OKP_Private.md │ │ │ ├── JWK_OKP_Public.md │ │ │ ├── JWK_RSA_Private.md │ │ │ ├── JWK_RSA_Public.md │ │ │ ├── JWK_oct.md │ │ │ ├── JWSHeaderParameters.md │ │ │ ├── JWTClaimVerificationOptions.md │ │ │ ├── JWTDecryptResult.md │ │ │ ├── JWTHeaderParameters.md │ │ │ ├── JWTPayload.md │ │ │ ├── JWTVerifyResult.md │ │ │ ├── JoseHeaderParameters.md │ │ │ ├── KeyObject.md │ │ │ ├── ProduceJWT.md │ │ │ ├── ResolvedKey.md │ │ │ ├── SignOptions.md │ │ │ └── VerifyOptions.md │ │ └── type-aliases/ │ │ └── CryptoKey.md │ └── util/ │ ├── base64url/ │ │ ├── README.md │ │ └── functions/ │ │ ├── decode.md │ │ └── encode.md │ ├── decode_jwt/ │ │ ├── README.md │ │ └── functions/ │ │ └── decodeJwt.md │ ├── decode_protected_header/ │ │ ├── README.md │ │ ├── functions/ │ │ │ └── decodeProtectedHeader.md │ │ └── type-aliases/ │ │ └── ProtectedHeaderParameters.md │ └── errors/ │ ├── README.md │ └── classes/ │ ├── JOSEAlgNotAllowed.md │ ├── JOSEError.md │ ├── JOSENotSupported.md │ ├── JWEDecryptionFailed.md │ ├── JWEInvalid.md │ ├── JWKInvalid.md │ ├── JWKSInvalid.md │ ├── JWKSMultipleMatchingKeys.md │ ├── JWKSNoMatchingKey.md │ ├── JWKSTimeout.md │ ├── JWSInvalid.md │ ├── JWSSignatureVerificationFailed.md │ ├── JWTClaimValidationFailed.md │ ├── JWTExpired.md │ └── JWTInvalid.md ├── jsr.json ├── package.json ├── patches/ │ ├── typedoc-plugin-markdown+4.11.0.patch │ └── typedoc-plugin-mdn-links+5.1.1.patch ├── playwright.config.ts ├── src/ │ ├── index.ts │ ├── jwe/ │ │ ├── compact/ │ │ │ ├── decrypt.ts │ │ │ └── encrypt.ts │ │ ├── flattened/ │ │ │ ├── decrypt.ts │ │ │ └── encrypt.ts │ │ └── general/ │ │ ├── decrypt.ts │ │ └── encrypt.ts │ ├── jwk/ │ │ ├── embedded.ts │ │ └── thumbprint.ts │ ├── jwks/ │ │ ├── local.ts │ │ └── remote.ts │ ├── jws/ │ │ ├── compact/ │ │ │ ├── sign.ts │ │ │ └── verify.ts │ │ ├── flattened/ │ │ │ ├── sign.ts │ │ │ └── verify.ts │ │ └── general/ │ │ ├── sign.ts │ │ └── verify.ts │ ├── jwt/ │ │ ├── decrypt.ts │ │ ├── encrypt.ts │ │ ├── sign.ts │ │ ├── unsecured.ts │ │ └── verify.ts │ ├── key/ │ │ ├── export.ts │ │ ├── generate_key_pair.ts │ │ ├── generate_secret.ts │ │ └── import.ts │ ├── lib/ │ │ ├── aesgcmkw.ts │ │ ├── aeskw.ts │ │ ├── asn1.ts │ │ ├── base64.ts │ │ ├── buffer_utils.ts │ │ ├── check_key_type.ts │ │ ├── content_encryption.ts │ │ ├── crypto_key.ts │ │ ├── deflate.ts │ │ ├── ecdhes.ts │ │ ├── helpers.ts │ │ ├── invalid_key_input.ts │ │ ├── is_key_like.ts │ │ ├── jwk_to_key.ts │ │ ├── jwt_claims_set.ts │ │ ├── key_management.ts │ │ ├── key_to_jwk.ts │ │ ├── normalize_key.ts │ │ ├── pbes2kw.ts │ │ ├── rsaes.ts │ │ ├── signing.ts │ │ ├── type_checks.ts │ │ ├── validate_algorithms.ts │ │ └── validate_crit.ts │ ├── types.d.ts │ └── util/ │ ├── base64url.ts │ ├── decode_jwt.ts │ ├── decode_protected_header.ts │ └── errors.ts ├── tap/ │ ├── .browser.ts │ ├── .browsers.sh │ ├── .bun.sh │ ├── .deno.sh │ ├── .electron.sh │ ├── .node.sh │ ├── .workerd.sh │ ├── aes.ts │ ├── aeskw.ts │ ├── cookbook.ts │ ├── ecdh.ts │ ├── encrypt.ts │ ├── env.ts │ ├── fixtures.ts │ ├── generate_options.ts │ ├── hmac.ts │ ├── jwk.ts │ ├── jwks.ts │ ├── jws.ts │ ├── keyobject-stub.ts │ ├── noop.ts │ ├── pbes2.ts │ ├── pem.ts │ ├── rsaes.ts │ ├── run-browser.ts │ ├── run-bun.ts │ ├── run-deno.ts │ ├── run-electron.ts │ ├── run-node.ts │ ├── run-workerd.ts │ ├── run.ts │ ├── sign.ts │ └── tsconfig.json ├── test/ │ ├── jwe/ │ │ ├── compact.decrypt.test.ts │ │ ├── compact.encrypt.test.ts │ │ ├── flattened.decrypt.test.ts │ │ ├── flattened.encrypt.test.ts │ │ ├── general.test.ts │ │ └── zip.test.ts │ ├── jwk/ │ │ ├── embedded.test.ts │ │ ├── issue-459.test.ts │ │ ├── jwk2key.test.ts │ │ └── thumbprint.test.ts │ ├── jwks/ │ │ ├── local.test.ts │ │ └── remote.test.ts │ ├── jws/ │ │ ├── compact.sign.test.ts │ │ ├── compact.verify.test.ts │ │ ├── crit.test.ts │ │ ├── flattened.sign.test.ts │ │ ├── flattened.verify.test.ts │ │ ├── general.test.ts │ │ ├── restrictions.test.ts │ │ └── unencoded.test.ts │ ├── jwt/ │ │ ├── decrypt.test.ts │ │ ├── encrypt.test.ts │ │ ├── sign.test.ts │ │ ├── time_setters.ts │ │ ├── unsecured.test.ts │ │ └── verify.test.ts │ ├── tsconfig.json │ ├── unit/ │ │ ├── buffer_utils.test.ts │ │ ├── cek.test.ts │ │ ├── check_iv_length.test.ts │ │ ├── check_key_type.test.ts │ │ ├── check_key_type_jwk.test.ts │ │ ├── iv.test.ts │ │ └── secs.test.ts │ └── util/ │ ├── decode_jwt.test.ts │ └── decode_protected_header.test.ts ├── tools/ │ ├── postbump.cjs │ ├── prebump.cjs │ ├── publish.cjs │ └── release-notes.cjs ├── tsconfig/ │ └── types.json ├── tsconfig.json └── typedoc.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .electron_flags.sh ================================================ echo $(electron -i <<< 'process.exit(0)' 2> /dev/null | grep "Using" | awk '{$1=$1};1' | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g") electron -i <<< 'process.exit(parseInt(process.versions.node, 10))' &> /dev/null NODE_VERSION=$? export NODE_OPTIONS='--enable-source-maps --import=tsx/esm' ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: 🐞Bug report description: There's a bug I want to report labels: - triage body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report. Do not use this form to ask questions or make suggestions, use the [appropriate](https://github.com/panva/jose/issues/new/choose) Discussions Topic for those. - type: textarea attributes: label: What happened? description: A clear and concise description of what the bug is and what you expected to happen instead. validations: required: true - type: input attributes: label: Version description: What exact version of the library do you use? placeholder: e.g. v3.14.0 validations: required: true - type: dropdown attributes: label: Runtime description: What runtime are you seeing the problem on? options: - Select an option - Browser - Bun - Cloudflare Workers - Deno - Electron - Node.js - Other (I will specify below) validations: required: true - type: input attributes: label: Runtime Details description: More information about the runtime (e.g. node version, browser vendor and version, electron version, operating system) validations: required: true - type: textarea attributes: label: Code to reproduce description: Please copy and paste code to reproduce the issue. render: js validations: required: true - type: checkboxes attributes: label: Required options: - label: I have searched the issues tracker and discussions for similar topics and couldn't find anything related. required: true - label: I agree to follow this project's [Code of Conduct](https://github.com/panva/jose/blob/main/CODE_OF_CONDUCT.md) required: true ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: ❓ Question url: https://github.com/panva/jose/discussions/new?category=q-a about: Have a question about using jose? Head over to the discussions "Q&A" Category - name: 💡 Feature proposal url: https://github.com/panva/jose/discussions/new?category=ideas about: Have a proposal for a new feature? Head over to the discussions "Ideas" Category - name: Support the project url: https://github.com/sponsors/panva about: Are you asking your nth question? Relying on jose for critical operations? Consider supporting the project so that it may continue being maintained. ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: actions: patterns: - "*" ================================================ FILE: .github/workflows/build.yml ================================================ name: Build permissions: {} on: workflow_call: outputs: cache-key: value: ${{ jobs.build.outputs.cache-key }} jobs: build: concurrency: build-${{ github.ref }} runs-on: ubuntu-latest outputs: cache-key: ${{ steps.cache-key.outputs.value }} steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - id: cache-key run: echo "value=dist-${{ hashFiles('src/**/*.ts', 'tsconfig/*.json', '.github/workflows/*.yml', 'package-lock.json') }}" >> $GITHUB_OUTPUT - name: Cache dist uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: dist with: path: dist key: ${{ steps.cache-key.outputs.value }} - name: Build run: npm run build-all if: ${{ steps.dist.outputs.cache-hit != 'true' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: dist path: dist - run: git reset HEAD --hard ================================================ FILE: .github/workflows/lock.yml ================================================ name: "Lock threads" permissions: {} on: schedule: - cron: '11 11 * * 1' jobs: lock: permissions: issues: write pull-requests: write discussions: write continue-on-error: true runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0 with: github-token: ${{ github.token }} issue-inactive-days: "90" issue-lock-reason: "" pr-inactive-days: "90" pr-lock-reason: "" discussion-inactive-days: "90" ================================================ FILE: .github/workflows/release.yml ================================================ name: Release permissions: {} on: push: tags: ['v6.[0-9]+.[0-9]+'] jobs: build: uses: ./.github/workflows/build.yml npm: needs: - build runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* registry-url: https://registry.npmjs.org - name: Load cached dist uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: dist with: path: dist key: dist-${{ hashFiles('src/**/*.ts', 'tsconfig/*.json', '.github/workflows/*.yml', 'package-lock.json') }} fail-on-cache-miss: true - name: Prepare distribution run: node tools/publish.cjs - run: npm publish jsr: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - run: npx jsr publish cleanup: needs: - npm - jsr runs-on: ubuntu-latest permissions: contents: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - run: git push origin $GITHUB_SHA:v6.x - run: git rm -r dist - run: | git config --local user.name "github-actions[bot]" git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" git commit -m "chore: cleanup after release" - run: git push origin HEAD:main github: needs: - npm - jsr runs-on: ubuntu-latest permissions: contents: write discussions: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: node tools/release-notes.cjs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/retry.yml ================================================ name: Retry permissions: {} on: workflow_run: workflows: - Test types: - completed jobs: retry: permissions: actions: write runs-on: ubuntu-latest if: ${{ github.repository == 'panva/jose' && github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }} steps: - run: gh api -XPOST ${{ github.event.workflow_run.rerun_url }}-failed-jobs env: GH_TOKEN: ${{ github.token }} ================================================ FILE: .github/workflows/test.yml ================================================ name: Test permissions: {} on: push: branches: [main] pull_request: branches: [main] schedule: - cron: '11 11 * * 1' workflow_dispatch: jobs: build: uses: ./.github/workflows/build.yml node-versions: uses: panva/.github/.github/workflows/node-versions.yml@main with: min: 20 node: needs: - node-versions strategy: fail-fast: false matrix: node-version: ${{ fromJSON(needs.node-versions.outputs.matrix) }} suite: - tap:node - test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup node id: node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: ${{ matrix.node-version }} cache: 'npm' check-latest: true - run: npm clean-install - name: Run Test Suite run: npm run ${{ matrix.suite }} deno: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4 with: deno-version: latest - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - name: Test Deno Definitions run: | npm run build:deno deno check dist/deno/index.ts - name: Test Deno run: npm run tap:deno bun: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version: latest - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - name: Test Bun run: npm run tap:bun workerd: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - run: npm install --global workerd - run: npm link workerd - name: Run Test Suite run: npm run tap:workerd browsers: runs-on: macos-latest strategy: fail-fast: false matrix: browser: [chromium, firefox, safari] steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - run: npm upgrade playwright - name: get playwright version id: playwright-version run: echo "version=$(npm list playwright --json | jq -r '.dependencies["@playwright/test"].dependencies.playwright.version')" >> $GITHUB_OUTPUT - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: ~/Library/Caches/ms-playwright key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - run: npx playwright install --only-shell chromium firefox webkit - name: Run Test Suite run: npm run tap:browsers env: BROWSER: ${{ matrix.browser }} electron: runs-on: macos-15 steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: lts/* cache: 'npm' - run: npm clean-install - name: Install Electron run: npm install --global electron - name: Run Test Suite run: npm run tap:electron ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directory and lockfiles node_modules/ jspm_packages/ yarn.lock npm-shrinkwrap.json # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist dist-browser-tests # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* .npmrc tap/*.js tap/*.js.map tap/run-*.mjs tap/run-*.mjs.map tap/run-*.cjs tap/run-*.cjs.map *.bak *.bun tap/.workerd.capnp tap/.workers.capnp test-results/.last-run.json ================================================ FILE: .node_flags.sh ================================================ echo "Using Node.js $(node --version)" node -e 'process.exit(parseInt(process.versions.node, 10))' &> /dev/null NODE_VERSION=$? export NODE_OPTIONS='--enable-source-maps --import=tsx/esm' ================================================ FILE: .prettierignore ================================================ # Ignore artifacts: build dist coverage ================================================ FILE: .prettierrc.json ================================================ { "trailingComma": "all", "singleQuote": true, "printWidth": 100, "semi": false, "plugins": ["prettier-plugin-jsdoc"], "tsdoc": true, "jsdocSeparateReturnsFromParam": true, "jsdocSeparateTagGroups": true, "jsdocPrintWidth": 100 } ================================================ FILE: .versionrc.json ================================================ { "commit-all": true, "scripts": { "prerelease": "node ./tools/prebump.cjs && npm run-script build-all", "postbump": "node ./tools/postbump.cjs", "postchangelog": "sed -i '' -e 's/### \\[/## [/g' CHANGELOG.md" }, "types": [ { "type": "feat", "section": "Features" }, { "type": "fix", "section": "Fixes" }, { "type": "chore", "hidden": true }, { "type": "docs", "section": "Documentation", "hidden": false }, { "type": "style", "hidden": true }, { "type": "refactor", "section": "Refactor", "hidden": false }, { "type": "revert", "section": "Reverts", "hidden": false }, { "type": "perf", "section": "Performance", "hidden": false }, { "type": "test", "hidden": true } ] } ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ## [6.2.2](https://github.com/panva/jose/compare/v6.2.1...v6.2.2) (2026-03-18) ### Fixes * reject failed decompression with JWEInvalid error ([043b181](https://github.com/panva/jose/commit/043b181a96ee55d92b9ff1ee94e11be36e258ee4)) ## [6.2.1](https://github.com/panva/jose/compare/v6.2.0...v6.2.1) (2026-03-09) ### Refactor * reorganize internals, less files, smaller footprint ([d4231f9](https://github.com/panva/jose/commit/d4231f9f2a654d203589a787bfa8a34fb03c87c5)) ## [6.2.0](https://github.com/panva/jose/compare/v6.1.3...v6.2.0) (2026-03-05) ### Features * re-introduce JWE "zip" (Compression Algorithm) Header Parameter support ([b13b446](https://github.com/panva/jose/commit/b13b44688baeaf078259379c61f42569f5d63ab5)) ### Documentation * clarify return of general jws and jwe ([56682b4](https://github.com/panva/jose/commit/56682b4608eacafb7bcd6b63713d6434e0e6ad66)) ## [6.1.3](https://github.com/panva/jose/compare/v6.1.2...v6.1.3) (2025-12-02) ### Refactor * avoid export * as for google closure's compiler sake ([6303d98](https://github.com/panva/jose/commit/6303d98efba00c9a3f8f3e814c85ac6e6944b11c)), closes [#832](https://github.com/panva/jose/issues/832) ## [6.1.2](https://github.com/panva/jose/compare/v6.1.1...v6.1.2) (2025-11-15) ### Refactor * fallback to checking instanceof for CryptoKey ([901cd90](https://github.com/panva/jose/commit/901cd908f325265c39f8af1f1505138e0a689f94)), closes [#765](https://github.com/panva/jose/issues/765) [#803](https://github.com/panva/jose/issues/803) [#821](https://github.com/panva/jose/issues/821) [#827](https://github.com/panva/jose/issues/827) [#828](https://github.com/panva/jose/issues/828) ## [6.1.1](https://github.com/panva/jose/compare/v6.1.0...v6.1.1) (2025-11-09) ### Documentation * add link to RFC9864 ([767edde](https://github.com/panva/jose/commit/767edde5bde07c60e2c1b1db365ad234a9ae5195)) * link to ML-DSA for JOSE ([ed4252c](https://github.com/panva/jose/commit/ed4252ca8ed084e734a53b214f62e3da11c39857)) * remove mention of Edge Runtime from the readme ([94fdde7](https://github.com/panva/jose/commit/94fdde702498f5e94ec3307f85f120f43a527590)) * update README.md ([25098ef](https://github.com/panva/jose/commit/25098ef7518be732bbef3ea6ed87b0d61a85f581)) ### Refactor * eliminate named exports in the source code ([f6ae30d](https://github.com/panva/jose/commit/f6ae30d5d86c376269ffa853f62e403d13d4f610)) * expose setKeyManagementParameters also on a GeneralEncrypt Recipient ([16e6b23](https://github.com/panva/jose/commit/16e6b230ee29662c599c556b91a2c367f0fa088c)) * faster path for symmetric key checks ([a44c2ec](https://github.com/panva/jose/commit/a44c2ec6351fb639816846ec8b23f5f084a3d9fb)) * improve en/decoding overheads ([daee426](https://github.com/panva/jose/commit/daee4265c9ab218acbcdb1d7b10c3c728447240b)) ## [6.1.0](https://github.com/panva/jose/compare/v6.0.13...v6.1.0) (2025-08-27) ### Features * support AKP JWKs in calculateJwkThumbprint and calculateJwkThumbprintUri ([cf2092a](https://github.com/panva/jose/commit/cf2092a2b51c9fb67049e96ee22d551ad34c0b2c)) * support for the ML-DSA PQC Algorithm Identifiers ([25ddce4](https://github.com/panva/jose/commit/25ddce491ba3968e8802db5913e49a52224246be)) ## [6.0.13](https://github.com/panva/jose/compare/v6.0.12...v6.0.13) (2025-08-21) ### Refactor * more readability in ecdhes.ts ([84da9de](https://github.com/panva/jose/commit/84da9decd8b2f266a343a507b6b79197f2da11e8)) * update asn1.ts helpers ([b4f8fb3](https://github.com/panva/jose/commit/b4f8fb372689b5b38074aa45c9921a6a997a9142)) ## [6.0.12](https://github.com/panva/jose/compare/v6.0.11...v6.0.12) (2025-07-15) ### Documentation * add known caveats to customFetch ([02e1f1e](https://github.com/panva/jose/commit/02e1f1e87c764885121590aa2af80c831a9320ab)) * mention the apu/apv parameter names in setKeyManagementParameters ([6274d5a](https://github.com/panva/jose/commit/6274d5abca3d3882d3d722415f064fee5c44d0e4)) * update compact setKeyManagementParameters ([2f44381](https://github.com/panva/jose/commit/2f44381b6b0e30cf538ea2edb0d42b76a61de1f8)) * use GitHub Flavored Markdown for notes and warnings ([f6b4ffc](https://github.com/panva/jose/commit/f6b4ffcd82d9645d9b818ece09a09b5a636b69c9)) ### Refactor * createPublicKey is not a constructor ([61ded78](https://github.com/panva/jose/commit/61ded787150c6ae13eeb65b6680f857d6657465f)) * update asn1.ts helper functions ([b2b611c](https://github.com/panva/jose/commit/b2b611c426eeed3c40c3a1423d8a02dd46f3f7e8)) ## [6.0.11](https://github.com/panva/jose/compare/v6.0.10...v6.0.11) (2025-05-05) ### Fixes * typ checking edge-cases when it contains a slash (/) character ([31e4baf](https://github.com/panva/jose/commit/31e4bafc0a908cac044bbe34c7024f4eac9c974f)) ## [6.0.10](https://github.com/panva/jose/compare/v6.0.9...v6.0.10) (2025-03-12) ### Refactor * removed unused claims methods ([74719cf](https://github.com/panva/jose/commit/74719cfcfba1920b87740245da08bb70b68e7cd1)) * reorganize jwt claim set utils ([1f12d88](https://github.com/panva/jose/commit/1f12d88ee8cfa328126934a7020396f9a8dd8932)) ## [6.0.9](https://github.com/panva/jose/compare/v6.0.8...v6.0.9) (2025-03-11) ### Documentation * add more symbol document, ignore ts-private fields ([8b73687](https://github.com/panva/jose/commit/8b73687595a7ca608aa1b78870b6b165ad5249f2)) * bump typedoc ([6163a8b](https://github.com/panva/jose/commit/6163a8b6a773100ed31d207b598db1259a7e13a8)) * drop cdnjs links in README ([a910038](https://github.com/panva/jose/commit/a9100383ab16cb62c375401fac08a77f3c6c528d)) * drop denoland/x links in README and add jsr ([3662b9e](https://github.com/panva/jose/commit/3662b9ec44403bd501fc895bea4ded623d23e7e1)) * fix key export links from docs/README.md ([c8edfc2](https://github.com/panva/jose/commit/c8edfc29416d3339f6c78fdc42bdfdfadaa5cf7e)) ### Refactor * always assume structuredClone is present ([f7898a9](https://github.com/panva/jose/commit/f7898a9487508684dbbeba990e0cc96d344b1ff6)) * hide internal private fields and drop ProduceJWT inheritance ([ab18881](https://github.com/panva/jose/commit/ab18881a57524897aec300c9bf7bbd73d9e3447c)) * less objects when JWE JWT Replicated Header Parameters are used ([c763a0e](https://github.com/panva/jose/commit/c763a0e373b0e814dc4705dd2eb7daa614eae43a)) ## [6.0.8](https://github.com/panva/jose/compare/v6.0.7...v6.0.8) (2025-02-26) ### Fixes * export [customFetch] symbol from the default entrypoint ([1615614](https://github.com/panva/jose/commit/1615614964b4a66ac888f470bad94b6dee7009bc)), closes [#762](https://github.com/panva/jose/issues/762) ## [6.0.7](https://github.com/panva/jose/compare/v6.0.6...v6.0.7) (2025-02-25) ### Documentation * improve generate key/secret and import function descriptions ([cd06359](https://github.com/panva/jose/commit/cd06359597a3b8e5c30892142f3d750eded2fbce)) ### Fixes * use [customFetch] when provided to createRemoteJWKSet ([35f6509](https://github.com/panva/jose/commit/35f6509ff45927d9f55f3b5c09f96f459e60136a)), closes [#760](https://github.com/panva/jose/issues/760) ## [6.0.6](https://github.com/panva/jose/compare/v6.0.5...v6.0.6) (2025-02-23) ### Refactor * move base64url around ([e1350ef](https://github.com/panva/jose/commit/e1350eff8786b92eead82258dc2d5b9db128b523)) ### Documentation * add various exported symbol descriptions ([3b8ff71](https://github.com/panva/jose/commit/3b8ff717ad2054c1c50f72bcc866f08cd672b49d)) * add various exported symbol descriptions ([fc4e7da](https://github.com/panva/jose/commit/fc4e7dab4c7021d5d2f23a55de93c9ed35b79cef)) * add various exported symbol descriptions ([74f02c8](https://github.com/panva/jose/commit/74f02c833e057ab419173be7b8b8140eddcfc19f)) * update base64url function descriptions ([03d72c8](https://github.com/panva/jose/commit/03d72c8a5578bd442190724ca05994b0856432d5)) ## [6.0.5](https://github.com/panva/jose/compare/v6.0.4...v6.0.5) (2025-02-23) ### Refactor * **types:** make JWKParameters.kty compatible with @types/node and @types/web ([bb6ccfe](https://github.com/panva/jose/commit/bb6ccfed3efd5c84c540c2a1f621b9d2951f2b84)) ### Documentation * add various exported symbol descriptions ([f52c2ff](https://github.com/panva/jose/commit/f52c2ff0c3edbe30b32a4c9858199bfbc9f9367e)) ## [6.0.4](https://github.com/panva/jose/compare/v6.0.3...v6.0.4) (2025-02-22) ### Refactor * optimize base64 with tc39/proposal-arraybuffer-base64 ([8a0da69](https://github.com/panva/jose/commit/8a0da6968ec2813ced80962e847a89676ef9e8af)), closes [#752](https://github.com/panva/jose/issues/752) * update getSPKI to use crypto.createPublicKey when available ([92392a0](https://github.com/panva/jose/commit/92392a0aa278ecf419a8b0b6a984c72d071125ad)), closes [#752](https://github.com/panva/jose/issues/752) * use Double HMAC pattern for AES-CBC tag comparison ([f3ba4c7](https://github.com/panva/jose/commit/f3ba4c715f3ff5baef624bbb62771398de488166)), closes [#752](https://github.com/panva/jose/issues/752) ## [6.0.3](https://github.com/panva/jose/compare/v6.0.2...v6.0.3) (2025-02-22) ### Documentation * remove root module tag so that README.md shows up on jsr.io ([ee70698](https://github.com/panva/jose/commit/ee7069818b6a3bc6df7aa7ab8060a90299ef0c9a)) ## [6.0.2](https://github.com/panva/jose/compare/v6.0.1...v6.0.2) (2025-02-22) ### Documentation * add module tags to all entrypoints ([a5687aa](https://github.com/panva/jose/commit/a5687aaed475ba113dd01d8fddf95f5911c17d0f)) ## [6.0.1](https://github.com/panva/jose/compare/v6.0.0...v6.0.1) (2025-02-22) ### Fixes * **types:** update build to include extensions in type imports ([9b96672](https://github.com/panva/jose/commit/9b96672ef79aabf07699e0ccafa5b54380f6330c)) ## [6.0.0](https://github.com/panva/jose/compare/v5.10.0...v6.0.0) (2025-02-22) ### ⚠ BREAKING CHANGES * The PEMImportOptions type interface is renamed to KeyImportOptions. * all builds and bundles now use ES2022 as target * createRemoteJWKSet now uses fetch, because of that its Node.js only options.agent property has been removed and new fetch-related options were added * drop support for Ed448 and X448 * drop support for JWK key_ops and CryptoKey usages "(un)wrapKey" and "deriveKey" * resolved keys returned as part of verify/decrypt operations (when get key functions are used) are always normalized to either Uint8Array / CryptoKey depending on what's more efficient for the executed operation * Key "Type" Generics are removed * CJS-style require is now only possible when require(esm) support is present in the Node.js runtime * private KeyObject instances can no longer be used for verify operations * private KeyObject instances can no longer be used for encryption operations * generateSecret, generateKeyPair, importPKCS8, importSPKI, importJWK, and importX509 now yield a CryptoKey instead of a KeyObject in Node.js * drop support for Node.js 18.x and earlier * runtime-specific npm releases (jose-browser-runtime, jose-node-cjs-runtime, and jose-node-esm-runtime) are no longer maintained or supported * removed secp256k1 JWS support * removed deprecated experimental APIs * removed RSA1_5 JWE support ### Features * enable CryptoKey and KeyObject inputs in JWK thumbprint functions ([6fc9c44](https://github.com/panva/jose/commit/6fc9c4461a1fa39b363185e866bd686044ee30c6)) * JSON Web Key is now an allowed input everywhere ([ebda967](https://github.com/panva/jose/commit/ebda9674e9daf9b9d09568cf17b1a23d9cf20a60)) ### Refactor * always use infered CryptoKey ([c4abaa2](https://github.com/panva/jose/commit/c4abaa265ef56b517f868cf49db4ed8975446465)) * backport the Ed25519 JWS Algorithm Identifier support ([7a94cb9](https://github.com/panva/jose/commit/7a94cb997af94ae2db61efbeb271e0555afc62d8)) * drop support for Ed448 and X448 ([2fae1c4](https://github.com/panva/jose/commit/2fae1c447b621fb5dbdb1896c29c3a0772f26f44)) * drop support for JWK key_ops and CryptoKey usages "(un)wrapKey" and "deriveKey" ([ef918be](https://github.com/panva/jose/commit/ef918be8bab1b8dc5ec30b026d85da8ce7e0b062)) * ensure export functions continue to work with KeyObject inputs ([28e9e68](https://github.com/panva/jose/commit/28e9e684bbe15a1fa40631465b9688fdf1cecf0e)) * hardcode the cryptoRuntime export since it is now always WebCryptoAPI ([e00f273](https://github.com/panva/jose/commit/e00f2737fdc7b44b3c9d8c581460617d64152ce2)) * JWK import extractable default for public keys is now true ([64dcebe](https://github.com/panva/jose/commit/64dcebef368851863d11c3718f10bdbb1f0102c7)) * PEM import extractable default for public keys is now true ([4e9f114](https://github.com/panva/jose/commit/4e9f1143c7c31176e55d2e75aea96ead16db4107)) * removed deprecated APIs ([5352083](https://github.com/panva/jose/commit/5352083dc603f8f71acd34d969842be1881f3b19)) * removed secp256k1 JWS support ([e2b58a5](https://github.com/panva/jose/commit/e2b58a5ca50a40559451179cdd15f62be831eda2)) * restructure src/lib and src/runtime now that runtime is fixed ([9b236ce](https://github.com/panva/jose/commit/9b236cec4e66a588d0e7f27039a08a51db5abc49)) * target is now ES2022 everywhere ([aa590d5](https://github.com/panva/jose/commit/aa590d569f7eff948f96e4e8210843668777c724)) * update importJWK args to align with other import functions ([355a2dd](https://github.com/panva/jose/commit/355a2dd33a2466245f2872014110c9a1c0257c16)) * WebCryptoAPI is now the only crypto used ([161de46](https://github.com/panva/jose/commit/161de466a29d90a1129e671ed3a23be556c77f27)) ## [5.10.0](https://github.com/panva/jose/compare/v5.9.6...v5.10.0) (2025-02-17) ### Features * support fully specified Ed25519 algorithm identifier ([c39f57d](https://github.com/panva/jose/commit/c39f57d614ec7493ad7b1eeaf8ccdc51c499cd17)) ## [5.9.6](https://github.com/panva/jose/compare/v5.9.5...v5.9.6) (2024-10-20) ### Reverts * Revert "refactor(build): simplify package exports" ([2ef3a52](https://github.com/panva/jose/commit/2ef3a5266e2f903aab2f8c9d43437151d7da0122)) ## [5.9.5](https://github.com/panva/jose/compare/v5.9.4...v5.9.5) (2024-10-20) ### Refactor * **build:** simplify package exports ([4783f7f](https://github.com/panva/jose/commit/4783f7fcb0282c2e24479758614a82e3d7c0e627)) ## [5.9.4](https://github.com/panva/jose/compare/v5.9.3...v5.9.4) (2024-10-11) ### Refactor * **types:** update error definitions ([510c5ca](https://github.com/panva/jose/commit/510c5ca4a7c5dce08b6dd358a7120ad18747c3c8)) ## [5.9.3](https://github.com/panva/jose/compare/v5.9.2...v5.9.3) (2024-09-22) ### Refactor * use as Type for type assertions instead of ([c4dc24d](https://github.com/panva/jose/commit/c4dc24da1e6cec99dade1a82eecea423236d342e)) ## [5.9.2](https://github.com/panva/jose/compare/v5.9.1...v5.9.2) (2024-09-14) ### Refactor * **types:** remove index signatures from JWK interfaces ([ccf0cda](https://github.com/panva/jose/commit/ccf0cdaa7166d9273a951356859172192ed1be3f)) ## [5.9.1](https://github.com/panva/jose/compare/v5.9.0...v5.9.1) (2024-09-13) ### Fixes * **types:** add missing index signature on the convenience JWK types ([90a93dc](https://github.com/panva/jose/commit/90a93dc9ce5da294e91d2a964ed593299c464893)) ## [5.9.0](https://github.com/panva/jose/compare/v5.8.0...v5.9.0) (2024-09-13) ### Features * allow JWK objects as "key" input to sign and verify ([c6302ea](https://github.com/panva/jose/commit/c6302ea6886974eb433c51ddcf6eff1bbfdf5459)) ## [5.8.0](https://github.com/panva/jose/compare/v5.7.0...v5.8.0) (2024-08-26) ### Features * add subpath module exports ([72ecff6](https://github.com/panva/jose/commit/72ecff6574d252f407b6e145a166c995cdd85949)) ### Refactor * omit LocalJWKSet export since it's no longer needed for RemoteJWKSet ([c502731](https://github.com/panva/jose/commit/c502731fd888c165df32214f13333bc055d1d3f4)) ## [5.7.0](https://github.com/panva/jose/compare/v5.6.3...v5.7.0) (2024-08-19) ### Features * graduate jwksCache to stable API ([0f09c12](https://github.com/panva/jose/commit/0f09c124529bf84f027e57ce1f769319e7d42185)) ## [5.6.3](https://github.com/panva/jose/compare/v5.6.2...v5.6.3) (2024-07-03) ### Fixes * add sideEffects:false to nested ESM package.json files ([f3aff1c](https://github.com/panva/jose/commit/f3aff1cf018b62356e46a70e89aa96adeca6e686)) ## [5.6.2](https://github.com/panva/jose/compare/v5.6.1...v5.6.2) (2024-06-27) ### Refactor * CryptoKey normalization is not always async ([b7751f5](https://github.com/panva/jose/commit/b7751f58743c837f5bc76df301430c9b7c72dd85)) * weak cache normalized CryptoKey instances ([32b25a5](https://github.com/panva/jose/commit/32b25a5c94febe79dcfa7b2e62e432d1dce1cd44)) ### Fixes * ensure KeyObject type in Web API encrypt/decrypt ([b7920bd](https://github.com/panva/jose/commit/b7920bd635d1b7642307709e888ea3dcaf3e9b6f)) ## [5.6.1](https://github.com/panva/jose/compare/v5.6.0...v5.6.1) (2024-06-27) ### Refactor * normalize is always defined for Web API runtimes ([7bcb103](https://github.com/panva/jose/commit/7bcb10392cb7ff6bd17ebeb27f95f334d799fdb8)) ### Fixes * workaround turbo's eager optimizations ([723a042](https://github.com/panva/jose/commit/723a04264e03daa5e311055ad2672e6ae5dfd1e3)), closes [#690](https://github.com/panva/jose/issues/690) ## [5.6.0](https://github.com/panva/jose/compare/v5.5.0...v5.6.0) (2024-06-27) ### Features * support KeyObject inputs in WebCryptoAPI runtimes given compatibility ([e178b8f](https://github.com/panva/jose/commit/e178b8f0fbdd0313f5327a8e9e48ef7233060e0c)) ## [5.5.0](https://github.com/panva/jose/compare/v5.4.1...v5.5.0) (2024-06-26) ### Features * add experimental support for edge compute runtimes JWKS caching ([ab166e2](https://github.com/panva/jose/commit/ab166e207f1f2f63b1f1ab259e1280549b2e9097)), closes [#551](https://github.com/panva/jose/issues/551) [#661](https://github.com/panva/jose/issues/661) [#653](https://github.com/panva/jose/issues/653) [#415](https://github.com/panva/jose/issues/415) ## [5.4.1](https://github.com/panva/jose/compare/v5.4.0...v5.4.1) (2024-06-18) ### Fixes * ensure latest release on npm is v5.x ([a9b2a30](https://github.com/panva/jose/commit/a9b2a300947f9ab10d580748a3eda58207de4c62)) ## [5.4.0](https://github.com/panva/jose/compare/v5.3.0...v5.4.0) (2024-06-03) ### Features * expose JWT's payload in JWTClaimValidationFailed instances ([58bcffb](https://github.com/panva/jose/commit/58bcffbac735cc727fd81c36813f12fd6f58b695)), closes [#680](https://github.com/panva/jose/issues/680) ### Refactor * add explicit return types everywhere ([cc2b2d7](https://github.com/panva/jose/commit/cc2b2d7b76118d59b9e08c589dc33a45a6377f4a)) ## [5.3.0](https://github.com/panva/jose/compare/v5.2.4...v5.3.0) (2024-05-10) ### Features * allow observing remote JWKS resolver state and its manual reload ([fa8b639](https://github.com/panva/jose/commit/fa8b639c277e1694b08a08c7152341b22ec1725d)) ### Refactor * if should not be the only statement in else blocks ([a6b716b](https://github.com/panva/jose/commit/a6b716b13859c76e4c1f7e33c60574514c6c2504)) ## [5.2.4](https://github.com/panva/jose/compare/v5.2.3...v5.2.4) (2024-04-07) ### Refactor * use createLocalJWKSet instead of LocalJWKSet in createRemoteJWKSet ([a7c566c](https://github.com/panva/jose/commit/a7c566c61ccf3b62d2bd3a9e58a70e1d4d3c8b0c)) ## [5.2.3](https://github.com/panva/jose/compare/v5.2.2...v5.2.3) (2024-03-07) ### Refactor * move iv generation and optional outputs around ([05c4351](https://github.com/panva/jose/commit/05c4351be3a356da2a0e882fbbd8006f2725ec7b)) ## [5.2.2](https://github.com/panva/jose/compare/v5.2.1...v5.2.2) (2024-02-11) ### Fixes * **types:** iv and tag is optional in JSON serializations ([53019cd](https://github.com/panva/jose/commit/53019cd1fa3a4dc265d4868b9c626d4d6c832e86)) ## [5.2.1](https://github.com/panva/jose/compare/v5.2.0...v5.2.1) (2024-02-03) ### Fixes * **build:** refactor export targets for browser, node cjs, and node esm builds ([50cbc65](https://github.com/panva/jose/commit/50cbc65e165ea27b4ed08ee7fc5a747a17d235da)) ## [5.2.0](https://github.com/panva/jose/compare/v5.1.3...v5.2.0) (2023-12-24) ### Features * extend JWT NumericDate setter syntax ([ae363c3](https://github.com/panva/jose/commit/ae363c3c434fb040985f08da68ed02067d205cbe)) ## [5.1.3](https://github.com/panva/jose/compare/v5.1.2...v5.1.3) (2023-11-30) ## [5.1.2](https://github.com/panva/jose/compare/v5.1.1...v5.1.2) (2023-11-27) ### Fixes * do not mutate JWTVerifyOptions.requiredClaims ([1bf9cec](https://github.com/panva/jose/commit/1bf9cec024a4d01d989e15bb6e4b54e3940b4458)), closes [#610](https://github.com/panva/jose/issues/610) ## [5.1.1](https://github.com/panva/jose/compare/v5.1.0...v5.1.1) (2023-11-14) ### Refactor * deprecate the RSA1_5 JWE Algorithm ([f746da1](https://github.com/panva/jose/commit/f746da172b693eb417c4f75c8db6230cf213cd76)) ## [5.1.0](https://github.com/panva/jose/compare/v5.0.2...v5.1.0) (2023-11-03) ### Features * add payload generics to jose.decodeJwt ([9de49e2](https://github.com/panva/jose/commit/9de49e26956a20cdb94472f10f83b20480613329)), closes [#604](https://github.com/panva/jose/issues/604) ## [5.0.2](https://github.com/panva/jose/compare/v5.0.1...v5.0.2) (2023-11-02) ### Fixes * **createRemoteJWKSet:** ensure a default user-agent header is present ([887dd3c](https://github.com/panva/jose/commit/887dd3cd05f34e06ce20ad00201599a5a469fbac)), closes [#600](https://github.com/panva/jose/issues/600) ## [5.0.1](https://github.com/panva/jose/compare/v5.0.0...v5.0.1) (2023-10-25) ### Fixes * also use ES2020 in the CDN bundles ([8c4d390](https://github.com/panva/jose/commit/8c4d3909db56f2d62cf2bf413e8343c0fdd2b92f)) ## [5.0.0](https://github.com/panva/jose/compare/v4.15.4...v5.0.0) (2023-10-25) ### ⚠ BREAKING CHANGES * **Node.js:** return Uint8Array (not a Buffer) from base64url.decode * Browser distribution is now built using ES2020 as a target * Node.js distribution is now built using ES2022 as a target * **types:** jwtVerify and jwtDecrypt type argument for the resolved KeyLike type is now a second optional type argument following a type for the JWT Claims Set (aka payload) * PBES2 Key Management Algorithms' use in decrypt functions now requires the use of the keyManagementAlgorithms option to explicitly opt-in for their use. * importJWK "octAsKeyObject" option was removed. importJWK will no longer return CryptoKey or KeyObject for "oct" (octet sequence) JWK key types, it will instead always return a Uint8Array formed from the "k" (Key Value) Parameter regardless of the other JWK Parameters that may be present. * End-Of-Life versions of Node.js as of October 2023 are no longer supported. Node.js 18, 20, and 21 and future releases are the ones that remain supported. * The JWE "zip" (Compression Algorithm) Header Parameter is no longer supported by this JOSE implementation. ### Features * add Date as valid input to timestamp setting functions ([bd830a4](https://github.com/panva/jose/commit/bd830a47979912d4c0775d01a05584c2aa9f0dcd)) * default to an empty payload in JWT producing constructors ([98d6ca1](https://github.com/panva/jose/commit/98d6ca12c448697ed6342b1230b351eb5bfa0df8)) * **types:** add optional Generics for JWT verify and decrypt ([61bd2a0](https://github.com/panva/jose/commit/61bd2a0adb638c1c2469459d78556a99cec697c7)), closes [#568](https://github.com/panva/jose/issues/568) ### Reverts * Revert "test: fix test under lts/erbium" ([b64b6c7](https://github.com/panva/jose/commit/b64b6c731c3e2d0e6751e0221804af08d7015bfa)) ### Refactor * Browser distribution is now built using ES2020 as a target ([1836684](https://github.com/panva/jose/commit/18366840e1ae557b951fe921c5004b17ad56e972)) * drop support for EOL Node.js versions ([b5aee54](https://github.com/panva/jose/commit/b5aee542fb5995dd29e012011f832ce8dfd24e29)) * importJWK always returns a Uint8Array for symmetric key inputs ([163e1b0](https://github.com/panva/jose/commit/163e1b02ed5b64368110d750c9f5f5c3d247042d)) * Node.js distribution is now built using ES2022 as a target ([239697a](https://github.com/panva/jose/commit/239697a17d048b8eb2120d29adff7f98edc0f26e)) * **Node.js:** return Uint8Array (not a Buffer) from base64url.decode ([02d5182](https://github.com/panva/jose/commit/02d51827e24195d650cf83de100ae16cd8b0599e)) * PBES2 Algorithms require explicit opt-in during verification ([e2da031](https://github.com/panva/jose/commit/e2da031381b7c5327ea9a0ccf58f059fa8af7e92)) * remove support for JWE "zip" (Compression Algorithm) Header Parameter ([16998b1](https://github.com/panva/jose/commit/16998b15c75d90b64eb5b0fa0713cfdfa7896757)) * **types:** rename type parameters for the KeyLike returns ([eddd400](https://github.com/panva/jose/commit/eddd400235e84e3d84c1a8471b01915a12d3d866)) * update allow list error messages ([fe8114c](https://github.com/panva/jose/commit/fe8114c82646f2468857effb934f39dd7bc75902)) ## [4.15.4](https://github.com/panva/jose/compare/v4.15.3...v4.15.4) (2023-10-14) ### Fixes * **types:** export GetKeyFunction ([#592](https://github.com/panva/jose/issues/592)) ([936c9df](https://github.com/panva/jose/commit/936c9dff2bc124dc5f64906a96f665a28e57392c)), closes [#591](https://github.com/panva/jose/issues/591) ## [4.15.3](https://github.com/panva/jose/compare/v4.15.2...v4.15.3) (2023-10-11) ## [4.15.2](https://github.com/panva/jose/compare/v4.15.1...v4.15.2) (2023-10-04) ### Fixes * **build:** add a node target for jose-browser-runtime releases ([abb63d0](https://github.com/panva/jose/commit/abb63d0e8e7a55326dc343eec5f5eee9addc1dcf)) ## [4.15.1](https://github.com/panva/jose/compare/v4.15.0...v4.15.1) (2023-10-02) ### Fixes * resolve missing types for the cryptoRuntime const ([1627965](https://github.com/panva/jose/commit/16279652a67133fba0db7c9879767f000a8f1662)) ## [4.15.0](https://github.com/panva/jose/compare/v4.14.6...v4.15.0) (2023-10-02) ### Features * export the used crypto runtime as a constant ([0681dda](https://github.com/panva/jose/commit/0681dda1592a82c22a18981002b3763c502d0fc4)) ## [4.14.6](https://github.com/panva/jose/compare/v4.14.5...v4.14.6) (2023-09-04) ### Fixes * **build:** publish bundle and umd files with jose-browser-runtime module ([62fcbcc](https://github.com/panva/jose/commit/62fcbcc2170db00f5bbfc817839523dbf970239f)), closes [#571](https://github.com/panva/jose/issues/571) ## [4.14.5](https://github.com/panva/jose/compare/v4.14.4...v4.14.5) (2023-09-02) ### Refactor * catch type error when decoding base64url signature ([#569](https://github.com/panva/jose/issues/569)) ([935e920](https://github.com/panva/jose/commit/935e920d29d242e0446d365b1e4f0449d144c23c)) * catch type errors when decoding various base64url strings ([9024e87](https://github.com/panva/jose/commit/9024e870ece4ef121205dadc733c36d7978b97ab)) ## [4.14.4](https://github.com/panva/jose/compare/v4.14.3...v4.14.4) (2023-04-30) ### Refactor * cleanup NODE-ED25519 workerd workarounds ([072e83d](https://github.com/panva/jose/commit/072e83de5bf3a15775b0bf25ef8afa8851b8862d)) ## [4.14.3](https://github.com/panva/jose/compare/v4.14.2...v4.14.3) (2023-04-27) ### Reverts * Revert "fix(types): headers and payloads may only be JSON values and primitives" ([06d8101](https://github.com/panva/jose/commit/06d8101a5827a69bb25c2847b1a10d03f015db03)), closes [#534](https://github.com/panva/jose/issues/534) ## [4.14.2](https://github.com/panva/jose/compare/v4.14.1...v4.14.2) (2023-04-26) ### Fixes * **types:** headers and payloads may only be JSON values and primitives ([24f306e](https://github.com/panva/jose/commit/24f306e7f33485daaba1e250dfc97b5f621079ad)) ## [4.14.1](https://github.com/panva/jose/compare/v4.14.0...v4.14.1) (2023-04-20) ## [4.14.0](https://github.com/panva/jose/compare/v4.13.2...v4.14.0) (2023-04-14) ### Features * add requiredClaims JWT validation option ([eeea91d](https://github.com/panva/jose/commit/eeea91df48cadda84e4fdce6bbba7251ca7af83f)) ## [4.13.2](https://github.com/panva/jose/compare/v4.13.1...v4.13.2) (2023-04-12) ### Refactor * src/util/decode_protected_header.ts ([5716725](https://github.com/panva/jose/commit/5716725d7eb6fa8a416638db9d448840f839f620)) ## [4.13.1](https://github.com/panva/jose/compare/v4.13.0...v4.13.1) (2023-03-02) ### Fixes * **workerd:** avoid "The script will never generate a response" edge cases completely ([96a8c99](https://github.com/panva/jose/commit/96a8c99189f2399e9816ae1bca04b6d9cff93c26)), closes [#355](https://github.com/panva/jose/issues/355) [#509](https://github.com/panva/jose/issues/509) ## [4.13.0](https://github.com/panva/jose/compare/v4.12.2...v4.13.0) (2023-02-27) ### Features * **types:** allow generics to aid in CryptoKey or KeyObject narrowing of KeyLike ([6effa4d](https://github.com/panva/jose/commit/6effa4d35cfa984a5859d228f750e96af0c0a5e5)) ### Fixes * make jose.EmbeddedJWK arguments optional ([20610a9](https://github.com/panva/jose/commit/20610a930d337c25756de107d93b84ccc52707a3)) ## [4.12.2](https://github.com/panva/jose/compare/v4.12.1...v4.12.2) (2023-02-27) ### Fixes * **types:** declare explicit return from EmbeddedJWK ([46934ac](https://github.com/panva/jose/commit/46934ac474ba0119976c5ac15cce4ea7bf50de8c)) ## [4.12.1](https://github.com/panva/jose/compare/v4.12.0...v4.12.1) (2023-02-27) ### Refactor * clarify when alg is used and required on key imports ([19e525f](https://github.com/panva/jose/commit/19e525fdee04ba6281f70bd20523b878408aa7ee)) * **node:** have node:crypto deal with x509 parsing ([45bb45d](https://github.com/panva/jose/commit/45bb45d42b6c96cbfcab7242d5cc366fb34481f1)) ## [4.12.0](https://github.com/panva/jose/compare/v4.11.4...v4.12.0) (2023-02-15) ### Features * enable key iteration over JWKSMultipleMatchingKeys ([a278acd](https://github.com/panva/jose/commit/a278acdb0f458e555abdc1d048920e7da4fb7981)) ## [4.11.4](https://github.com/panva/jose/compare/v4.11.3...v4.11.4) (2023-02-07) ### Fixes * **build:** ignore deno files in npm publishes ([b3d6a11](https://github.com/panva/jose/commit/b3d6a11bf0803c37e1e9d0368ccec1f1264eef74)) ## [4.11.3](https://github.com/panva/jose/compare/v4.11.2...v4.11.3) (2023-02-07) ### Fixes * **CF Workers:** improve miniflare compat with different Node.js versions, get ready for future non-proprietary support ([3406b9f](https://github.com/panva/jose/commit/3406b9f73b1884b5db9c60675a68fe85794d48e0)), closes [#446](https://github.com/panva/jose/issues/446) [#495](https://github.com/panva/jose/issues/495) [#497](https://github.com/panva/jose/issues/497) ## [4.11.2](https://github.com/panva/jose/compare/v4.11.1...v4.11.2) (2023-01-01) ### Refactor * **node:** dry node version checks ([aff2f7c](https://github.com/panva/jose/commit/aff2f7c00f28b599ee72dd9f0a36c3783f1e195f)) ## [4.11.1](https://github.com/panva/jose/compare/v4.11.0...v4.11.1) (2022-11-22) ## [4.11.0](https://github.com/panva/jose/compare/v4.10.4...v4.11.0) (2022-11-08) ### Features * add bun as a supported runtime ([3a63631](https://github.com/panva/jose/commit/3a636318914866decd934d455d7c3789d304992c)) ### Fixes * respect JWK ext for symmetric keys ([20557fc](https://github.com/panva/jose/commit/20557fccf1ce0ebd7dd5d18cc33aa64d6f7b35ba)) ## [4.10.4](https://github.com/panva/jose/compare/v4.10.3...v4.10.4) (2022-10-28) ### Fixes * typo in importPKSC8 error message ([#468](https://github.com/panva/jose/issues/468)) ([746bc64](https://github.com/panva/jose/commit/746bc64675636f2a09a6745e71cba8a2bdf3718f)) * workaround for invalid use checks on CF Workers and Deno ([e4d04eb](https://github.com/panva/jose/commit/e4d04eb65f72041784d948eaa8432e4b64193729)) ## [4.10.3](https://github.com/panva/jose/compare/v4.10.2...v4.10.3) (2022-10-20) ## [4.10.2](https://github.com/panva/jose/compare/v4.10.1...v4.10.2) (2022-10-20) ## [4.10.1](https://github.com/panva/jose/compare/v4.10.0...v4.10.1) (2022-10-20) ## [4.10.0](https://github.com/panva/jose/compare/v4.9.3...v4.10.0) (2022-09-27) ### Features * Curve25519, and Curve448 support for WebCryptoAPI runtimes ([fea359a](https://github.com/panva/jose/commit/fea359a2055aa1b65170999a7f8e1bb23a3a1cb5)) ### Fixes * **importX509:** handle length encodings better ([47d0d77](https://github.com/panva/jose/commit/47d0d777a1ac90ff2ed0368fdab536db3d17aa8c)), closes [#459](https://github.com/panva/jose/issues/459) ## [4.9.3](https://github.com/panva/jose/compare/v4.9.2...v4.9.3) (2022-09-15) ### Refactor * update CEK length validation error message ([81a92a9](https://github.com/panva/jose/commit/81a92a9a9803022b82ea67577bde3fc0da3ecc6f)) * update key input validation error messages ([2eac34a](https://github.com/panva/jose/commit/2eac34aa8f02c800a5f0b944e03fbe681c962b9c)) * update keylike description for WinterCG ([6741679](https://github.com/panva/jose/commit/6741679936acf78f00c6effd559b4698cc92f123)) ## [4.9.2](https://github.com/panva/jose/compare/v4.9.1...v4.9.2) (2022-09-01) ### Fixes * limit default PBES2 alg's computational expense ([03d6d01](https://github.com/panva/jose/commit/03d6d013bf6e070e85adfe5731f526978e3e8e4d)) ## [4.9.1](https://github.com/panva/jose/compare/v4.9.0...v4.9.1) (2022-08-29) ### Fixes * **deno:** add a Deno package entrypoint ([9f3c459](https://github.com/panva/jose/commit/9f3c459e30b71eec54163d500edb59f5c72bf7c9)) ## [4.9.0](https://github.com/panva/jose/compare/v4.8.3...v4.9.0) (2022-08-17) ### Features * add support for RFC 9278 - JWK Thumbprint URI ([d06ce65](https://github.com/panva/jose/commit/d06ce654666c5f584716f39843534118407c14e0)) ### Refactor * consume some base64url decode errors ([#436](https://github.com/panva/jose/issues/436)) ([caaf2c3](https://github.com/panva/jose/commit/caaf2c38dc51209d7adc493029f416c61759b1b1)) * unify JOSENotSupported throw on key export ([fe5d093](https://github.com/panva/jose/commit/fe5d093bf74b812ecd3ee92d40dd02619e88e06c)) ## [4.8.3](https://github.com/panva/jose/compare/v4.8.1...v4.8.3) (2022-06-29) ## [4.8.1](https://github.com/panva/jose/compare/v4.8.0...v4.8.1) (2022-05-02) ### Fixes * **typescript:** add types export for nodenext module resolution ([#406](https://github.com/panva/jose/issues/406)) ([5a6d8f0](https://github.com/panva/jose/commit/5a6d8f0a2a3283bd1e832f1e71906d70f74c1262)) ## [4.8.0](https://github.com/panva/jose/compare/v4.7.0...v4.8.0) (2022-04-26) ### Features * add "worker" export in package.json ([#400](https://github.com/panva/jose/issues/400)) ([c58c80a](https://github.com/panva/jose/commit/c58c80ae98b7a55b3b95e72438040983ae9a23de)) * optional headers options for createRemoteJWKSet ([#397](https://github.com/panva/jose/issues/397)) ([b4612f5](https://github.com/panva/jose/commit/b4612f5d256b773ab7a1144ac839bdf0f8ccff53)) ## [4.7.0](https://github.com/panva/jose/compare/v4.6.2...v4.7.0) (2022-04-21) ### Features * add createRemoteJWKSet cacheMaxAge option ([5017d95](https://github.com/panva/jose/commit/5017d95764b3aca551631c1a2fbe7cc40cbb6055)), closes [#394](https://github.com/panva/jose/issues/394) ## [4.6.2](https://github.com/panva/jose/compare/v4.6.1...v4.6.2) (2022-04-19) ### Fixes * dont check JWT iat is in the past unless maxTokenAge is used ([96d85c7](https://github.com/panva/jose/commit/96d85c70033d2249de41ed07d97ed6843c15eb2a)) ## [4.6.1](https://github.com/panva/jose/compare/v4.6.0...v4.6.1) (2022-04-11) ## [4.6.0](https://github.com/panva/jose/compare/v4.5.3...v4.6.0) (2022-03-06) ### Features * mark APIs and parameters that can lead to footguns as deprecated ([0ddbcc6](https://github.com/panva/jose/commit/0ddbcc6725ecb2d68efdaf0951cec4db31cc9b16)) * **types:** include JSDoc in the types ([74187a9](https://github.com/panva/jose/commit/74187a9aa97cac70c42035949dd847177025af7c)) ## [4.5.3](https://github.com/panva/jose/compare/v4.5.2...v4.5.3) (2022-03-05) ### Fixes * **web api runtime:** rely on default fetch init values ([df6d966](https://github.com/panva/jose/commit/df6d96651d4ddeeb4a9b05bd2d778bd58528dad2)) ## [4.5.2](https://github.com/panva/jose/compare/v4.5.1...v4.5.2) (2022-03-04) ### Fixes * decrypting empty ciphertext compact JWEs ([#374](https://github.com/panva/jose/issues/374)) ([95fe597](https://github.com/panva/jose/commit/95fe59791dab9b31203f7a4ec5f4b44633d9b74f)) ## [4.5.1](https://github.com/panva/jose/compare/v4.5.0...v4.5.1) (2022-02-22) ### Fixes * **typescript:** allow synchronous get key functions ([7c99153](https://github.com/panva/jose/commit/7c99153a9e8ae45a35de7eff45fcf6e60e1b088b)) ## [4.5.0](https://github.com/panva/jose/compare/v4.4.0...v4.5.0) (2022-02-07) ### Features * add jose.decodeJwt utility ([3d2a2b8](https://github.com/panva/jose/commit/3d2a2b8eee18c9b60debbfae284b2bc3d2947dd2)) ### Fixes * concurrent fetch await in cloudflare ([e44cd18](https://github.com/panva/jose/commit/e44cd18ea4cf8af173f874ca3a847fc315eee592)), closes [#355](https://github.com/panva/jose/issues/355) ## [4.4.0](https://github.com/panva/jose/compare/v4.3.9...v4.4.0) (2022-01-24) ### Features * add createLocalJWKSet, resolver to verify using a local JWKSet ([bd7bf37](https://github.com/panva/jose/commit/bd7bf3789c146d765bbee2db0c93ba035020b24c)) ## [4.3.9](https://github.com/panva/jose/compare/v4.3.8...v4.3.9) (2022-01-22) ### Fixes * only add y to the epk header parameter when EC keys are used ([dd6775e](https://github.com/panva/jose/commit/dd6775eed00b60c14b7038ddec85c8bb3cf05781)), closes [#348](https://github.com/panva/jose/issues/348) ## [4.3.8](https://github.com/panva/jose/compare/v4.3.7...v4.3.8) (2022-01-09) ## [4.3.7](https://github.com/panva/jose/compare/v4.3.6...v4.3.7) (2021-11-18) ### Fixes * **typescript:** b64: true is fine to use in JWT, its useless, but allowed ([#324](https://github.com/panva/jose/issues/324)) ([ee401c9](https://github.com/panva/jose/commit/ee401c9e0f23f10ff5c0484798cb0cb3e9074b84)) ## [4.3.6](https://github.com/panva/jose/compare/v4.3.5...v4.3.6) (2021-11-16) ### Fixes * **electron:** rsa-pss keys are never supported ([188c1f7](https://github.com/panva/jose/commit/188c1f709002302da99105cfc8fc6863a95761d9)) ## [4.3.5](https://github.com/panva/jose/compare/v4.3.4...v4.3.5) (2021-11-12) ### Fixes * **typescript:** b64 header regression ([#324](https://github.com/panva/jose/issues/324)) ([9da0a7f](https://github.com/panva/jose/commit/9da0a7f49cf763314748eb01303320ce5af69762)) ## [4.3.4](https://github.com/panva/jose/compare/v4.3.3...v4.3.4) (2021-11-12) ### Fixes * Compact JWS verification handles a zero-length payload string ([7c70e7b](https://github.com/panva/jose/commit/7c70e7b9700886dfad8e7555b909da8e079c88da)) ## [4.3.3](https://github.com/panva/jose/compare/v4.3.2...v4.3.3) (2021-11-11) ### Fixes * **typescript:** apply updated compact and jwt headers to compact/jwt verify and decrypt results ([0c1946c](https://github.com/panva/jose/commit/0c1946c3e2a95e082b9a9095bf035756d8f17730)) ## [4.3.2](https://github.com/panva/jose/compare/v4.3.0...v4.3.2) (2021-11-11) ### Fixes * createRemoteJWKSet handles all JWS syntaxes ([aaba8f3](https://github.com/panva/jose/commit/aaba8f3000b76b41733567367b9000348a839c17)) * **typescript:** Compact JWS Header Parameters has alg and enc as required ([0fa87af](https://github.com/panva/jose/commit/0fa87af64b8e9f0f0cb68264f4dc22cc985acf91)) * **typescript:** Compact JWS Header Parameters has alg as required ([c7fabd0](https://github.com/panva/jose/commit/c7fabd0f012513f3c9161b0f59befae1d7430e16)) * **typescript:** Signed JWT Header Parameters has alg as required and b64 as never ([79cbd82](https://github.com/panva/jose/commit/79cbd82d3dd36f9ef87e4d306d77d9694a1c5836)) ## [4.3.0](https://github.com/panva/jose/compare/v4.2.1...v4.3.0) (2021-11-11) ### Features * add GeneralSign signature and GeneralEncrypt recipient builder chaining ([cfc93f5](https://github.com/panva/jose/commit/cfc93f5daf4729a189ef5caabae4a9ec9ad45378)) ## [4.2.1](https://github.com/panva/jose/compare/v4.2.0...v4.2.1) (2021-11-09) ### Fixes * **node:** dont mention CryptoKey in versions without webcrypto ([401cabf](https://github.com/panva/jose/commit/401cabf97419768cea1d685dc73d933fa38d6c26)) ## [4.2.0](https://github.com/panva/jose/compare/v4.1.5...v4.2.0) (2021-11-08) ### Features * General JWE Encryption ([94eca81](https://github.com/panva/jose/commit/94eca816872527074d2a591a983ee6c5d64da30c)) ## [4.1.5](https://github.com/panva/jose/compare/v4.1.4...v4.1.5) (2021-11-05) ### Fixes * importX509 certificate values that do not include a version number ([51a18b6](https://github.com/panva/jose/commit/51a18b675a771ed573047398f79cd6f70d8f9fec)), closes [#308](https://github.com/panva/jose/issues/308) ## [4.1.4](https://github.com/panva/jose/compare/v4.1.3...v4.1.4) (2021-11-01) ### Fixes * allow shorter HMAC secrets ([57126f1](https://github.com/panva/jose/commit/57126f1806493a2782647610c2a6b5d20ea3e516)) ## [4.1.3](https://github.com/panva/jose/compare/v4.1.2...v4.1.3) (2021-11-01) ### Fixes * **edge-functions:** don't use globalThis ([3952030](https://github.com/panva/jose/commit/39520302d078da2273b5a24f8254f6c221195c63)) ## [4.1.2](https://github.com/panva/jose/compare/v4.1.1...v4.1.2) (2021-10-25) ### Fixes * **build:** ensure cjs/esm specific packages have the right main entry ([2f4526a](https://github.com/panva/jose/commit/2f4526a22b9bd62727bdd825e326ef79695c8ea3)) ## [4.1.1](https://github.com/panva/jose/compare/v4.1.0...v4.1.1) (2021-10-21) ### Fixes * **typescript:** work around potentially missing global URL from DOM lib ([7ed731c](https://github.com/panva/jose/commit/7ed731c567db6e64f0fbd618efe7e48d812af0c6)), closes [#295](https://github.com/panva/jose/issues/295) ## [4.1.0](https://github.com/panva/jose/compare/v4.0.4...v4.1.0) (2021-10-18) ### Features * **web:** publish umd and bundle files to cdnjs.com ([3b3100a](https://github.com/panva/jose/commit/3b3100a8f115db5fb7c56482a0c5cf4814e0f838)) ## [4.0.4](https://github.com/panva/jose/compare/v4.0.3...v4.0.4) (2021-10-17) ### Fixes * **web:** check Uint8Array CEK lengths, refactor for better tree-shaking ([e8299f2](https://github.com/panva/jose/commit/e8299f246b1dbf1665d8f1ed141b9bde34684293)) ## [4.0.3](https://github.com/panva/jose/compare/v4.0.2...v4.0.3) (2021-10-16) ### Fixes * **web:** checking cryptokey applicability early ([89dc2aa](https://github.com/panva/jose/commit/89dc2aab99d831e922ba865eccfb29b8229ed767)) ## [4.0.2](https://github.com/panva/jose/compare/v4.0.1...v4.0.2) (2021-10-15) ### Fixes * **typescript:** export ProduceJWT ([#285](https://github.com/panva/jose/issues/285)) ([2b8738e](https://github.com/panva/jose/commit/2b8738e38a4286c9a1411e3aef3159f61427317c)) ## [4.0.1](https://github.com/panva/jose/compare/v4.0.0...v4.0.1) (2021-10-14) ### Fixes * **typescript:** re-export all types from index.d.ts ([d68f104](https://github.com/panva/jose/commit/d68f104d5895f639812b3317696a4616c3e5fb59)) ## [4.0.0](https://github.com/panva/jose/compare/v3.20.3...v4.0.0) (2021-10-14) ### ⚠ BREAKING CHANGES * All module named exports have moved from subpaths to just "jose". For example, `import { jwtVerify } from 'jose/jwt/verify'` is now just `import { jwtVerify } from 'jose'`. * All submodule default exports and named have been removed in favour of just "jose" named exports. * **typescript:** remove repeated type re-exports * The undocumented `jose/util/random` was removed. * The `jose/jwk/thumbprint` named export is renamed to `calculateJwkThumbprint`, now `import { calculateJwkThumbprint } from 'jose'` * The deprecated `jose/jwk/parse` module was removed, use `import { importJWK } from 'jose'` instead. * The deprecated `jose/jwk/from_key_like` module was removed, use `import { exportJWK } from 'jose'` instead. ### Refactor * redo exports to support broader tooling ([dd2cf9e](https://github.com/panva/jose/commit/dd2cf9ed2d89488de6dc4536f721887ffc9bb34f)) * remove util/random ([914e47f](https://github.com/panva/jose/commit/914e47fc9b6c207fd7e3469b1c3fac40f7a81031)) * removed the deprecated jwk/from_key_like module ([ec1d0e7](https://github.com/panva/jose/commit/ec1d0e72fe39ec2bccc28e46b5bce2dc17711134)) * removed the deprecated jwk/parse module ([8d3cc3b](https://github.com/panva/jose/commit/8d3cc3bb46e7e87e6511859dce58a651811ca551)) * rename calculateThumprint to calculateJwkThumbprint ([5afb713](https://github.com/panva/jose/commit/5afb713fbb99e6c884bb3b1c68ae2cf490e54595)) * **typescript:** remove repeated type re-exports ([3e137d2](https://github.com/panva/jose/commit/3e137d2427035d18397825074c2ee1e5db97515b)) ## [3.20.3](https://github.com/panva/jose/compare/v3.20.2...v3.20.3) (2021-10-14) ### Fixes * remove clutter when tree shaking browser dist ([73ba370](https://github.com/panva/jose/commit/73ba3708d45e32215c76f17d9982b0f4e20b7f08)) * **typescript:** JWTExpired error TS2417 ([373e0e4](https://github.com/panva/jose/commit/373e0e4b22fb48cefcf14385a19c5ea6a57a849e)) ## [3.20.2](https://github.com/panva/jose/compare/v3.20.1...v3.20.2) (2021-10-13) ### Fixes * allow tree-shaking of errors ([0824301](https://github.com/panva/jose/commit/08243010d922c36d22002e35299ec5710654c695)) ## [3.20.1](https://github.com/panva/jose/compare/v3.20.0...v3.20.1) (2021-10-06) ### Fixes * **typescript:** PEM import functions always resolve a KeyLike, never a Uint8Array ([8ef3a8e](https://github.com/panva/jose/commit/8ef3a8ebb78b592e664102cb593542ae6259d72a)) ## [3.20.0](https://github.com/panva/jose/compare/v3.19.0...v3.20.0) (2021-10-06) ### Features * improve key input type errors, remove dependency on @types/node ([a13eb04](https://github.com/panva/jose/commit/a13eb045d86d96e56f7a250cdc808f8c5aa0e62a)) ### Fixes * proper createRemoteJWKSet timeoutDuration handling ([efa1619](https://github.com/panva/jose/commit/efa16195173f9f66b21d4f41039caaad0ccfa92a)), closes [#277](https://github.com/panva/jose/issues/277) ## [3.19.0](https://github.com/panva/jose/compare/v3.18.0...v3.19.0) (2021-09-26) ### Features * return resolved key when verify and decrypt resolve functions are used ([49fb62c](https://github.com/panva/jose/commit/49fb62cb96cd9afc854f5102313f16e27c0eb2b4)) ## [3.18.0](https://github.com/panva/jose/compare/v3.17.0...v3.18.0) (2021-09-22) ### Features * add X.509/SPKI/PKCS8 key import and SPKI/PKCS8 export functions ([a2af0f4](https://github.com/panva/jose/commit/a2af0f45fe47b3d73178ab00c18e49fccd2b1432)) ## [3.17.0](https://github.com/panva/jose/compare/v3.16.1...v3.17.0) (2021-09-10) ### Features * **cloudflare workers:** add support for EdDSA using Ed25519 ([0967369](https://github.com/panva/jose/commit/09673694027ffc4961c211c12e0b7eb2ac9966f3)) ## [3.16.1](https://github.com/panva/jose/compare/v3.16.0...v3.16.1) (2021-09-08) ### Fixes * guard Sign payloads and Encrypt plaintext argument types ([10a18f2](https://github.com/panva/jose/commit/10a18f28a0f845e91579afab3573730c9b1ae478)) ## [3.16.0](https://github.com/panva/jose/compare/v3.15.5...v3.16.0) (2021-09-07) ### Features * **node:** support rsa-pss keys in Node.js >= 16.9.0 for sign/verify ([0b112cf](https://github.com/panva/jose/commit/0b112cf63ed2a859806531853c37486485740f9c)) ## [3.15.5](https://github.com/panva/jose/compare/v3.15.4...v3.15.5) (2021-09-02) ### Fixes * omit some fetch options when running in Cloudflare Workers env ([ced065a](https://github.com/panva/jose/commit/ced065aa9754c625ea88a598025962503e078ae9)), closes [#255](https://github.com/panva/jose/issues/255) ## [3.15.4](https://github.com/panva/jose/compare/v3.15.3...v3.15.4) (2021-08-20) ### Fixes * **deno:** ignore incomplete webcrypto api type errors ([c5f2262](https://github.com/panva/jose/commit/c5f226290ead93b7f43f664fc05c5fec90f38be8)) * **typescript:** generateKeyPair never returns Uint8Array ([73adc01](https://github.com/panva/jose/commit/73adc014ad9827067637153a97f230bfdd72cb9b)) ## [3.15.3](https://github.com/panva/jose/compare/v3.15.2...v3.15.3) (2021-08-20) ### Fixes * **typescript:** GeneralJWSInput and GeneralJWS omit ([bc0b42f](https://github.com/panva/jose/commit/bc0b42f0f58e802721910ac1bc4d62eb704910ff)) ## [3.15.2](https://github.com/panva/jose/compare/v3.15.1...v3.15.2) (2021-08-20) ## [3.15.1](https://github.com/panva/jose/compare/v3.15.0...v3.15.1) (2021-08-20) ### Fixes * **typescript:** remove file extensions from types/**/*.d.ts files ([0c432e5](https://github.com/panva/jose/commit/0c432e554e7b1f0382efefe44c0053a446c9dcc4)), closes [#222](https://github.com/panva/jose/issues/222) ## [3.15.0](https://github.com/panva/jose/compare/v3.14.4...v3.15.0) (2021-08-20) ### Features * experimental Deno build & publish ([5c7d265](https://github.com/panva/jose/commit/5c7d2656b6e5659a19c6cb3c4fed73e724fe2f6e)) ### Fixes * **typescript:** allow sign results to be passed to verify ([59aa96d](https://github.com/panva/jose/commit/59aa96d28dd259d9d8b03fcf37b5a703c5e36874)) ## [3.14.4](https://github.com/panva/jose/compare/v3.14.3...v3.14.4) (2021-08-16) ### Fixes * throw JWEInvalid when jwe protected header is invalid ([991d435](https://github.com/panva/jose/commit/991d4350d0357ebad17080644c24bccec844c3b9)) * throw JWSInvalid when jws protected header is invalid ([#244](https://github.com/panva/jose/issues/244)) ([1fc79aa](https://github.com/panva/jose/commit/1fc79aa8315fa25e28f63f1c5534d0630fc781dc)) ## [3.14.3](https://github.com/panva/jose/compare/v3.14.2...v3.14.3) (2021-07-21) ### Fixes * **docs:** update doc links again ([26c4361](https://github.com/panva/jose/commit/26c4361c007e3bc7e6ee60b65f9535cecf447fe6)) ## [3.14.2](https://github.com/panva/jose/compare/v3.14.1...v3.14.2) (2021-07-21) ### Fixes * **docs:** update doc links ([86f9134](https://github.com/panva/jose/commit/86f9134248a1746904f4c9f79ee404007ab68858)) ## [3.14.1](https://github.com/panva/jose/compare/v3.14.0...v3.14.1) (2021-07-21) ### Fixes * **typescript:** export generate key pair result interface ([2b5cc28](https://github.com/panva/jose/commit/2b5cc28684bd9cd09de2f774d7326bffe61fe6ea)) ## [3.14.0](https://github.com/panva/jose/compare/v3.13.0...v3.14.0) (2021-07-02) ### Features * add verbose key type error messages ([df56b94](https://github.com/panva/jose/commit/df56b942c64dfdbb14cb860a403742f25ec60b49)) ### Fixes * **typescript:** remove file extensions from .d.ts files ([e091f0f](https://github.com/panva/jose/commit/e091f0f24537541e350e803bd1e657348f428da2)), closes [#222](https://github.com/panva/jose/issues/222) * AES Key Wrap input type check ([b83821b](https://github.com/panva/jose/commit/b83821b2bf99fe2051d4d4d89fe4ff18a8559722)) * guard SignJWT.prototype.sign() from missing protected header ([4103719](https://github.com/panva/jose/commit/4103719c24d1811306acf7d5290ef15c5afddcfb)), closes [#221](https://github.com/panva/jose/issues/221) * **typescript:** add "jku" header to JoseHeaderParameters ([#220](https://github.com/panva/jose/issues/220)) ([72a72db](https://github.com/panva/jose/commit/72a72db7723e06994066d6ad154073387c5bc17c)) ## [3.13.0](https://github.com/panva/jose/compare/v3.12.3...v3.13.0) (2021-06-22) ### Features * **typescript:** export consume module interface types ([#213](https://github.com/panva/jose/issues/213)) ([13fa3d8](https://github.com/panva/jose/commit/13fa3d8ae089b21dace0ea22782451ca77941600)) ## [3.12.3](https://github.com/panva/jose/compare/v3.12.2...v3.12.3) (2021-06-02) ### Fixes * **browser:** remove the use of a node std-lib in decodeProtectedHeader ([d9d4a5f](https://github.com/panva/jose/commit/d9d4a5f2e88ca5172ff753a503bfbdb50522d094)), closes [#206](https://github.com/panva/jose/issues/206) ## [3.12.2](https://github.com/panva/jose/compare/v3.12.1...v3.12.2) (2021-05-19) ### Performance * **node:** use util.types.is* helpers when available ([d36311d](https://github.com/panva/jose/commit/d36311d5162b3500728937bf25bd2c756f8a33d6)) ## [3.12.1](https://github.com/panva/jose/compare/v3.12.0...v3.12.1) (2021-05-14) ### Fixes * **browser:** avoid global-conflicting variable name fetch ([#199](https://github.com/panva/jose/issues/199)) ([b2c6273](https://github.com/panva/jose/commit/b2c6273eccad5e34cbe0219c521c6453ba71e6c4)) ## [3.12.0](https://github.com/panva/jose/compare/v3.11.6...v3.12.0) (2021-05-12) ### Features * **webcrypto:** allow generate* modules extractable: false override ([afae428](https://github.com/panva/jose/commit/afae428f39eb920297ef474878d4266172d9a015)) ## [3.11.6](https://github.com/panva/jose/compare/v3.11.5...v3.11.6) (2021-04-30) ### Fixes * swallow promisified crypto.verify errors ([d512ede](https://github.com/panva/jose/commit/d512ede0730155051707d60ae8c69ba0492d858f)) ## [3.11.5](https://github.com/panva/jose/compare/v3.11.4...v3.11.5) (2021-04-13) ### Fixes * isObject helper in different vm contexts or jest re-assigned globals ([7819df7](https://github.com/panva/jose/commit/7819df73ebf6391377ef3e7623948d8329ac47f5)), closes [#178](https://github.com/panva/jose/issues/178) ## [3.11.4](https://github.com/panva/jose/compare/v3.11.3...v3.11.4) (2021-04-09) ### Fixes * defer AES CBC w/ HMAC decryption after tag verification passes ([579485c](https://github.com/panva/jose/commit/579485cb806e9989643e32a66752d3235cd43f09)) ## [3.11.3](https://github.com/panva/jose/compare/v3.11.2...v3.11.3) (2021-04-01) ### Fixes * **node:** check CryptoKey algorithm & usage before exporting KeyObject ([dab4b2f](https://github.com/panva/jose/commit/dab4b2f03efc5772773e66fdb757db5571deee4d)) ## [3.11.2](https://github.com/panva/jose/compare/v3.11.1...v3.11.2) (2021-03-30) ### Fixes * assert KeyLike input types, change "any" types to "unknown" ([edb83a8](https://github.com/panva/jose/commit/edb83a846a880d316d77ace485641330dd0debb6)) ## [3.11.1](https://github.com/panva/jose/compare/v3.11.0...v3.11.1) (2021-03-26) ### Fixes * **node:** crypto.verify callback invocation with a private keyobject ([d3d4acd](https://github.com/panva/jose/commit/d3d4acd8be612850999309ef7de86c549d5de9c0)) ## [3.11.0](https://github.com/panva/jose/compare/v3.10.0...v3.11.0) (2021-03-24) ### Features * export error codes as static properties ([89d8003](https://github.com/panva/jose/commit/89d80038755be21228a3455a8feca396e76fbcf5)), closes [#170](https://github.com/panva/jose/issues/170) ## [3.10.0](https://github.com/panva/jose/compare/v3.9.0...v3.10.0) (2021-03-18) ### Features * **node:** use libuv threadpool to sign in node >= 15.12.0 ([cf5074e](https://github.com/panva/jose/commit/cf5074e7e1333728f7632ee6785cc52ef32711bf)) * **node:** use libuv threadpool to verify in node >= 15.12.0 ([ae9a7f4](https://github.com/panva/jose/commit/ae9a7f4186da9675820dc2e77786b9ee3f7dd0d0)) * **node:** use native JWK export in node >= 15.9.0 ([7f3cc44](https://github.com/panva/jose/commit/7f3cc44bd0508bf15c061500738473eeafdc32d1)) * **node:** use native JWK import in node >= 15.12.0 ([f0c2a64](https://github.com/panva/jose/commit/f0c2a6472844c43a92a79ed90b51cc5133a2e22e)) ## [3.9.0](https://github.com/panva/jose/compare/v3.8.0...v3.9.0) (2021-03-15) ### Features * add named exports for all modules ([5cba6b0](https://github.com/panva/jose/commit/5cba6b0fdddd24c2e48623d8aaf48640b3279a43)) ## [3.8.0](https://github.com/panva/jose/compare/v3.7.1...v3.8.0) (2021-03-12) ### Features * publish alternative Node.js and Browser specific distributions ([7856dad](https://github.com/panva/jose/commit/7856dad1031845bfc3cadfdbe609d0f0154f19ce)) ## [3.7.1](https://github.com/panva/jose/compare/v3.7.0...v3.7.1) (2021-03-11) ### Fixes * swallow invalid signature encoding errors ([e0adf49](https://github.com/panva/jose/commit/e0adf49e5789f9fc23afb1e2bd3e330e34b46b78)) ## [3.7.0](https://github.com/panva/jose/compare/v3.6.2...v3.7.0) (2021-03-02) ### Features * electron >=12.0.0 is now supported (and tested on ci) ([8fffd3e](https://github.com/panva/jose/commit/8fffd3e2e1ec0c5f3517a779b42974a4c1beae27)) ### Fixes * **electron:** only call (de)cipher.setAAD() when aad is not empty ([a5a6c4d](https://github.com/panva/jose/commit/a5a6c4dc9f459b88de5f243cf1d4ea620def8d98)) * **electron:** properly ASN.1 encode [0x00] when converting RSA JWKs ([433f020](https://github.com/panva/jose/commit/433f020246a9131f63705a3e1aa99492dac50947)) ## [3.6.2](https://github.com/panva/jose/compare/v3.6.1...v3.6.2) (2021-02-16) ### Fixes * **typescript:** update maxTokenAge type and examples ([2c358e0](https://github.com/panva/jose/commit/2c358e0ea550f19896ccf43724ee8224aa04a664)) ## [3.6.1](https://github.com/panva/jose/compare/v3.6.0...v3.6.1) (2021-02-10) ### Fixes * node runtime json fetch handles connection errors properly ([fc584b2](https://github.com/panva/jose/commit/fc584b2efd9a6e7bf2ac83c6fb0ddf96fb0ca6a5)) ## [3.6.0](https://github.com/panva/jose/compare/v3.5.4...v3.6.0) (2021-02-04) ### Features * allow CryptoKey instances in a regular non-webcrypto node runtime ([e8d41a9](https://github.com/panva/jose/commit/e8d41a933582495c9a9b02d6ec38b46bef8795e1)) ## [3.5.4](https://github.com/panva/jose/compare/v3.5.3...v3.5.4) (2021-01-26) ### Fixes * export package.json ([8c29107](https://github.com/panva/jose/commit/8c29107aea26a54869d8adadceaf0bbf70fb18cd)), closes [#157](https://github.com/panva/jose/issues/157) ## [3.5.3](https://github.com/panva/jose/compare/v3.5.2...v3.5.3) (2021-01-20) ### Fixes * workaround downstream dependency issues messing with http ([2e58005](https://github.com/panva/jose/commit/2e5800535ab72ab35f3abfaab7493163d8b0494e)), closes [#154](https://github.com/panva/jose/issues/154) ## [3.5.2](https://github.com/panva/jose/compare/v3.5.1...v3.5.2) (2021-01-18) ### Performance * use 'base64url' encoding when available in Node.js runtime ([808f06c](https://github.com/panva/jose/commit/808f06cd08b10cf53343afb35802cc6e5b95ea20)) * use KeyObject.prototype asymmetricKeyDetails when available ([ad88ee2](https://github.com/panva/jose/commit/ad88ee2cd5bcaee3c3e5ec79735c8172ae2725be)) ## [3.5.1](https://github.com/panva/jose/compare/v3.5.0...v3.5.1) (2021-01-10) ### Fixes * workaround for RangeError in browser runtime base64url ([ed32b0d](https://github.com/panva/jose/commit/ed32b0d46ee570e405e0d88b43aecd8ef6fea129)) ## [3.5.0](https://github.com/panva/jose/compare/v3.4.0...v3.5.0) (2020-12-17) ### Features * added JWE General JSON Serialization decryption ([16dea9e](https://github.com/panva/jose/commit/16dea9ec7d6179471f794a3463bba0c6e77295ff)) ## [3.4.0](https://github.com/panva/jose/compare/v3.3.2...v3.4.0) (2020-12-16) ### Features * added JWS General JSON Serialization signing ([6fb862c](https://github.com/panva/jose/commit/6fb862cf12d34b7dc5077d1872ad29eeac27d21e)), closes [#129](https://github.com/panva/jose/issues/129) * added JWS General JSON Serialization verification ([55b7781](https://github.com/panva/jose/commit/55b77810d03a1f7e38e13bec384dece08b74b206)), closes [#129](https://github.com/panva/jose/issues/129) * added utility function for decoding token's protected header ([fa29d68](https://github.com/panva/jose/commit/fa29d68cfdf0922c7e4dac24eb50161d1eab28d4)) ## [3.3.2](https://github.com/panva/jose/compare/v3.3.1...v3.3.2) (2020-12-14) ### Fixes * **typescript:** ref dom lib via triple-slash to fix some compile issues ([175f273](https://github.com/panva/jose/commit/175f273819785c29b9ad822dcb5d70073523f504)), closes [#126](https://github.com/panva/jose/issues/126) ## [3.3.1](https://github.com/panva/jose/compare/v3.3.0...v3.3.1) (2020-12-06) ### Fixes * botched v3.3.0 release ([1c3e116](https://github.com/panva/jose/commit/1c3e116976c997f205b917405f010b568d1bd3b9)) ## [3.3.0](https://github.com/panva/jose/compare/v3.2.0...v3.3.0) (2020-12-06) ### Features * support recognizing proprietary `crit` header parameters ([5163116](https://github.com/panva/jose/commit/5163116ca1c091871ed0c601c9fbc1dbe94599cd)), closes [#123](https://github.com/panva/jose/issues/123) ### Fixes * reject JWTs with b64: false ([691b44a](https://github.com/panva/jose/commit/691b44ad4717c82a06539facfedff48fa0e9c6a9)) ## [3.2.0](https://github.com/panva/jose/compare/v3.1.3...v3.2.0) (2020-12-02) ### Features * allow specifying modulusLength when generating RSA Key Pairs ([5f7a0e9](https://github.com/panva/jose/commit/5f7a0e9055256bce4786a53711bcf14cf59fa8f1)), closes [#121](https://github.com/panva/jose/issues/121) ## [3.1.3](https://github.com/panva/jose/compare/v3.1.2...v3.1.3) (2020-11-26) ### Fixes * **typescript:** refactored how types are published ([2937363](https://github.com/panva/jose/commit/29373633bc540ff1e7bfe8fb3e5c5b391e79c2d9)), closes [#119](https://github.com/panva/jose/issues/119) ## [3.1.2](https://github.com/panva/jose/compare/v3.1.1...v3.1.2) (2020-11-24) ### Fixes * handle globalThis undefined in legacy browsers ([b83c59b](https://github.com/panva/jose/commit/b83c59bb43ad14ac932cd0c662f7dfc2c4c62753)) ## [3.1.1](https://github.com/panva/jose/compare/v3.1.0...v3.1.1) (2020-11-24) ### Fixes * global detection in a browser worker runtime ([56ff8fa](https://github.com/panva/jose/commit/56ff8fa65aa045411c6c6a67d80b67c1099576a0)) ## [3.1.0](https://github.com/panva/jose/compare/v3.0.2...v3.1.0) (2020-11-22) ### Features * added "KeyLike to JWK" module ([7a8418e](https://github.com/panva/jose/commit/7a8418eadd68b645fb7edf78873a35980ea8e41d)), closes [#109](https://github.com/panva/jose/issues/109) * allow compact verify/decrypt tokens to be uint8array encoded ([e39c3db](https://github.com/panva/jose/commit/e39c3dba75e5ae70697e6a4f93096c492a265c07)) * allow http.Agent and https.Agent passed in remote JWK Set ([38494a8](https://github.com/panva/jose/commit/38494a88828a8df2015efa78ca29c1a6317a3a50)) ## [3.0.2](https://github.com/panva/jose/compare/v3.0.1...v3.0.2) (2020-11-15) ### Fixes * **build:** publish esm submodules ([7b6364f](https://github.com/panva/jose/commit/7b6364f26f7654368c9e33af58043ee40e77ec77)), closes [#104](https://github.com/panva/jose/issues/104) ## [3.0.1](https://github.com/panva/jose/compare/v3.0.0...v3.0.1) (2020-11-15) ### Fixes * **typescript:** fix compiling by adding .d.ts files for runtime modules ([d9cb573](https://github.com/panva/jose/commit/d9cb5734d779df26c3e717a9f4f23d18b856dc5f)) ## [3.0.0](https://github.com/panva/jose/compare/v2.0.3...v3.0.0) (2020-11-14) ### ⚠ BREAKING CHANGES * Revised, Promise-based API * No dependencies * Browser support (using [Web Cryptography API](https://www.w3.org/TR/WebCryptoAPI/)) * Support for verification using a remote JWKS endpoint ### Features * Revised API, No dependencies, Browser Support, Promises ([357fe0b](https://github.com/panva/jose/commit/357fe0b964903e8c84ab49f0f27ddf0447d44c84)) ## [2.0.3](https://github.com/panva/jose/compare/v2.0.2...v2.0.3) (2020-10-29) ### Fixes * allow stubbing of the JWT.decode function ([6c3b92f](https://github.com/panva/jose/commit/6c3b92f4394a5d7092d7336922eda61e311e6f8c)) ## [2.0.2](https://github.com/panva/jose/compare/v2.0.1...v2.0.2) (2020-09-14) ### Fixes * **esm:** include esm files in the published package ([1956746](https://github.com/panva/jose/commit/1956746df6542c00bc33af750f93394805b5d603)) ## [2.0.1](https://github.com/panva/jose/compare/v2.0.0...v2.0.1) (2020-09-10) ### Fixes * allow plugins such as jose-chacha to work in newer node runtime ([30f1dc2](https://github.com/panva/jose/commit/30f1dc2c41e5554d322167b84b610a99bf5e69c5)) ## [2.0.0](https://github.com/panva/jose/compare/v1.28.0...v2.0.0) (2020-09-08) ### ⚠ BREAKING CHANGES * the `JWE.decrypt` option `algorithms` was removed and replaced with contentEncryptionAlgorithms (handles `enc` allowlist) and keyManagementAlgorithms (handles `alg` allowlist) * the `JWT.verify` profile option was removed, use e.g. `JWT.IdToken.verify` instead. * removed the `maxAuthAge` `JWT.verify` option, this option is now only present at the specific JWT profile APIs where the `auth_time` property applies. * removed the `nonce` `JWT.verify` option, this option is now only present at the specific JWT profile APIs where the `nonce` property applies. * the `acr`, `amr`, `nonce` and `azp` claim value types will only be checked when verifying a specific JWT profile using its dedicated API. * using the draft implementing APIs will emit a one-time warning per process using `process.emitWarning` * `JWT.sign` function options no longer accept a `nonce` property. To create a JWT with a `nonce` just pass the value to the payload. * due to added ESM module support Node.js version with ESM implementation bugs are no longer supported, this only affects early v13.x versions. The resulting Node.js semver range is `>=10.13.0 < 13 || >=13.7.0` * deprecated method `JWK.importKey` was removed * deprecated method `JWKS.KeyStore.fromJWKS` was removed * the use of unregistered curve name P-256K for secp256k1 was removed * jose.JWE.Encrypt constructor aad and unprotectedHeader arguments swapped places * jose.JWE.encrypt.flattened header (unprotectedHeader) and aad arguments swapped places * jose.JWE.encrypt.general header (unprotectedHeader) and aad arguments swapped places * JWS.verify returned payloads are now always buffers * JWS.verify options `encoding` and `parse` were removed ### Features * added support for ESM (ECMAScript modules) ([1aa9035](https://github.com/panva/jose/commit/1aa9035552bbcb34b95e092d0f082cc6d94465ab)) * decrypt allowlists for both key management and content encryption ([30e5c46](https://github.com/panva/jose/commit/30e5c46ecf00a498e65a551ced88bc897531c2a4)) ### Fixes * **typescript:** allow Buffer when verifying detached signature ([cadbd04](https://github.com/panva/jose/commit/cadbd047ca953d6d8171439f2efd7bb98a5d8e73)) * **typescript:** properly type all decode/verify/decrypt fn options ([4c23bd6](https://github.com/panva/jose/commit/4c23bd65fe6fa634726a5eb73c6d590f7348a97e)) ### Refactor * encrypt APIs unprotectedHeader and aad arguments swapped ([70bd4ae](https://github.com/panva/jose/commit/70bd4ae6b2e6ba94bbe0b3dc1a17b2990af3a18b)) * move JWT profile specifics outside of generic JWT ([fd69d7f](https://github.com/panva/jose/commit/fd69d7f5093d0b3a231d7d79aa3bca3a8a64464c)) * removed `nonce` option from `JWT.sign` ([c4267cc](https://github.com/panva/jose/commit/c4267cc655bc2721d846c98f8a40640d1a12e9ad)) * removed deprecated methods and utilities ([6c35c51](https://github.com/panva/jose/commit/6c35c519c9181f8246b36ad02572adb609d6de1d)) * removed payload parsing from JWS.verify ([ba5c897](https://github.com/panva/jose/commit/ba5c89791915a2a3cd56b3dab1f3328778152d33)) ## [1.28.0](https://github.com/panva/jose/compare/v1.27.3...v1.28.0) (2020-08-10) ### Features * support for validating issuer from a list of values ([#91](https://github.com/panva/jose/issues/91)) ([ce6836a](https://github.com/panva/jose/commit/ce6836af88c9e73c29560233f15ed1760c7dcc13)) ## [1.27.3](https://github.com/panva/jose/compare/v1.27.2...v1.27.3) (2020-08-04) ### Fixes * do not mutate unencoded payload when signing for multiple parties ([1695423](https://github.com/panva/jose/commit/169542363f884e4028db9f80086d631e626eb469)), closes [#89](https://github.com/panva/jose/issues/89) * ensure "b64" is the same for all recipients edge cases ([d56ec9f](https://github.com/panva/jose/commit/d56ec9f5ddc2612e5ff21fe35d45a56e7153e0e4)) ## [1.27.2](https://github.com/panva/jose/compare/v1.27.1...v1.27.2) (2020-07-01) ### Fixes * handle private EC keys without public component ([#86](https://github.com/panva/jose/issues/86)) ([e8ad389](https://github.com/panva/jose/commit/e8ad38993e29747098f7fd1594dde4ce893ba802)), closes [#85](https://github.com/panva/jose/issues/85) ## [1.27.1](https://github.com/panva/jose/compare/v1.27.0...v1.27.1) (2020-06-01) ### Fixes * allow any JSON numeric value for timestamp values ([7ba4922](https://github.com/panva/jose/commit/7ba492237aaf788914166c134d50fb046041efa0)) ## [1.27.0](https://github.com/panva/jose/compare/v1.26.1...v1.27.0) (2020-05-05) ### Features * add opt-in objects to verify using embedded JWS Header public keys ([7c1cab1](https://github.com/panva/jose/commit/7c1cab196edc409ec6cc4741bdf7e06c5aaf5dab)) ## [1.26.1](https://github.com/panva/jose/compare/v1.26.0...v1.26.1) (2020-04-27) ### Fixes * **typescript:** types of key generate functions without overloads ([7e60722](https://github.com/panva/jose/commit/7e60722ae7054f8acf833e015c22679d56fbc0ca)), closes [#80](https://github.com/panva/jose/issues/80) * "typ" content-type validation, case insensitive and handled prefix ([0691586](https://github.com/panva/jose/commit/06915861b32c0ae252dcc84791050bc3716ce102)) ## [1.26.0](https://github.com/panva/jose/compare/v1.25.2...v1.26.0) (2020-04-16) ### Features * update JWT Profile for OAuth 2.0 Access Tokens to latest draft ([8c0a8a9](https://github.com/panva/jose/commit/8c0a8a950e4503cb7a756589e307286fe1116b05)) ### BREAKING CHANGES * `at+JWT` JWT draft profile - in the draft's Section 2.2 the claims `iat` and `jti` are now REQUIRED (was RECOMMENDED). ## [1.25.2](https://github.com/panva/jose/compare/v1.25.1...v1.25.2) (2020-04-15) ### Fixes * **build:** don't publish junk files ([6e98c1a](https://github.com/panva/jose/commit/6e98c1a5f994224b9412fc47c4065b468c89fe2c)) ## [1.25.1](https://github.com/panva/jose/compare/v1.25.0...v1.25.1) (2020-04-15) ### Fixes * use native openssl AES Key Wrap 🤦 ([dcf8d75](https://github.com/panva/jose/commit/dcf8d75a8aca4f05fe04df64fdd2ba50bbc75bc9)) ## [1.25.0](https://github.com/panva/jose/compare/v1.24.1...v1.25.0) (2020-03-11) ### Features * update JWT Profile for OAuth 2.0 Access Tokens to latest draft ([bc77a15](https://github.com/panva/jose/commit/bc77a15fab10f8a29561ef667a923b2f074fa9b3)) ## [1.24.1](https://github.com/panva/jose/compare/v1.24.0...v1.24.1) (2020-03-05) ### Fixes * allow importing simpler passphrases as `oct` keys ([f86bda3](https://github.com/panva/jose/commit/f86bda3bb709f29e4264fb8de45242f518128744)) ## [1.24.0](https://github.com/panva/jose/compare/v1.23.0...v1.24.0) (2020-02-25) ### Features * add JWT.verify "typ" option for checking JWT Type Header parameter ([fc08426](https://github.com/panva/jose/commit/fc08426466233709b442ba21232768ddeeb94e56)) ## [1.23.0](https://github.com/panva/jose/compare/v1.22.2...v1.23.0) (2020-02-18) ### Fixes * **typescript:** add optional JWK.Key props and make them readonly ([b92079c](https://github.com/panva/jose/commit/b92079cb64216b8ea91082adc07ac03972dbbb0e)), closes [#67](https://github.com/panva/jose/issues/67) ### Features * add ECDH-ES with X25519 and X448 OKP keys ([38369ea](https://github.com/panva/jose/commit/38369ea3d72812abe7ecebd6dc7da164b0a2e29d)) * add RSA-OAEP-384 and RSA-OAEP-512 JWE Key Management Algorithms ([7477f08](https://github.com/panva/jose/commit/7477f0831b38765a9a916b35b1d40aaf11f0e6b8)) ## [1.22.2](https://github.com/panva/jose/compare/v1.22.1...v1.22.2) (2020-02-06) ### Performance Improvements * various codepaths refactored ([3e3d7dd](https://github.com/panva/jose/commit/3e3d7dd38168159e188e54c48a9f83e3a02a8fe1)) ## [1.22.1](https://github.com/panva/jose/compare/v1.22.0...v1.22.1) (2020-02-03) ### Fixes * actually remove the base64url proper encoding check ([eae01b5](https://github.com/panva/jose/commit/eae01b57ab9f33e8c621ffcd2a77d513a51d22b2)) ## [1.22.0](https://github.com/panva/jose/compare/v1.21.1...v1.22.0) (2020-01-29) ### Features * keystore filtering by JWK Key thumbprint ([a9f6f71](https://github.com/panva/jose/commit/a9f6f7135005d6231d6f42d95c02414139a89d17)) ### Performance Improvements * base64url decode, JWT.verify, JWK.Key instance re-use ([470b4c7](https://github.com/panva/jose/commit/470b4c73154e1fcf8b92726d521940e5e11c9d94)) ## [1.21.1](https://github.com/panva/jose/compare/v1.21.0...v1.21.1) (2020-01-25) ### Fixes * contactKDF iteration count fixed for key sizes larger than 256 bits ([70ff222](https://github.com/panva/jose/commit/70ff22227ad303e57228dc8351688531499a833a)) ## [1.21.0](https://github.com/panva/jose/compare/v1.20.0...v1.21.0) (2020-01-23) ### Fixes * **typescript:** don't expose non existant classes, fix decode key ([0f8bf88](https://github.com/panva/jose/commit/0f8bf886da1b5d02cd0d968d0ec02a58673df258)) ### Features * add opt-in support for Unsecured JWS algorithm "none" ([3a6d17f](https://github.com/panva/jose/commit/3a6d17fdd18d8bbd074c07c2dd08f0406c16a8f1)) ## [1.20.0](https://github.com/panva/jose/compare/v1.19.0...v1.20.0) (2020-01-16) ### Features * add JWTExpired error and JWTClaimInvalid claim and reason props ([a0c0c7a](https://github.com/panva/jose/commit/a0c0c7ad70f42d9b23b3e71de43599a8ac6fe1ff)), closes [#62](https://github.com/panva/jose/issues/62) ## [1.19.0](https://github.com/panva/jose/compare/v1.18.2...v1.19.0) (2020-01-13) ### Features * exposed shorthands for JWT verification profiles ([b1864e3](https://github.com/panva/jose/commit/b1864e319d1a7a42eadfa0c4b0145952e7814726)) ## [1.18.2](https://github.com/panva/jose/compare/v1.18.1...v1.18.2) (2020-01-08) ### Fixes * ensure asn1.js version to remove Buffer deprecation notice ([13b1106](https://github.com/panva/jose/commit/13b1106048fdeae00b09d54f05245dded85b14a7)) * expose JOSENotSupported key import errors on unsupported runtimes ([bc81e5d](https://github.com/panva/jose/commit/bc81e5dec2987f6ce6dc3fa5daa23dfe620c0a34)) * typo in JOSENotSupported error when x509 certs are not supported ([bb58c9c](https://github.com/panva/jose/commit/bb58c9ce52e807ca4cfad6bcbf1ab96b91778b1f)) ## [1.18.1](https://github.com/panva/jose/compare/v1.18.0...v1.18.1) (2020-01-01) ### Fixes * force iat past check when maxTokenAge option is used + JWT refactor ([828ad5a](https://github.com/panva/jose/commit/828ad5a33dc0cc0049923b69f43f97463295456e)) ## [1.18.0](https://github.com/panva/jose/compare/v1.17.2...v1.18.0) (2019-12-31) ### Features * add JWT validation profiles for Access Tokens and Logout Tokens ([7bb5c95](https://github.com/panva/jose/commit/7bb5c953a9c6d9bd915e8ebc0608bc0649427745)) ## [1.17.2](https://github.com/panva/jose/compare/v1.17.1...v1.17.2) (2019-12-17) ### Fixes * skip validating iat is in the past when exp is present ([0ed5025](https://github.com/panva/jose/commit/0ed5025de30a754de95ae2587ce0f4573909b006)) ## [1.17.1](https://github.com/panva/jose/compare/v1.17.0...v1.17.1) (2019-12-10) ### Fixes * properly fail to import unsupported openssh keys ([bee5744](https://github.com/panva/jose/commit/bee574457f29597ccab09d51ac61b85dd7a7146a)) ## [1.17.0](https://github.com/panva/jose/compare/v1.16.2...v1.17.0) (2019-12-10) ### Features * importing a certificate populates x5c and x5t thumbprints ([25a7a71](https://github.com/panva/jose/commit/25a7a71915c4f7514536cec9e7e162d0ad3b670c)), closes [#59](https://github.com/panva/jose/issues/59) ## [1.16.2](https://github.com/panva/jose/compare/v1.16.1...v1.16.2) (2019-12-05) ### Fixes * handle Unencoded Payload (b64:false) with arbitrary buffer payloads ([daabedc](https://github.com/panva/jose/commit/daabedc776617f4fde427b3a5e79d8c176293132)), closes [#57](https://github.com/panva/jose/issues/57) ## [1.16.1](https://github.com/panva/jose/compare/v1.16.0...v1.16.1) (2019-12-05) ### Fixes * allow PBES2 for the correct JWK `use` values ([f0d7194](https://github.com/panva/jose/commit/f0d719416ec9ca041ea88b8a983b5d899a6aa107)) ## [1.16.0](https://github.com/panva/jose/compare/v1.15.1...v1.16.0) (2019-12-04) ### Features * two official jose plugins/extensions for those living on the edge ([5b27c97](https://github.com/panva/jose/commit/5b27c97ac8836ffa9f3880e009c8db5afbfbaa2c)), closes [#56](https://github.com/panva/jose/issues/56) ## [1.15.1](https://github.com/panva/jose/compare/v1.15.0...v1.15.1) (2019-11-30) ### Fixes * **typescript:** export Key Input types ([0277fcd](https://github.com/panva/jose/commit/0277fcd1896af497e79190212b0719f7e62366c1)) ## [1.15.0](https://github.com/panva/jose/compare/v1.14.0...v1.15.0) (2019-11-27) ### Fixes * default JWT.sign `kid` option value is false for HMAC signatures ([ce77388](https://github.com/panva/jose/commit/ce7738825403f8cdb8f99cb51c096baf0dfa3af7)) ### Features * allow JWK.asKey inputs for sign/verify/encrypt/decrypt operations ([5e1009a](https://github.com/panva/jose/commit/5e1009a63e4bc829009cc46d6295c00f8431024c)) ## [1.14.0](https://github.com/panva/jose/compare/v1.13.0...v1.14.0) (2019-11-26) ### Features * allow JWKS.KeyStore .all and .get to filter for key curves ([ea60338](https://github.com/panva/jose/commit/ea60338ca6f58f2626992a38da76812477ce4540)) ## [1.13.0](https://github.com/panva/jose/compare/v1.12.1...v1.13.0) (2019-11-23) ### Features * return the CEK from JWE.decrypt operation with { complete: true } ([c3eb845](https://github.com/panva/jose/commit/c3eb8450b98b2f5ecc127d69afe85a7ae2cc5aaa)) ## [1.12.1](https://github.com/panva/jose/compare/v1.12.0...v1.12.1) (2019-11-14) ## [1.12.0](https://github.com/panva/jose/compare/v1.11.0...v1.12.0) (2019-11-05) ### Features * add JWS.verify encoding and parsing options ([6bb66d4](https://github.com/panva/jose/commit/6bb66d4f0b4c96f2da8ac5f14fda6bc4f53f2994)) ## [1.11.0](https://github.com/panva/jose/compare/v1.10.2...v1.11.0) (2019-11-03) ### Features * expose crypto.KeyObject instances in supported runtimes ([8ea9683](https://github.com/panva/jose/commit/8ea968312e97ed0f992fab909a20e7993159ec45)) ## [1.10.2](https://github.com/panva/jose/compare/v1.10.1...v1.10.2) (2019-10-29) ### Fixes * only use secp256k1 keys for signing/verification ([9588223](https://github.com/panva/jose/commit/95882232d6d409a321b6a8c168f5b78ebbdabf95)) ## [1.10.1](https://github.com/panva/jose/compare/v1.10.0...v1.10.1) (2019-10-04) ### Fixes * throw proper error when runtime doesn't support OKP ([0a16efb](https://github.com/panva/jose/commit/0a16efb)), closes [#48](https://github.com/panva/jose/issues/48) ## [1.10.0](https://github.com/panva/jose/compare/v1.9.2...v1.10.0) (2019-10-01) ### Features * rename package ([26f4cf2](https://github.com/panva/jose/commit/26f4cf2)) ## [1.9.2](https://github.com/panva/jose/compare/v1.9.1...v1.9.2) (2019-09-16) ### Fixes * keystore.toJWKS(true) does not throw on public keys ([81abdfa](https://github.com/panva/jose/commit/81abdfa)), closes [#42](https://github.com/panva/jose/issues/42) ## [1.9.1](https://github.com/panva/jose/compare/v1.9.0...v1.9.1) (2019-09-10) ## [1.9.0](https://github.com/panva/jose/compare/v1.8.0...v1.9.0) (2019-08-24) ### Features * allow JWKS.asKeyStore to swallow errors ([78398d3](https://github.com/panva/jose/commit/78398d3)) ## [1.8.0](https://github.com/panva/jose/compare/v1.7.0...v1.8.0) (2019-08-22) ### Features * added Node.js lts/dubnium support for runtime supported features ([67a8601](https://github.com/panva/jose/commit/67a8601)) ## [1.7.0](https://github.com/panva/jose/compare/v1.6.1...v1.7.0) (2019-08-20) ### Features * add RSA-OAEP-256 support (when a node version supports it) ([28d7cf8](https://github.com/panva/jose/commit/28d7cf8)), closes [#29](https://github.com/panva/jose/issues/29) ## [1.6.1](https://github.com/panva/jose/compare/v1.6.0...v1.6.1) (2019-07-29) ### Fixes * properly pad calculated RSA primes ([dd121ce](https://github.com/panva/jose/commit/dd121ce)) ## [1.6.0](https://github.com/panva/jose/compare/v1.5.2...v1.6.0) (2019-07-27) ### Fixes * use the correct ECPrivateKey version when importing EC JWK ([24acd20](https://github.com/panva/jose/commit/24acd20)) ### Features * electron v6.x support ([e7ad82c](https://github.com/panva/jose/commit/e7ad82c)) ## [1.5.2](https://github.com/panva/jose/compare/v1.5.1...v1.5.2) (2019-07-27) ### Fixes * importing x5c in electron requires the input split ([181fd09](https://github.com/panva/jose/commit/181fd09)) ## [1.5.1](https://github.com/panva/jose/compare/v1.5.0...v1.5.1) (2019-07-27) ### Fixes * correctly pad integers when importing RSA JWK ([1dc7f35](https://github.com/panva/jose/commit/1dc7f35)) ## [1.5.0](https://github.com/panva/jose/compare/v1.4.1...v1.5.0) (2019-07-23) ### Features * validate JWTs according to a JWT profile - ID Token ([6c98b61](https://github.com/panva/jose/commit/6c98b61)) ## [1.4.1](https://github.com/panva/jose/compare/v1.4.0...v1.4.1) (2019-07-14) ### Fixes * honour the JWT.sign `jti` option ([36c9ce2](https://github.com/panva/jose/commit/36c9ce2)), closes [#33](https://github.com/panva/jose/issues/33) ## [1.4.0](https://github.com/panva/jose/compare/v1.3.0...v1.4.0) (2019-07-08) ### Features * add secp256k1 EC Key curve and ES256K ([211d7af](https://github.com/panva/jose/commit/211d7af)) ## [1.3.0](https://github.com/panva/jose/compare/v1.0.2...c51dc28) (2019-06-21) ### Features * compute private RSA key p, q, dp, dq, qi when omitted ([6e3d6fd](https://github.com/panva/jose/commit/6e3d6fd)), closes [#26](https://github.com/panva/jose/issues/26) * add support for JWK x5c, x5t and x5t#S256 ([9d46c48](https://github.com/panva/jose/commit/9d46c48)) * instances of JWKS.KeyStore are now iterable (e.g. for ... of) ([2eae293](https://github.com/panva/jose/commit/2eae293)) ### Fixes * limit calculation of missing RSA private components ([5b53cb0](https://github.com/panva/jose/commit/5b53cb0)) * reject rsa keys without all factors and exponents with a specific message ([b0ff436](https://github.com/panva/jose/commit/b0ff436)) ### Deprecations - this deprecates the use of `JWK.importKey` in favor of `JWK.asKey` - this deprecates the use of `JWKS.KeyStore.fromJWKS` in favor of `JWKS.asKeyStore` Both `JWK.importKey` and `JWKS.KeyStore.fromJWKS` could have resulted in the process getting blocked when large bitsize RSA private keys were missing their components and could also result in an endless calculation loop when the private key's private exponent was outright invalid or tampered with. The new methods still allow to import private RSA keys with these optimization key parameters missing but it is disabled by default and one should choose to enable it when working with keys from trusted sources It is recommended not to use `jose` versions with this feature in its original on-by-default form - v1.1.0 and v1.2.0 ## [1.0.2](https://github.com/panva/jose/compare/v1.0.1...v1.0.2) (2019-05-13) ### Fixes * add missing keystore.toJWKS() .d.ts definition ([c7a8606](https://github.com/panva/jose/commit/c7a8606)), closes [#25](https://github.com/panva/jose/issues/25) ## [1.0.1](https://github.com/panva/jose/compare/v1.0.0...v1.0.1) (2019-04-27) ### Fixes * oct key ts "k" type fix ([0750d2c](https://github.com/panva/jose/commit/0750d2c)) ## [1.0.0](https://github.com/panva/jose/compare/v0.12.0...v1.0.0) (2019-04-23) ### Fixes * fail to import invalid PEM formatted strings and buffers ([857dc2b](https://github.com/panva/jose/commit/857dc2b)) ### Features * add JWK key_ops support, fix .algorithms() op returns ([23b874c](https://github.com/panva/jose/commit/23b874c)) * add key.toPEM() export function with optional encryption ([1159b0d](https://github.com/panva/jose/commit/1159b0d)) * add OKP Key and EdDSA sign/verify support ([2dbd3ed](https://github.com/panva/jose/commit/2dbd3ed)), closes [#12](https://github.com/panva/jose/issues/12) ### BREAKING CHANGES * key.algorithms(op) un+wrapKey was split into correct wrapKey/unwrapKey/deriveKey returns * keystore.all and keystore.get `operation` option was removed, `key_ops: string[]` supersedes it * Node.js minimal version is now v12.0.0 due to its added EdDSA support (crypto.sign, crypto.verify and eddsa key objects) ## [0.12.0](https://github.com/panva/jose/compare/v0.11.5...v0.12.0) (2019-04-07) ### Reverts * add EC P-256K JWK and ES256K sign/verify support ([e21fea1](https://github.com/panva/jose/commit/e21fea1)) ### BREAKING CHANGES * removing ES256K alg and EC P-256K crv support until the IETF WG decides on what the final names will be. ## [0.11.5](https://github.com/panva/jose/compare/v0.11.4...v0.11.5) (2019-04-04) ### Features * add key.secret and key.type for completeness ([2dd7053](https://github.com/panva/jose/commit/2dd7053)) * add key.thumbprint always returning the JWK Thumbprint (RFC7638) ([65db7e0](https://github.com/panva/jose/commit/65db7e0)) ## [0.11.4](https://github.com/panva/jose/compare/v0.11.3...v0.11.4) (2019-03-28) ### Fixes * properly restrict EC curves in generate(Sync) ([764b863](https://github.com/panva/jose/commit/764b863)) * remove unintended exposure of private material via enumerables ([946d9df](https://github.com/panva/jose/commit/946d9df)) ## [0.11.3](https://github.com/panva/jose/compare/v0.11.2...v0.11.3) (2019-03-27) ### Fixes * throw on unsupported EC curves ([cfa4222](https://github.com/panva/jose/commit/cfa4222)) ### Features * add EC P-256K JWK and ES256K sign/verify support ([2e33e1c](https://github.com/panva/jose/commit/2e33e1c)) ## [0.11.2](https://github.com/panva/jose/compare/v0.11.1...v0.11.2) (2019-03-19) ### Fixes * internal symbol method is now really a symbol ([925d47c](https://github.com/panva/jose/commit/925d47c)) * key.toJWK() fixed on windows ([57f1692](https://github.com/panva/jose/commit/57f1692)), closes [#17](https://github.com/panva/jose/issues/17) ## [0.11.1](https://github.com/panva/jose/compare/v0.11.0...v0.11.1) (2019-03-17) ### Fixes * restrict RS key algorithms by the key's bit size ([9af295b](https://github.com/panva/jose/commit/9af295b)) ## [0.11.0](https://github.com/panva/jose/compare/v0.10.0...v0.11.0) (2019-03-16) ### Fixes * all JWA defined RSA operations require key of 2048 or more ([cc70c5d](https://github.com/panva/jose/commit/cc70c5d)) * use correct salt length for RSASSA-PSS ([e936d54](https://github.com/panva/jose/commit/e936d54)) ### BREAKING CHANGES * all [JWA](https://www.rfc-editor.org/rfc/rfc7518) defined RSA based operations require key size of 2048 bits or more. ## [0.10.0](https://github.com/panva/jose/compare/v0.9.2...v0.10.0) (2019-03-12) ### Fixes * do not list "dir" under wrap/unwrapKey operations ([17b37d3](https://github.com/panva/jose/commit/17b37d3)) ### Features * keystore .all and .get operation option ([d349ba9](https://github.com/panva/jose/commit/d349ba9)) ### BREAKING CHANGES * "dir" is no longer returned as wrap/unwrapKey key operation ## [0.9.2](https://github.com/panva/jose/compare/v0.9.1...v0.9.2) (2019-03-05) ### Fixes * "dir" is only available on keys with correct lengths ([6854860](https://github.com/panva/jose/commit/6854860)) * do not 'in' operator when importing keys as string ([be3f4e4](https://github.com/panva/jose/commit/be3f4e4)) ## [0.9.1](https://github.com/panva/jose/compare/v0.9.0...v0.9.1) (2019-03-02) ### Fixes * only import RSA, EC and oct successfully ([e5e02fc](https://github.com/panva/jose/commit/e5e02fc)) # 0.9.0 (2019-03-02) Initial release ### Implemented Features - JSON Web Signature (JWS) - [RFC7515][spec-jws] - JSON Web Encryption (JWE) - [RFC7516][spec-jwe] - JSON Web Key (JWK) - [RFC7517][spec-jwk] - JSON Web Algorithms (JWA) - [RFC7518][spec-jwa] - JSON Web Token (JWT) - [RFC7519][spec-jwt] - JSON Web Key (JWK) Thumbprint - [RFC7638][spec-thumbprint] - JWS Unencoded Payload Option - [RFC7797][spec-b64] | JWK Key Types | Supported || | -- | -- | -- | | RSA | ✓ | RSA | | Elliptic Curve | ✓ | EC | | Octet sequence | ✓ | oct | | Serialization | JWS Sign | JWS Verify | JWE Encrypt | JWE Decrypt | | -- | -- | -- | -- | -- | | Compact | ✓ | ✓ | ✓ | ✓ | | General JSON | ✓ | ✓ | ✓ | ✓ | | Flattened JSON | ✓ | ✓ | ✓ | ✓ | | JWS Algorithms | Supported || | -- | -- | -- | | RSASSA-PKCS1-v1_5 | ✓ | RS256, RS384, RS512 | | RSASSA-PSS | ✓ | PS256, PS384, PS512 | | ECDSA | ✓ | ES256, ES384, ES512 | | HMAC with SHA-2 | ✓ | HS256, HS384, HS512 | | JWE Key Management Algorithms | Supported || | -- | -- | -- | | AES | ✓ | A128KW, A192KW, A256KW | | AES GCM | ✓ | A128GCMKW, A192GCMKW, A256GCMKW | | Direct Key Agreement | ✓ | dir | | RSAES OAEP | ✓* | RSA-OAEP (*RSA-OAEP-256 is not supported due to its lack of support in Node.js) | | RSAES-PKCS1-v1_5 | ✓ | RSA1_5 | | PBES2 | ✓ | PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW | | ECDH-ES | ✓ | ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW | | JWE Content Encryption Algorithms | Supported || | -- | -- | -- | | AES GCM | ✓ | A128GCM, A192GCM, A256GCM | | AES_CBC_HMAC_SHA2 | ✓ | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 | [spec-b64]: https://www.rfc-editor.org/rfc/rfc7797 [spec-jwa]: https://www.rfc-editor.org/rfc/rfc7518 [spec-jwe]: https://www.rfc-editor.org/rfc/rfc7516 [spec-jwk]: https://www.rfc-editor.org/rfc/rfc7517 [spec-jws]: https://www.rfc-editor.org/rfc/rfc7515 [spec-jwt]: https://www.rfc-editor.org/rfc/rfc7519 [spec-thumbprint]: https://www.rfc-editor.org/rfc/rfc7638 ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at panva.ip@gmail.com. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to jose Please note we have a [code of conduct][coc], please follow it in all your interactions with the project. When contributing to this project, please first discuss the change you wish to make via a discussion, email, or any other appropriate method with the owners of this project before proposing a change via a Pull Request. The project promotes and follows current best practices in regards to the specifications it implements. A contribution that tries to implement something non-standard will most likely be dismissed. ## Rules of the discussions Remember to be very clear and transparent when discussing any issue in the discussions boards. We ask that you keep the language to English and keep on track with the issue at hand. Lastly, please be respectful of our fellow contributors and keep an exemplary level of professionalism at all times. [coc]: https://github.com/panva/jose/blob/main/CODE_OF_CONDUCT.md ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2018 Filip Skokan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # jose `jose` is a JavaScript module for JSON Object Signing and Encryption, providing support for JSON Web Tokens (JWT), JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Key Set (JWKS), and more. The module is designed to work across various Web-interoperable runtimes including Node.js, browsers, Cloudflare Workers, Deno, Bun, and others. ## Sponsor Auth0 by Okta If you want to quickly add JWT authentication to JavaScript apps, feel free to check out Auth0's JavaScript SDK and free plan. [Create an Auth0 account; it's free!][sponsor-auth0]

## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Dependencies: 0 `jose` has no dependencies and it exports tree-shakeable ESM[^cjs]. ## Documentation `jose` is distributed via [npmjs.com](https://www.npmjs.com/package/jose), [jsr.io](https://jsr.io/@panva/jose), [jsdelivr.com](https://www.jsdelivr.com/package/npm/jose), and [github.com](https://github.com/panva/jose). **`example`** ESM import[^cjs] ```js import * as jose from 'jose' ``` ### JSON Web Tokens (JWT) The `jose` module supports JSON Web Tokens (JWT) and provides functionality for signing and verifying tokens, as well as their JWT Claims Set validation. - [JWT Claims Set Validation & Signature Verification](docs/jwt/verify/functions/jwtVerify.md) using the `jwtVerify` function - [Using a remote JSON Web Key Set (JWKS)](docs/jwks/remote/functions/createRemoteJWKSet.md) - [Using a local JSON Web Key Set (JWKS)](docs/jwks/local/functions/createLocalJWKSet.md) - [Signing](docs/jwt/sign/classes/SignJWT.md) using the `SignJWT` class - Utility functions - [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md) - [Decoding JWT Claims Set](docs/util/decode_jwt/functions/decodeJwt.md) prior to its validation ### Encrypted JSON Web Tokens The `jose` module supports encrypted JSON Web Tokens and provides functionality for encrypting and decrypting tokens, as well as their JWT Claims Set validation. - [Decryption & JWT Claims Set Validation](docs/jwt/decrypt/functions/jwtDecrypt.md) using the `jwtDecrypt` function - [Encryption](docs/jwt/encrypt/classes/EncryptJWT.md) using the `EncryptJWT` class - Utility functions - [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md) ### Key Utilities The `jose` module supports importing, exporting, and generating keys and secrets in various formats, including PEM formats like SPKI, X.509 certificate, and PKCS #8, as well as JSON Web Key (JWK). - Key Import Functions - [JWK Import](docs/key/import/functions/importJWK.md) - [Public Key Import (SPKI)](docs/key/import/functions/importSPKI.md) - [Public Key Import (X.509 Certificate)](docs/key/import/functions/importX509.md) - [Private Key Import (PKCS #8)](docs/key/import/functions/importPKCS8.md) - Key and Secret Generation Functions - [Asymmetric Key Pair Generation](docs/key/generate_key_pair/functions/generateKeyPair.md) - [Symmetric Secret Generation](docs/key/generate_secret/functions/generateSecret.md) - Key Export Functions - [JWK Export](docs/key/export/functions/exportJWK.md) - [Private Key Export](docs/key/export/functions/exportPKCS8.md) - [Public Key Export](docs/key/export/functions/exportSPKI.md) ### JSON Web Signature (JWS) The `jose` module supports signing and verification of JWS messages with arbitrary payloads in Compact, Flattened JSON, and General JSON serialization syntaxes. - Signing - [Compact](docs/jws/compact/sign/classes/CompactSign.md), [Flattened JSON](docs/jws/flattened/sign/classes/FlattenedSign.md), [General JSON](docs/jws/general/sign/classes/GeneralSign.md) - Verification - [Compact](docs/jws/compact/verify/functions/compactVerify.md), [Flattened JSON](docs/jws/flattened/verify/functions/flattenedVerify.md), [General JSON](docs/jws/general/verify/functions/generalVerify.md) - [Using a remote JSON Web Key Set (JWKS)](docs/jwks/remote/functions/createRemoteJWKSet.md) - [Using a local JSON Web Key Set (JWKS)](docs/jwks/local/functions/createLocalJWKSet.md) - Utility functions - [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md) ### JSON Web Encryption (JWE) The `jose` module supports encryption and decryption of JWE messages with arbitrary plaintext in Compact, Flattened JSON, and General JSON serialization syntaxes. - Encryption - [Compact](docs/jwe/compact/encrypt/classes/CompactEncrypt.md), [Flattened JSON](docs/jwe/flattened/encrypt/classes/FlattenedEncrypt.md), [General JSON](docs/jwe/general/encrypt/classes/GeneralEncrypt.md) - Decryption - [Compact](docs/jwe/compact/decrypt/functions/compactDecrypt.md), [Flattened JSON](docs/jwe/flattened/decrypt/functions/flattenedDecrypt.md), [General JSON](docs/jwe/general/decrypt/functions/generalDecrypt.md) - Utility functions - [Decoding Token's Protected Header](docs/util/decode_protected_header/functions/decodeProtectedHeader.md) ### Other The following are additional features and utilities provided by the `jose` module: - [Calculating JWK Thumbprint](docs/jwk/thumbprint/functions/calculateJwkThumbprint.md) - [Calculating JWK Thumbprint URI](docs/jwk/thumbprint/functions/calculateJwkThumbprintUri.md) - [Verification using a JWK Embedded in a JWS Header](docs/jwk/embedded/functions/EmbeddedJWK.md) - [Unsecured JWT](docs/jwt/unsecured/classes/UnsecuredJWT.md) - [JOSE Errors](docs/util/errors/README.md) ## Supported Runtimes The `jose` module is compatible with JavaScript runtimes that support the utilized Web API globals and standard built-in objects or are Node.js. The following runtimes are supported _(this is not an exhaustive list)_: - [Bun](https://github.com/panva/jose/issues/471) - [Browsers](https://github.com/panva/jose/issues/263) - [Cloudflare Workers](https://github.com/panva/jose/issues/265) - [Deno](https://github.com/panva/jose/issues/266) - [Electron](https://github.com/panva/jose/issues/264) - [Node.js](https://github.com/panva/jose/issues/262) Please note that certain algorithms may not be available depending on the runtime used. You can find a list of available algorithms for each runtime in the specific issue links provided above. ## Supported Versions | Version | Security Fixes 🔑 | Other Bug Fixes 🐞 | New Features ⭐ | Runtime and Module type | | ----------------------------------------------- | ----------------- | ------------------ | --------------- | ------------------------------- | | [v6.x](https://github.com/panva/jose/tree/v6.x) | [Security Policy] | ✅ | ✅ | Universal[^universal] ESM[^cjs] | | [v5.x](https://github.com/panva/jose/tree/v5.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM | | [v4.x](https://github.com/panva/jose/tree/v4.x) | [Security Policy] | ❌ | ❌ | Universal[^universal] CJS + ESM | | [v2.x](https://github.com/panva/jose/tree/v2.x) | [Security Policy] | ❌ | ❌ | Node.js CJS | ## Specifications
Details - JSON Web Signature (JWS) - [RFC7515](https://www.rfc-editor.org/rfc/rfc7515) - JSON Web Encryption (JWE) - [RFC7516](https://www.rfc-editor.org/rfc/rfc7516) - JSON Web Key (JWK) - [RFC7517](https://www.rfc-editor.org/rfc/rfc7517) - JSON Web Algorithms (JWA) - [RFC7518](https://www.rfc-editor.org/rfc/rfc7518) - JSON Web Token (JWT) - [RFC7519](https://www.rfc-editor.org/rfc/rfc7519) - JSON Web Key Thumbprint - [RFC7638](https://www.rfc-editor.org/rfc/rfc7638) - JSON Web Key Thumbprint URI - [RFC9278](https://www.rfc-editor.org/rfc/rfc9278) - JWS Unencoded Payload Option - [RFC7797](https://www.rfc-editor.org/rfc/rfc7797) - CFRG Elliptic Curve ECDH and Signatures - [RFC8037](https://www.rfc-editor.org/rfc/rfc8037) - Fully-Specified Algorithms for JOSE - [RFC9864](https://www.rfc-editor.org/rfc/rfc9864.html) - ML-DSA for JOSE - [draft-ietf-cose-dilithium-10](https://www.ietf.org/archive/id/draft-ietf-cose-dilithium-10.html) The algorithm implementations in `jose` have been tested using test vectors from their respective specifications as well as [RFC7520](https://www.rfc-editor.org/rfc/rfc7520).
[sponsor-auth0]: https://a0.to/signup/panva [WebCryptoAPI]: https://w3c.github.io/webcrypto/ [Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API [Security Policy]: https://github.com/panva/jose/security/policy [^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where the `require(esm)` feature is enabled by default (^20.19.0 || ^22.12.0 || >= 23.0.0). [^universal]: Assumes runtime support of [WebCryptoAPI][] and [Fetch API][] ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions The following major versions are currently supported with security updates. | Version | End-of-life | | ----------------------------------------------- | ----------- | | [v6.x](https://github.com/panva/jose/tree/v6.x) | TBD | | [v5.x](https://github.com/panva/jose/tree/v5.x) | 2026-04-30 | | [v4.x](https://github.com/panva/jose/tree/v4.x) | 2026-04-30 | | [v2.x](https://github.com/panva/jose/tree/v2.x) | 2026-04-30 | End-of-life for the current release will be determined prior to the release of its successor. ## Reporting a Vulnerability Vulnerabilities must be reported using the [project's security advisory](https://github.com/panva/jose/security/advisories/new). **All vulnerability reports MUST be submitted through the channel listed above.** This allows the maintainers to assess the report, collaborate on remediation, and coordinate disclosure in a responsible manner. CVE identifiers for confirmed vulnerabilities will only be requested by the maintainers through the GitHub Security Advisory process. Vulnerability reports submitted directly to third-party CVE Numbering Authorities (CNAs), such as MITRE, without first being reported and confirmed through this project's documented channel will be considered as not following the coordinated disclosure process. The maintainers reserve the right to request rejection or dispute of any CVE entry that was assigned without prior coordinated disclosure with the project. ## Threat Model This section documents the threat model for `jose`, a JavaScript implementation of JSON Object Signing and Encryption standards including [JSON Web Token (JWT) - RFC 7519](https://www.rfc-editor.org/rfc/rfc7519), [JSON Web Signature (JWS) - RFC 7515](https://www.rfc-editor.org/rfc/rfc7515), [JSON Web Encryption (JWE) - RFC 7516](https://www.rfc-editor.org/rfc/rfc7516), [JSON Web Key (JWK) - RFC 7517](https://www.rfc-editor.org/rfc/rfc7517), and [JSON Web Algorithms (JWA) - RFC 7518](https://www.rfc-editor.org/rfc/rfc7518). ### Purpose and Intended Users This library is intended for general application developers, cryptography practitioners, and anyone needing JOSE functionality in JavaScript runtimes (Node.js, browsers, Cloudflare Workers, Deno, Bun, and other Web-interoperable environments). ### Trust Assumptions #### Underlying Cryptographic Primitives This library trusts that the Web Cryptography implementations provided by the runtime are correct and secure. The library delegates all cryptographic operations (key generation, signing, verification, encryption, decryption, key derivation, etc.) to the runtime's Web Cryptography implementation and does not attempt to validate or verify the correctness of these underlying primitives. #### Runtime Environment The library assumes it is running in a trusted execution environment. The following are considered outside the scope of this library's threat model: - **Prototype pollution attacks**: If an attacker can modify JavaScript prototypes, this is considered a vulnerability in the user's application code or the runtime environment, not in this library. - **Debugger access**: If an attacker has debugger access to the running process, they can inspect memory, modify variables, and bypass security controls. This is a runtime-level compromise, not a library vulnerability. - **Runtime compromise**: Attacks that compromise the JavaScript runtime itself (e.g., malicious runtime modifications, compromised Node.js binaries, malicious browser extensions with elevated privileges) are not considered attacks on this library. #### Remote JWKS Sources When using remote JSON Web Key Sets (JWKS) via `createRemoteJWKSet`, the library assumes that users configure trusted JWKS sources. The security of key material fetched from remote sources depends on the trustworthiness of those sources and the security of the transport (HTTPS). #### Key Material Private keys and secret key material provided by users for signing, decryption, or key management operations are considered trusted and fitting the user's own security requirements. The library does not validate that key material originates from a secure source or has been handled securely prior to being provided. #### Key and Secret Sizes As cryptographic requirements on key and secret sizes evolve over time, following these developments is the user's responsibility. The library implements reasonable measures where practical, but in the spirit of interoperability with other implementations, it does not enforce strict key size requirements for all algorithms. For example, the library cannot prevent use of short HMAC secret keys because such restrictions are easily sidestepped and would hinder interoperability. #### Input Size Limits The library does not enforce size limits on any inputs (tokens, keys, payloads, headers, etc.). It is the application's responsibility to enforce input size limits appropriate for its context before passing data to the library. Without such limits, an attacker could supply arbitrarily large inputs that consume excessive memory or processing time. #### Side-Channel Attacks This library delegates all cryptographic operations to the underlying Web Cryptography. Any resistance to side-channel attacks (timing attacks, cache attacks, etc.) is entirely dependent on the underlying cryptographic implementations and is outside the scope of this library. ### Security Guarantees This library aims to provide the following security guarantees: - **Specification compliance**: Correct implementation of the JOSE family of specifications (RFC 7515, RFC 7516, RFC 7517, RFC 7518, RFC 7519, and related RFCs), validated against test vectors from the respective specifications. - **JWT Claims Set validation**: Proper validation of JWT claims (`exp`, `nbf`, `iat`, `aud`, `iss`, etc.) as defined by the underlying RFCs. - **Input validation**: Validation of inputs to prevent misuse of the API. ### Out of Scope #### Key Management This library does not handle key storage. Users are responsible for securely storing, managing, and distributing cryptographic keys. #### Memory Clearing This library does not guarantee that key material or other sensitive data is cleared from memory after use. As long as the user retains references to key objects, the key material may remain in memory. Secure memory management is the responsibility of the user and the runtime environment. ### Threat Actors and Security Properties This library aims to provide the security properties defined by the JOSE specifications. For detailed security considerations, refer to the Security Considerations sections in [RFC 7515 (JWS)](https://www.rfc-editor.org/rfc/rfc7515#section-10), [RFC 7516 (JWE)](https://www.rfc-editor.org/rfc/rfc7516#section-11), [RFC 7517 (JWK)](https://www.rfc-editor.org/rfc/rfc7517#section-9), [RFC 7518 (JWA)](https://www.rfc-editor.org/rfc/rfc7518#section-8), and [RFC 7519 (JWT)](https://www.rfc-editor.org/rfc/rfc7519#section-8). ### What is NOT Considered a Vulnerability The following are explicitly **not** considered vulnerabilities in this library: - **Prototype pollution** ([CWE-1321](https://cwe.mitre.org/data/definitions/1321.html)): Attacks that exploit JavaScript prototype pollution are considered vulnerabilities in user application code or the runtime, not this library. - **Object injection** ([CWE-915](https://cwe.mitre.org/data/definitions/915.html)): Similar to prototype pollution, object injection attacks are outside the scope of this library. - **Debugger/inspector access** ([CWE-489](https://cwe.mitre.org/data/definitions/489.html)): If an attacker can attach a debugger to the process, they have already compromised the runtime environment. - **Memory inspection**: Reading process memory, heap dumps, or core dumps to extract key material is a runtime-level attack. - **Side-channel attacks** ([CWE-208](https://cwe.mitre.org/data/definitions/208.html)): Timing attacks, cache attacks, and other side-channel vulnerabilities in the underlying Web Cryptography implementations are not vulnerabilities in this library. - **Compromised runtime environment**: Malicious or backdoored JavaScript runtimes, compromised system libraries, or tampered Web Cryptography implementations. - **Supply chain attacks on the runtime** ([CWE-1357](https://cwe.mitre.org/data/definitions/1357.html)): Compromised Node.js binaries, malicious browser builds, or similar supply chain attacks on the execution environment. - **Denial of service via resource exhaustion** ([CWE-400](https://cwe.mitre.org/data/definitions/400.html)): While the library validates inputs, it does not implement resource limits. Applications should implement their own rate limiting and resource management. - **Oversized inputs** ([CWE-400](https://cwe.mitre.org/data/definitions/400.html)): The library does not enforce size limits on JWTs, JWS, JWE, JWK, or JWKS inputs. Enforcing input size limits appropriate for the application's context (e.g., limiting the size of incoming tokens or payloads before passing them to the library) is the responsibility of the application. - **Untrusted JWKS sources**: Security issues arising from fetching keys from untrusted or compromised JWKS endpoints are the user's responsibility. ================================================ FILE: ava.config.mjs ================================================ const files = ['test/**/*.test.ts'] if ('CITGM' in process.env) { files.push("!**/remote.test.ts") } export default { extensions: { ts: 'module', mjs: true, }, files, workerThreads: false, nodeArguments: ['--enable-source-maps'], } ================================================ FILE: cookbook/jwe.mjs ================================================ export default [ { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.2 - Key Encryption using RSA-OAEP with AES-GCM', input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'RSA', ext: false, kid: 'samwise.gamgee@hobbiton.example', use: 'enc', n: 'wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIUe7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBODFskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqBSAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhOOnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDaiCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnTyC0xhWBlsolZE', e: 'AQAB', alg: 'RSA-OAEP', d: 'n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bxcc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9EA-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIjh1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yDF-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1LoomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c9WsWgRzI-K8gE', p: '7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKghvM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsYa_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3mY46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9sfbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgPgWCv5HoQ', q: 'zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6ZyKQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDcqssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYGRuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJaPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EXe2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJJlXXnH8Q', dp: '19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xnx5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQJ_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72FZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3iXjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGmpKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9LcnwwT0jvoQ', dq: 'S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fgdyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrIChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iznBNCeOUIQ', qi: 'FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCciRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMwQqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq8EzqZEKIA', }, alg: 'RSA-OAEP', enc: 'A256GCM', }, generated: { cek: 'mYMfsggkTAm0TbvtlFh2hyoXnbEzJQjMxmgLN3d8xXA', iv: '-nBoKLH0YkLZPSI9', }, encrypting_key: {}, encrypting_content: { protected: { alg: 'RSA-OAEP', kid: 'samwise.gamgee@hobbiton.example', enc: 'A256GCM', }, }, output: { compact: 'eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0.rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs.-nBoKLH0YkLZPSI9.o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw.UCGiqJxhBI3IFVdPalHHvA', json: { recipients: [ { encrypted_key: 'rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs', }, ], protected: 'eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0', iv: '-nBoKLH0YkLZPSI9', ciphertext: 'o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw', tag: 'UCGiqJxhBI3IFVdPalHHvA', }, json_flat: { protected: 'eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0', encrypted_key: 'rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQibeYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyucvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8BpxKdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pKIIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQfOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe38UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDRs', iv: '-nBoKLH0YkLZPSI9', ciphertext: 'o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgRL-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEwP7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSVmaPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw', tag: 'UCGiqJxhBI3IFVdPalHHvA', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.3 - Key Wrap using PBES2-AES-KeyWrap with AES-CBC-HMAC-SHA2', deterministic: true, input: { plaintext: '{"keys":[{"kty":"oct","kid":"77c7e2b8-6e13-45cf-8672-617b5b45243a","use":"enc","alg":"A128GCM","k":"XctOhJAkA-pD9Lh7ZgW_2A"},{"kty":"oct","kid":"81b20965-8332-43d9-a468-82160ad91ac8","use":"enc","alg":"A128KW","k":"GZy6sIZ6wl9NJOKB-jnmVQ"},{"kty":"oct","kid":"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d","use":"enc","alg":"A256GCMKW","k":"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8"}]}', pwd: 'entrap_o–peter_long–credit_tun', alg: 'PBES2-HS512+A256KW', enc: 'A128CBC-HS256', }, generated: { cek: 'uwsjJXaBK407Qaf0_zpcpmr1Cs0CC50hIUEyGNEt3m0', iv: 'VBiCzVHNoLiR3F4V82uoTQ', }, encrypting_key: { salt: '8Q1SzinasR3xchYz6ZZcHA', iteration_count: 8192, }, encrypting_content: { protected: { alg: 'PBES2-HS512+A256KW', p2s: '8Q1SzinasR3xchYz6ZZcHA', p2c: 8192, cty: 'jwk-set+json', enc: 'A128CBC-HS256', }, }, output: { compact: 'eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g.VBiCzVHNoLiR3F4V82uoTQ.23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p.0HlwodAhOCILG5SQ2LQ9dg', json: { recipients: [ { encrypted_key: 'd3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g', }, ], protected: 'eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0', iv: 'VBiCzVHNoLiR3F4V82uoTQ', ciphertext: '23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p', tag: '0HlwodAhOCILG5SQ2LQ9dg', }, json_flat: { protected: 'eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0', encrypted_key: 'd3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g', iv: 'VBiCzVHNoLiR3F4V82uoTQ', ciphertext: '23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IRsfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6lTF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKdPQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrokAKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V3kobXZ77ulMwDs4p', tag: '0HlwodAhOCILG5SQ2LQ9dg', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.4 - Key Agreement with Key Wrapping using ECDH-ES and AES-KeyWrap with AES-GCM', input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'EC', ext: false, kid: 'peregrin.took@tuckborough.example', use: 'enc', crv: 'P-384', x: 'YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQLpe2FpxBmu2', y: 'A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-SkgaFL1ETP', d: 'iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0IdnYK2xDlZh-j', }, alg: 'ECDH-ES+A128KW', enc: 'A128GCM', }, generated: { cek: 'Nou2ueKlP70ZXDbq9UrRwg', iv: 'mH-G2zVqgztUtnW_', }, encrypting_key: { epk: { kty: 'EC', crv: 'P-384', x: 'uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6iuEDsQ6wNdNg3', y: 'sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQTA-JdaY8tb7E0', d: 'D5H4Y_5PSKZvhfVFbcCYJOtcGZygRgfZkpsBr59Icmmhe9sW6nkZ8WfwhinUfWJg', }, }, encrypting_content: { protected: { alg: 'ECDH-ES+A128KW', kid: 'peregrin.took@tuckborough.example', epk: { kty: 'EC', crv: 'P-384', x: 'uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6iuEDsQ6wNdNg3', y: 'sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQTA-JdaY8tb7E0', }, enc: 'A128GCM', }, }, output: { compact: 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0.0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2.mH-G2zVqgztUtnW_.tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ.WuGzxmcreYjpHGJoa17EBg', json: { recipients: [ { encrypted_key: '0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2', }, ], protected: 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0', iv: 'mH-G2zVqgztUtnW_', ciphertext: 'tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ', tag: 'WuGzxmcreYjpHGJoa17EBg', }, json_flat: { protected: 'eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdHVja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NHhBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMyZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWTh0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0', encrypted_key: '0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2', iv: 'mH-G2zVqgztUtnW_', ciphertext: 'tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cPWJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkcY9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w03XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ', tag: 'WuGzxmcreYjpHGJoa17EBg', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.5 - Key Agreement using ECDH-ES with AES-CBC-HMAC-SHA2', input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'EC', ext: false, kid: 'meriadoc.brandybuck@buckland.example', use: 'enc', crv: 'P-256', x: 'Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0', y: 'HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw', d: 'r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8', }, alg: 'ECDH-ES', enc: 'A128CBC-HS256', }, generated: { iv: 'yc9N8v5sYyv3iGQT926IUg', }, encrypting_key: { epk: { kty: 'EC', crv: 'P-256', x: 'mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA', y: '8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs', d: 'AtH35vJsQ9SGjYfOsjUxYXQKrPH3FjZHmEtSKoSN8cM', }, cek: 'hzHdlfQIAEehb8Hrd_mFRhKsKLEzPfshfXs9l6areCc', }, encrypting_content: { protected: { alg: 'ECDH-ES', kid: 'meriadoc.brandybuck@buckland.example', epk: { kty: 'EC', crv: 'P-256', x: 'mPUKT_bAWGHIhg0TpjjqVsP1rXWQu_vwVOHHtNkdYoA', y: '8BQAsImGeAS46fyWw5MhYfGTT0IjBpFw2SS34Dv4Irs', }, enc: 'A128CBC-HS256', }, }, output: { compact: 'eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ..yc9N8v5sYyv3iGQT926IUg.BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg.WCCkNa-x4BeB9hIDIfFuhg', json: { protected: 'eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ', iv: 'yc9N8v5sYyv3iGQT926IUg', ciphertext: 'BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg', tag: 'WCCkNa-x4BeB9hIDIfFuhg', }, json_flat: { protected: 'eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidWNrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZFlvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ', iv: 'yc9N8v5sYyv3iGQT926IUg', ciphertext: 'BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_evAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ6195_JGG2m9Csg', tag: 'WCCkNa-x4BeB9hIDIfFuhg', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.6 - Direction Encryption using AES-GCM', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '77c7e2b8-6e13-45cf-8672-617b5b45243a', use: 'enc', alg: 'A128GCM', k: 'XctOhJAkA-pD9Lh7ZgW_2A', }, alg: 'dir', enc: 'A128GCM', }, generated: { iv: 'refa467QzzKx6QAB', }, encrypting_content: { protected: { alg: 'dir', kid: '77c7e2b8-6e13-45cf-8672-617b5b45243a', enc: 'A128GCM', }, }, output: { compact: 'eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0..refa467QzzKx6QAB.JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp.vbb32Xvllea2OtmHAdccRQ', json: { protected: 'eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0', iv: 'refa467QzzKx6QAB', ciphertext: 'JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp', tag: 'vbb32Xvllea2OtmHAdccRQ', }, json_flat: { protected: 'eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MTdiNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0', iv: 'refa467QzzKx6QAB', ciphertext: 'JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7YhLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zMDB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSInZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp', tag: 'vbb32Xvllea2OtmHAdccRQ', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.6 - Key Wrap using AES-GCM KeyWrap with AES-CBC-HMAC-SHA2', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', use: 'enc', alg: 'A256GCMKW', k: 'qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8', }, alg: 'A256GCMKW', enc: 'A128CBC-HS256', }, generated: { cek: 'UWxARpat23nL9ReIj4WG3D1ee9I4r-Mv5QLuFXdy_rE', iv: 'gz6NjyEFNm_vm8Gj6FwoFQ', }, encrypting_key: { iv: 'KkYT0GX_2jHlfqN_', tag: 'kfPduVQ3T3H6vnewt--ksw', }, encrypting_content: { protected: { alg: 'A256GCMKW', kid: '18ec08e1-bfa9-4d95-b205-2b4dd1d4321d', tag: 'kfPduVQ3T3H6vnewt--ksw', iv: 'KkYT0GX_2jHlfqN_', enc: 'A128CBC-HS256', }, }, output: { compact: 'eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9.lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok.gz6NjyEFNm_vm8Gj6FwoFQ.Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU.DKW7jrb4WaRSNfbXVPlT5g', json: { recipients: [ { encrypted_key: 'lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok', }, ], protected: 'eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9', iv: 'gz6NjyEFNm_vm8Gj6FwoFQ', ciphertext: 'Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU', tag: 'DKW7jrb4WaRSNfbXVPlT5g', }, json_flat: { protected: 'eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYjIwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1NiJ9', encrypted_key: 'lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok', iv: 'gz6NjyEFNm_vm8Gj6FwoFQ', ciphertext: 'Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaEeVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCzLjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFqhpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hdeb6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0JtjxAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR1B-gxpNk3xWU', tag: 'DKW7jrb4WaRSNfbXVPlT5g', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.8 - Key Wrap using AES-KeyWrap with AES-GCM', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '81b20965-8332-43d9-a468-82160ad91ac8', use: 'enc', alg: 'A128KW', k: 'GZy6sIZ6wl9NJOKB-jnmVQ', }, alg: 'A128KW', enc: 'A128GCM', }, generated: { cek: 'aY5_Ghmk9KxWPBLu_glx1w', iv: 'Qx0pmsDa8KnJc9Jo', }, encrypting_key: {}, encrypting_content: { protected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', }, }, output: { compact: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0.CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx.Qx0pmsDa8KnJc9Jo.AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF.ER7MWJZ1FBI_NKvn7Zb1Lw', json: { recipients: [ { encrypted_key: 'CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx', }, ], protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0', iv: 'Qx0pmsDa8KnJc9Jo', ciphertext: 'AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF', tag: 'ER7MWJZ1FBI_NKvn7Zb1Lw', }, json_flat: { protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0', encrypted_key: 'CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx', iv: 'Qx0pmsDa8KnJc9Jo', ciphertext: 'AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD61A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfeF0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8REwOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-puQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRaa8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF', tag: 'ER7MWJZ1FBI_NKvn7Zb1Lw', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.9 - Compressed Content', deterministic: false, // https://www.rfc-editor.org/errata/eid7680 input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '81b20965-8332-43d9-a468-82160ad91ac8', use: 'enc', alg: 'A128KW', k: 'GZy6sIZ6wl9NJOKB-jnmVQ', }, alg: 'A128KW', enc: 'A128GCM', zip: 'DEF', }, generated: { plaintext_c: 'bY_BDcIwDEVX-QNU3QEOrIA4pqlDokYxchxVvbEDGzIJbioOSJwc-f___HPjBu8KVFpVtAplVE1-wZo0YjNZo3C7R5v72pV5f5X382VWjYQpqZKAyjziZOr2B7kQPSy6oZIXUnDYbVKN4jNXi2u0yB7t1qSHTjmMODf9QgvrDzfTIQXnyQRuUya4zIWG3vTOdir0v7BRHFYWq3k1k1A_gSDJqtcBF-GZxw8', cek: 'hC-MpLZSuwWv8sexS6ydfw', iv: 'p9pUq6XHY0jfEZIl', }, encrypting_key: {}, encrypting_content: { protected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', zip: 'DEF', }, }, output: { compact: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0.5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi.p9pUq6XHY0jfEZIl.HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw.VILuUwuIxaLVmh5X-T7kmA', json: { recipients: [ { encrypted_key: '5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi', }, ], protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0', iv: 'p9pUq6XHY0jfEZIl', ciphertext: 'HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw', tag: 'VILuUwuIxaLVmh5X-T7kmA', }, json_flat: { protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0', encrypted_key: '5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi', iv: 'p9pUq6XHY0jfEZIl', ciphertext: 'HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyezSPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBKhpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw', tag: 'VILuUwuIxaLVmh5X-T7kmA', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.10 - Including Additional Authenticated Data', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '81b20965-8332-43d9-a468-82160ad91ac8', use: 'enc', alg: 'A128KW', k: 'GZy6sIZ6wl9NJOKB-jnmVQ', }, alg: 'A128KW', enc: 'A128GCM', aad: '["vcard",[["version",{},"text","4.0"],["fn",{},"text","Meriadoc Brandybuck"],["n",{},"text",["Brandybuck","Meriadoc","Mr.",""]],["bday",{},"text","TA 2982"],["gender",{},"text","M"]]]', }, generated: { cek: '75m1ALsYv10pZTKPWrsqdg', iv: 'veCx9ece2orS7c_N', aad_b64u: 'WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d', }, encrypting_key: {}, encrypting_content: { protected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', }, }, output: { json: { recipients: [ { encrypted_key: '4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X', }, ], protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0', iv: 'veCx9ece2orS7c_N', aad: 'WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d', ciphertext: 'Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV', tag: 'vOaH_Rajnpy_3hOtqvZHRA', }, json_flat: { protected: 'eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0', encrypted_key: '4YiiQ_ZzH76TaIkJmYfRFgOV9MIpnx4X', aad: 'WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d', iv: 'veCx9ece2orS7c_N', ciphertext: 'Z_3cbr0k3bVM6N3oSNmHz7Lyf3iPppGf3Pj17wNZqteJ0Ui8p74SchQP8xygM1oFRWCNzeIa6s6BcEtp8qEFiqTUEyiNkOWDNoF14T_4NFqF-p2Mx8zkbKxI7oPK8KNarFbyxIDvICNqBLba-v3uzXBdB89fzOI-Lv4PjOFAQGHrgv1rjXAmKbgkft9cB4WeyZw8MldbBhc-V_KWZslrsLNygon_JJWd_ek6LQn5NRehvApqf9ZrxB4aq3FXBxOxCys35PhCdaggy2kfUfl2OkwKnWUbgXVD1C6HxLIlqHhCwXDG59weHrRDQeHyMRoBljoV3X_bUTJDnKBFOod7nLz-cj48JMx3SnCZTpbQAkFV', tag: 'vOaH_Rajnpy_3hOtqvZHRA', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.11 - Protecting Specific Header Fields', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '81b20965-8332-43d9-a468-82160ad91ac8', use: 'enc', alg: 'A128KW', k: 'GZy6sIZ6wl9NJOKB-jnmVQ', }, alg: 'A128KW', enc: 'A128GCM', }, generated: { cek: 'WDgEptBmQs9ouUvArz6x6g', iv: 'WgEJsDS9bkoXQ3nR', }, encrypting_key: {}, encrypting_content: { protected: { enc: 'A128GCM', }, unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', }, }, output: { json: { recipients: [ { encrypted_key: 'jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H', }, ], unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', }, protected: 'eyJlbmMiOiJBMTI4R0NNIn0', iv: 'WgEJsDS9bkoXQ3nR', ciphertext: 'lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf', tag: 'fNYLqpUe84KD45lvDiaBAQ', }, json_flat: { protected: 'eyJlbmMiOiJBMTI4R0NNIn0', unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', }, encrypted_key: 'jJIcM9J-hbx3wnqhf5FlkEYos0sHsF0H', iv: 'WgEJsDS9bkoXQ3nR', ciphertext: 'lIbCyRmRJxnB2yLQOTqjCDKV3H30ossOw3uD9DPsqLL2DM3swKkjOwQyZtWsFLYMj5YeLht_StAn21tHmQJuuNt64T8D4t6C7kC9OCCJ1IHAolUv4MyOt80MoPb8fZYbNKqplzYJgIL58g8N2v46OgyG637d6uuKPwhAnTGm_zWhqc_srOvgiLkzyFXPq1hBAURbc3-8BqeRb48iR1-_5g5UjWVD3lgiLCN_P7AW8mIiFvUNXBPJK3nOWL4teUPS8yHLbWeL83olU4UAgL48x-8dDkH23JykibVSQju-f7e-1xreHWXzWLHs1NqBbre0dEwK3HX_xM0LjUz77Krppgegoutpf5qaKg3l-_xMINmf', tag: 'fNYLqpUe84KD45lvDiaBAQ', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-5.12 - Protecting Content Only', deterministic: true, input: { plaintext: 'You can trust us to stick with you through thick and thin–to the bitter end. And you can trust us to keep any secret of yours–closer than you keep it yourself. But you cannot trust us to let you face trouble alone, and go off without a word. We are your friends, Frodo.', key: { kty: 'oct', ext: false, kid: '81b20965-8332-43d9-a468-82160ad91ac8', use: 'enc', alg: 'A128KW', k: 'GZy6sIZ6wl9NJOKB-jnmVQ', }, alg: 'A128KW', enc: 'A128GCM', }, generated: { cek: 'KBooAFl30QPV3vkcZlXnzQ', iv: 'YihBoVOGsR1l7jCD', }, encrypting_key: {}, encrypting_content: { unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', }, }, output: { json: { recipients: [ { encrypted_key: '244YHfO_W7RMpQW81UjQrZcq5LSyqiPv', }, ], unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', }, iv: 'YihBoVOGsR1l7jCD', ciphertext: 'qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF', tag: 'e2m0Vm7JvjK2VpCKXS-kyg', }, json_flat: { unprotected: { alg: 'A128KW', kid: '81b20965-8332-43d9-a468-82160ad91ac8', enc: 'A128GCM', }, encrypted_key: '244YHfO_W7RMpQW81UjQrZcq5LSyqiPv', iv: 'YihBoVOGsR1l7jCD', ciphertext: 'qtPIMMaOBRgASL10dNQhOa7Gqrk7Eal1vwht7R4TT1uq-arsVCPaIeFwQfzrSS6oEUWbBtxEasE0vC6r7sphyVziMCVJEuRJyoAHFSP3eqQPb4Ic1SDSqyXjw_L3svybhHYUGyQuTmUQEDjgjJfBOifwHIsDsRPeBz1NomqeifVPq5GTCWFo5k_MNIQURR2Wj0AHC2k7JZfu2iWjUHLF8ExFZLZ4nlmsvJu_mvifMYiikfNfsZAudISOa6O73yPZtL04k_1FI7WDfrb2w7OqKLWDXzlpcxohPVOLQwpA3mFNRKdY-bQz4Z4KX9lfz1cne31N4-8BKmojpw-OdQjKdLOGkC445Fb_K1tlDQXw2sBF', tag: 'e2m0Vm7JvjK2VpCKXS-kyg', }, }, }, ] ================================================ FILE: cookbook/jws.mjs ================================================ export default [ { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.1 - RSA v1.5 Signature', deterministic: true, input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'RSA', ext: false, kid: 'bilbo.baggins@hobbiton.example', use: 'sig', n: 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', e: 'AQAB', d: 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', p: '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', q: 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', dp: 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', dq: 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', qi: '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', }, alg: 'RS256', }, signing: { protected: { alg: 'RS256', kid: 'bilbo.baggins@hobbiton.example', }, }, output: { compact: 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { protected: 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', protected: 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc8037#appendix-A.4 - Ed25519 Signing', deterministic: false, // https://github.com/WICG/webcrypto-secure-curves/issues/28 input: { payload: 'Example of Ed25519 signing', key: { kty: 'OKP', ext: false, crv: 'Ed25519', d: 'nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A', x: '11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo', }, alg: 'EdDSA', }, signing: { protected: { alg: 'EdDSA', }, }, output: { compact: 'eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg', json: { payload: 'RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc', signatures: [ { protected: 'eyJhbGciOiJFZERTQSJ9', signature: 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg', }, ], }, json_flat: { payload: 'RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc', protected: 'eyJhbGciOiJFZERTQSJ9', signature: 'hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.2 - RSA-PSS Signature', input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'RSA', ext: false, kid: 'bilbo.baggins@hobbiton.example', use: 'sig', n: 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', e: 'AQAB', d: 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', p: '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', q: 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', dp: 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', dq: 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', qi: '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4', }, alg: 'PS384', }, signing: { protected: { alg: 'PS384', kid: 'bilbo.baggins@hobbiton.example', }, }, output: { compact: 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { protected: 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', protected: 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.3 - ECDSA Signature', input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'EC', ext: false, kid: 'bilbo.baggins@hobbiton.example', use: 'sig', crv: 'P-521', x: 'AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt', y: 'AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1', d: 'AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt', }, alg: 'ES512', }, signing: { protected: { alg: 'ES512', kid: 'bilbo.baggins@hobbiton.example', }, }, output: { compact: 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { protected: 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', protected: 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhbXBsZSJ9', signature: 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.4 - HMAC-SHA2 Integrity Protection', deterministic: true, input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'oct', ext: false, kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', use: 'sig', alg: 'HS256', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', }, alg: 'HS256', }, signing: { protected: { alg: 'HS256', kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, }, output: { compact: 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4.s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { protected: 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9', signature: 's0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', protected: 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9', signature: 's0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.6 - Protecting Specific Header Fields', deterministic: true, input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'oct', ext: false, kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', use: 'sig', alg: 'HS256', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', }, alg: 'HS256', }, signing: { protected: { alg: 'HS256', }, unprotected: { kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, }, output: { json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { protected: 'eyJhbGciOiJIUzI1NiJ9', header: { kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, signature: 'bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', protected: 'eyJhbGciOiJIUzI1NiJ9', header: { kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, signature: 'bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7520#section-4.7 - Protecting Content Only', deterministic: true, input: { payload: "It’s a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there’s no knowing where you might be swept off to.", key: { kty: 'oct', ext: false, kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', use: 'sig', alg: 'HS256', k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg', }, alg: 'HS256', }, signing: { unprotected: { alg: 'HS256', kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, }, output: { json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', signatures: [ { header: { alg: 'HS256', kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, signature: 'xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4', header: { alg: 'HS256', kid: '018c0ae5-4d9b-471b-bfd6-eef314bc7037', }, signature: 'xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk', }, }, }, { title: 'https://www.rfc-editor.org/rfc/rfc7797#section-4.1 - { "b64": false } JSON only', deterministic: true, input: { payload: '$.02', key: { kty: 'oct', ext: false, alg: 'HS256', k: 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow', }, alg: 'HS256', }, signing: { protected: { alg: 'HS256', b64: false, crit: ['b64'], }, }, output: { json: { payload: '$.02', signatures: [ { protected: 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19', signature: 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY', }, ], }, json_flat: { payload: '$.02', protected: 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19', signature: 'A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY', }, }, }, { title: 'ietf-cose-dilithium - ML-DSA-44', deterministic: false, input: { payload: 'It’s a dangerous business, Frodo, going out your door.', key: { kty: 'AKP', alg: 'ML-DSA-44', pub: 'unH59k4RuutY-pxvu24U5h8YZD2rSVtHU5qRZsoBmBMcRPgmu9VuNOVdteXi1zNIXjnqJg_GAAxepLqA00Vc3lO0bzRIKu39VFD8Lhuk8l0V-cFEJC-zm7UihxiQMMUEmOFxe3x1ixkKZ0jqmqP3rKryx8tSbtcXyfea64QhT6XNje2SoMP6FViBDxLHBQo2dwjRls0k5a-XSQSu2OTOiHLoaWsLe8pQ5FLNfTDqmkrawDEdZyxr3oSWJAsHQxRjcIiVzZuvwxYy1zl2STiP2vy_fTBaPemkleynQzqPg7oPCyXEE8bjnJbrfWkbNNN8438e6tHPIX4l7zTuzz98YPhLjt_d6EBdT4MldsYe-Y4KLyjaGHcAlTkk9oa5RhRwW89T0z_t1DSO3dvfKLUGXh8gd1BD6Fz5MfgpF5NjoafnQEqDjsAAhrCXY4b-Y3yYJEdX4_dp3dRGdHG_rWcPmgX4JG7lCnser4f8QGnDriqiAzJYEXeS8LzUngg_0bx0lqv_KcyU5IaLISFO0xZSU5mmEPvdSoDnyAcV8pV44qhLtAvd29n0ehG259oRihtljTWeiu9V60a1N2tbZVl5mEqSK-6_xZvNYA1TCdzNctvweH24unV7U3wer9XA9Q6kvJWDVJ4oKaQsKMrCSMlteBJMRxWbGK7ddUq6F7GdQw-3j2M-qdJvVKm9UPjY9rc1lPgol25-oJxTu7nxGlbJUH-4m5pevAN6NyZ6lfhbjWTKlxkrEKZvQXs_Yf6cpXEwpI_ZJeriq1UC1XHIpRkDwdOY9MH3an4RdDl2r9vGl_IwlKPNdh_5aF3jLgn7PCit1FNJAwC8fIncAXgAlgcXIpRXdfJk4bBiO89GGccSyDh2EgXYdpG3XvNgGWy7npuSoNTE7WIyblAk13UQuO4sdCbMIuriCdyfE73mvwj15xgb07RZRQtFGlFTmnFcIdZ90zDrWXDbANntv7KCKwNvoTuv64bY3HiGbj-NQ-U9eMylWVpvr4hrXcES8c9K3PqHWADZC0iIOvlzFv4VBoc_wVflcOrL_SIoaNFCNBAZZq-2v5lAgpJTqVOtqJ_HVraoSfcKy5g45p-qULunXj6Jwq21fobQiKubBKKOZwcJFyJD7F4ACKXOrz-HIvSHMCWW_9dVrRuCpJw0s0aVFbRqopDNhu446nqb4_EDYQM1tTHMozPd_jKxRRD0sH75X8ZoToxFSpLBDbtdWcenxj-zBf6IGWfZnmaetjKEBYJWC7QDQx1A91pJVJCEgieCkoIfTqkeQuePpIyu48g2FG3P1zjRF-kumhUTfSjo5qS0YiZQy0E1BMs6M11EvuxXRsHClLHoy5nLYI2Sj4zjVjYyxSHyPRPGGo9hwB34yWxzYNtPPGiqXS_dNCpi_zRZwRY4lCGrQ-hYTEWIK1Dm5OlttvC4_eiQ1dv63NiGkLRJ5kJA3bICN0fzCDY-MBqnd1cWn8YVBijVkgtaoascjL9EywDgJdeHnXK0eeOvUxHHhXJVkNqcibn8O4RQdpVU60TSA-uiu675ytIjcBHC6kTv8A8pmkj_4oypPd-F92YIJC741swkYQoeIHj8rE-ThcMUkF7KqC5VORbZTRp8HsZSqgiJcIPaouuxd1-8Rxrid3fXkE6p8bkrysPYoxWEJgh7ZFsRCPDWX-yTeJwFN0PKFP1j0F6YtlLfK5wv-c4F8ZQHA_-yc_gODicy7KmWDZgbTP07e7gEWzw4MFRrndjbDQ', priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', }, alg: 'ML-DSA-44', }, signing: { protected: { alg: 'ML-DSA-44', }, }, output: { compact: 'eyJhbGciOiJNTC1EU0EtNDQiLCJraWQiOiJUNHhsNzBTN01UNlplcTZyOVY5ZlBKR1ZuNzZ3Zm5YSjIxLWd5bzBHdTZvIn0.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.knI1Q_9CIzLH5Xy94Kkc7WVKqZcAgtJ3mNf0GUj1uLA6YXAWFJfXkh-zQxUtEl3UIC7zPCiUwKTDR6ZsuUmFj8Ctb_6aH64hElN7weS_1m5okCy8GqHNL2lsfclCH3Y2f4QNP-DLVS1XsuboDA7Dw3ir2IdYKIfWJyIU7ROHgd24nuun1zJbxcLJC2EKt2M8R0wZudcIE9nm5oPzYXq0z-hPsKoXp9leVYkqgMmO9Lo8SP_1YYIEth3B8v-GuP249KDTFRKPjISmK4aPCknjtjihHsQVv2XePXxKExatHl4qhsiiW-y-EJXa1Kfw4WYpLA7B4_5Ids--cIJmIx7f6xxAWKh5qoBWq1QIOaaFuzsAraRW3NOEuzThew1En85gI3GcRTZGp-VDGyxHm0Al04cyWo2bxAVOF0fbDc265iP2mCNw6Qg10jIJeAhGB4OAMYcBUWJAG0l1MN1U_koEmGh5dXKnQTRl461ea_Cq3DLkcA2Dj2woWUFyDTmQ8oO_yheASfJacyRm7_suj88z5XFNo8F53P8OxTG9xUPlrwvH-TAq7AH3NU4SNXApyVKTU3zhx1tJ34nlTILcTujXVJVo_f0DZfUxr6JSCYqvy4z1Kl0wDQzd55aopyFtQxvOPhcCHbAN34g2Ug750Jm835fl7NOxcqoMbuTcgH68kr37M-Pdh2K9WazXUJgCupgdIWW8WjfOjmTiF59CrVtfVtK2qDzF40OENCfqtNPQlZe5cN5p0P8arj4USB8HCPh7NdqQBAeWrw0wsYhdiM39lrSkA8mLRYMhZnqKGCTPCrHXDdEjRKYRNaqIUT44laYl5c27K0v-ozjKPu6tzEhkYSC4XZ3LehEFtmAzOE0mHbhKMgXqjoPJjOrGIPibX3jwK8_Q5RmMOXtXo8R3vXfBaUdQoLeeyywNYE0nIcsl4z5a8_utwEFiVf0VK2pdviyiOPVSi3zOMAmqz6gFhVy8aMMQOWZAEAuTyDw7ZWG6diwptmrgSXZotW63I19S2ZH7keCXRIq_pFLuYhOuG6dD4MkouILRdC9bXZMLrNDq7COpUOO86aQVlYd0pR935WpUw-V6obSRnHlRFZSmUSIB7h1Q0ImciRzojN93Xhw7qpzGzdzDEO3OOTayXaSG_0YHQyy-eH4hBbmgt_LBx120g1eY4XHeHFRfTfetHkL5ZZusX1jQ_nk9ez4XBG_6hRtTNSuVBsYlH8-KUuR5-qTP8dkvRf8Wk2hHoUr2sz5YO_xDFCMMTrt8ahiMyfjo5ih5Fwo3riFbFUGKibniTLXspFd4spcNK_WchlZLRgkPK4jh6Z_X8JJkHxvQhpyouHQFyGxgBrl24x-_EB1zbWMhJthmm8DiKt-nzKaJz8Cju1-HwCpg76CRqRsEz2hyKEpbb4M5KQSj3AsENCroVmQ5QIv3K2XNRkve4vjBmP6sV2b6GSY_UeRvPElA7SUgBGTKbn-c0aYhBuB8plPhRTBa55_cFqAmNmavF1-fdMktJuIaH2f-K0zZCzbHw54998T7kIWgyMsyGCAvynEB_khOqwT7tCjg5HQ8SIjdnRYW0kjZfjt5LJbGA-PnRo8gPVQVGeYDP2vsSXhNJY94AitKCY1srcSsuYDrhNBKrnoJ1uEsMPVHsgFw_ZHMyAEaVQughSNW4fm8q6_1Nv4zLutDITzmAL6a6i6-WS6QRIs_4VUtwr5cXXIFDDeHVWeGcNivQ6W9urEUP4crguiq7z_DTiYaGfUksub-T7mw0zU8ZoOSd5pUTpJLv-IYIUAl6CscHvunnRLEKqpW1Sa1dcFZs5VP4AfR3mg7wX4Vlq1AHnpFxE2L1LZiKoTc9jDEOvTDkxr86gMkwMm6RdyPF_q48AVJ1br8Qp88-4B84X52zZ5cw-IJYe-HiVJ29LpeYm340_rWivpy-UB5i9TKlMrxf94y1okzZTPbP3_v1_XX0nE7RTLz98EA96euJ7l3EpbEqks7mh6i1FJNnvvlM_u29sYobJ6PUT-i1VlQnF_JBARKEz74pBXm1l5Y5Lo15rsIlaQHinUBCO8fHCHI59LAfKusN4JmodDqLYwkWijEL_sfrC6LtrbXqpM1pw09zSrs_tS1RQ-LnWHuPrU5KLCzv53JKrh8lU_cdBowe_F-Ib_Ui4bQ2FME-0mnyG0XijHUsrGMZ9dfowvIkr83JpqwlFOZAwMmSGPNPEJRw9kDshjotndUB5S1UCfv_U4IoVn7WgvxeCS-BBxqyWfh7YTdf73EnmGwVYxVjlXaHCeeTZmUacnT4MQUAcbFjTq6BBlboAQGWP2FZWpd6HNnruv744VeWmfgLk9z5567wFhwuXMkmE2xvDo4wP80xutjUfsePx5YkLxhY1XsWqTZr19tInxJWWq8RLZsWPmtq5wZ5ucBMasCLpOABenYZdSAcQNhC73wLS0Z2s1HQhBoIl7lr1p372LZs_Seu1u_8Fo7DoJqRpKaNoc2_JUMmn7TUZS8zLyzxgeq8R8iNbRP20DwDBNXocsTDBKaQrtB-QiEPySQtJa4G61XeNZyh5aGzfoWZ9OmjZG9pbbehcqwIrt-ESjPyeT6sfSrvOfTZr7fBXwpUs2rS4BrlNse5g_h8CQiik8aaOTOEPkXiyg4s5DewRlgDZHS-3g-YXPUIBNO62_HxknkMpkJvKW-tkvDbgtxvy4nG80ul6W_KeRsoEKDTRYNKZWxXjZITNa0h6agnwNCJKEbFg3Qhre394c0i60mfP9YIgKTXrCX3Yt2eX-6mPzYmLbSbV5jH69v6WZqYV2WAj-9DU0diR4hOfYQaJnBZhTtKb-SQsYiFuN1BDJ3v9eM9K8hq91NBdCHVa-Thk9Dov-JkcTZnZGRRyW5yXHUV4NOEltBXh8GkjjDvs5Yo3u-2rPCXjK1aGPSI1W8BaUJLQY5sbfAVCAuUHBv-Vlh5Qamt-lgeKguhqTSuy-tjabOb5kiBOG7xGQt3z-XYXtnWFDCii-5h11XfZsQ-xQxy8gSfdMz4hDK9Nw_VQt6fzWiQY0Th_dHzVki0MUfVfsDUjgblhD6j0wgbs3zdj-GM3rtt8oit0wXx11bIOaOKgf07tP0wimVXMRqRWe7LCUAKTE5PkRKU1x_h4iusrzi5uwKDhc4SmRwm6KssNrmCAkiNDZCREVKd3yMnrjA4PAGDzdKWVplcHJ6jKmrsbrEztHd9QAAAAAAAAAAAAAAABIfMEQ', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signatures: [ { protected: 'eyJhbGciOiJNTC1EU0EtNDQiLCJraWQiOiJUNHhsNzBTN01UNlplcTZyOVY5ZlBKR1ZuNzZ3Zm5YSjIxLWd5bzBHdTZvIn0', signature: 'knI1Q_9CIzLH5Xy94Kkc7WVKqZcAgtJ3mNf0GUj1uLA6YXAWFJfXkh-zQxUtEl3UIC7zPCiUwKTDR6ZsuUmFj8Ctb_6aH64hElN7weS_1m5okCy8GqHNL2lsfclCH3Y2f4QNP-DLVS1XsuboDA7Dw3ir2IdYKIfWJyIU7ROHgd24nuun1zJbxcLJC2EKt2M8R0wZudcIE9nm5oPzYXq0z-hPsKoXp9leVYkqgMmO9Lo8SP_1YYIEth3B8v-GuP249KDTFRKPjISmK4aPCknjtjihHsQVv2XePXxKExatHl4qhsiiW-y-EJXa1Kfw4WYpLA7B4_5Ids--cIJmIx7f6xxAWKh5qoBWq1QIOaaFuzsAraRW3NOEuzThew1En85gI3GcRTZGp-VDGyxHm0Al04cyWo2bxAVOF0fbDc265iP2mCNw6Qg10jIJeAhGB4OAMYcBUWJAG0l1MN1U_koEmGh5dXKnQTRl461ea_Cq3DLkcA2Dj2woWUFyDTmQ8oO_yheASfJacyRm7_suj88z5XFNo8F53P8OxTG9xUPlrwvH-TAq7AH3NU4SNXApyVKTU3zhx1tJ34nlTILcTujXVJVo_f0DZfUxr6JSCYqvy4z1Kl0wDQzd55aopyFtQxvOPhcCHbAN34g2Ug750Jm835fl7NOxcqoMbuTcgH68kr37M-Pdh2K9WazXUJgCupgdIWW8WjfOjmTiF59CrVtfVtK2qDzF40OENCfqtNPQlZe5cN5p0P8arj4USB8HCPh7NdqQBAeWrw0wsYhdiM39lrSkA8mLRYMhZnqKGCTPCrHXDdEjRKYRNaqIUT44laYl5c27K0v-ozjKPu6tzEhkYSC4XZ3LehEFtmAzOE0mHbhKMgXqjoPJjOrGIPibX3jwK8_Q5RmMOXtXo8R3vXfBaUdQoLeeyywNYE0nIcsl4z5a8_utwEFiVf0VK2pdviyiOPVSi3zOMAmqz6gFhVy8aMMQOWZAEAuTyDw7ZWG6diwptmrgSXZotW63I19S2ZH7keCXRIq_pFLuYhOuG6dD4MkouILRdC9bXZMLrNDq7COpUOO86aQVlYd0pR935WpUw-V6obSRnHlRFZSmUSIB7h1Q0ImciRzojN93Xhw7qpzGzdzDEO3OOTayXaSG_0YHQyy-eH4hBbmgt_LBx120g1eY4XHeHFRfTfetHkL5ZZusX1jQ_nk9ez4XBG_6hRtTNSuVBsYlH8-KUuR5-qTP8dkvRf8Wk2hHoUr2sz5YO_xDFCMMTrt8ahiMyfjo5ih5Fwo3riFbFUGKibniTLXspFd4spcNK_WchlZLRgkPK4jh6Z_X8JJkHxvQhpyouHQFyGxgBrl24x-_EB1zbWMhJthmm8DiKt-nzKaJz8Cju1-HwCpg76CRqRsEz2hyKEpbb4M5KQSj3AsENCroVmQ5QIv3K2XNRkve4vjBmP6sV2b6GSY_UeRvPElA7SUgBGTKbn-c0aYhBuB8plPhRTBa55_cFqAmNmavF1-fdMktJuIaH2f-K0zZCzbHw54998T7kIWgyMsyGCAvynEB_khOqwT7tCjg5HQ8SIjdnRYW0kjZfjt5LJbGA-PnRo8gPVQVGeYDP2vsSXhNJY94AitKCY1srcSsuYDrhNBKrnoJ1uEsMPVHsgFw_ZHMyAEaVQughSNW4fm8q6_1Nv4zLutDITzmAL6a6i6-WS6QRIs_4VUtwr5cXXIFDDeHVWeGcNivQ6W9urEUP4crguiq7z_DTiYaGfUksub-T7mw0zU8ZoOSd5pUTpJLv-IYIUAl6CscHvunnRLEKqpW1Sa1dcFZs5VP4AfR3mg7wX4Vlq1AHnpFxE2L1LZiKoTc9jDEOvTDkxr86gMkwMm6RdyPF_q48AVJ1br8Qp88-4B84X52zZ5cw-IJYe-HiVJ29LpeYm340_rWivpy-UB5i9TKlMrxf94y1okzZTPbP3_v1_XX0nE7RTLz98EA96euJ7l3EpbEqks7mh6i1FJNnvvlM_u29sYobJ6PUT-i1VlQnF_JBARKEz74pBXm1l5Y5Lo15rsIlaQHinUBCO8fHCHI59LAfKusN4JmodDqLYwkWijEL_sfrC6LtrbXqpM1pw09zSrs_tS1RQ-LnWHuPrU5KLCzv53JKrh8lU_cdBowe_F-Ib_Ui4bQ2FME-0mnyG0XijHUsrGMZ9dfowvIkr83JpqwlFOZAwMmSGPNPEJRw9kDshjotndUB5S1UCfv_U4IoVn7WgvxeCS-BBxqyWfh7YTdf73EnmGwVYxVjlXaHCeeTZmUacnT4MQUAcbFjTq6BBlboAQGWP2FZWpd6HNnruv744VeWmfgLk9z5567wFhwuXMkmE2xvDo4wP80xutjUfsePx5YkLxhY1XsWqTZr19tInxJWWq8RLZsWPmtq5wZ5ucBMasCLpOABenYZdSAcQNhC73wLS0Z2s1HQhBoIl7lr1p372LZs_Seu1u_8Fo7DoJqRpKaNoc2_JUMmn7TUZS8zLyzxgeq8R8iNbRP20DwDBNXocsTDBKaQrtB-QiEPySQtJa4G61XeNZyh5aGzfoWZ9OmjZG9pbbehcqwIrt-ESjPyeT6sfSrvOfTZr7fBXwpUs2rS4BrlNse5g_h8CQiik8aaOTOEPkXiyg4s5DewRlgDZHS-3g-YXPUIBNO62_HxknkMpkJvKW-tkvDbgtxvy4nG80ul6W_KeRsoEKDTRYNKZWxXjZITNa0h6agnwNCJKEbFg3Qhre394c0i60mfP9YIgKTXrCX3Yt2eX-6mPzYmLbSbV5jH69v6WZqYV2WAj-9DU0diR4hOfYQaJnBZhTtKb-SQsYiFuN1BDJ3v9eM9K8hq91NBdCHVa-Thk9Dov-JkcTZnZGRRyW5yXHUV4NOEltBXh8GkjjDvs5Yo3u-2rPCXjK1aGPSI1W8BaUJLQY5sbfAVCAuUHBv-Vlh5Qamt-lgeKguhqTSuy-tjabOb5kiBOG7xGQt3z-XYXtnWFDCii-5h11XfZsQ-xQxy8gSfdMz4hDK9Nw_VQt6fzWiQY0Th_dHzVki0MUfVfsDUjgblhD6j0wgbs3zdj-GM3rtt8oit0wXx11bIOaOKgf07tP0wimVXMRqRWe7LCUAKTE5PkRKU1x_h4iusrzi5uwKDhc4SmRwm6KssNrmCAkiNDZCREVKd3yMnrjA4PAGDzdKWVplcHJ6jKmrsbrEztHd9QAAAAAAAAAAAAAAABIfMEQ', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', protected: 'eyJhbGciOiJNTC1EU0EtNDQiLCJraWQiOiJUNHhsNzBTN01UNlplcTZyOVY5ZlBKR1ZuNzZ3Zm5YSjIxLWd5bzBHdTZvIn0', signature: 'knI1Q_9CIzLH5Xy94Kkc7WVKqZcAgtJ3mNf0GUj1uLA6YXAWFJfXkh-zQxUtEl3UIC7zPCiUwKTDR6ZsuUmFj8Ctb_6aH64hElN7weS_1m5okCy8GqHNL2lsfclCH3Y2f4QNP-DLVS1XsuboDA7Dw3ir2IdYKIfWJyIU7ROHgd24nuun1zJbxcLJC2EKt2M8R0wZudcIE9nm5oPzYXq0z-hPsKoXp9leVYkqgMmO9Lo8SP_1YYIEth3B8v-GuP249KDTFRKPjISmK4aPCknjtjihHsQVv2XePXxKExatHl4qhsiiW-y-EJXa1Kfw4WYpLA7B4_5Ids--cIJmIx7f6xxAWKh5qoBWq1QIOaaFuzsAraRW3NOEuzThew1En85gI3GcRTZGp-VDGyxHm0Al04cyWo2bxAVOF0fbDc265iP2mCNw6Qg10jIJeAhGB4OAMYcBUWJAG0l1MN1U_koEmGh5dXKnQTRl461ea_Cq3DLkcA2Dj2woWUFyDTmQ8oO_yheASfJacyRm7_suj88z5XFNo8F53P8OxTG9xUPlrwvH-TAq7AH3NU4SNXApyVKTU3zhx1tJ34nlTILcTujXVJVo_f0DZfUxr6JSCYqvy4z1Kl0wDQzd55aopyFtQxvOPhcCHbAN34g2Ug750Jm835fl7NOxcqoMbuTcgH68kr37M-Pdh2K9WazXUJgCupgdIWW8WjfOjmTiF59CrVtfVtK2qDzF40OENCfqtNPQlZe5cN5p0P8arj4USB8HCPh7NdqQBAeWrw0wsYhdiM39lrSkA8mLRYMhZnqKGCTPCrHXDdEjRKYRNaqIUT44laYl5c27K0v-ozjKPu6tzEhkYSC4XZ3LehEFtmAzOE0mHbhKMgXqjoPJjOrGIPibX3jwK8_Q5RmMOXtXo8R3vXfBaUdQoLeeyywNYE0nIcsl4z5a8_utwEFiVf0VK2pdviyiOPVSi3zOMAmqz6gFhVy8aMMQOWZAEAuTyDw7ZWG6diwptmrgSXZotW63I19S2ZH7keCXRIq_pFLuYhOuG6dD4MkouILRdC9bXZMLrNDq7COpUOO86aQVlYd0pR935WpUw-V6obSRnHlRFZSmUSIB7h1Q0ImciRzojN93Xhw7qpzGzdzDEO3OOTayXaSG_0YHQyy-eH4hBbmgt_LBx120g1eY4XHeHFRfTfetHkL5ZZusX1jQ_nk9ez4XBG_6hRtTNSuVBsYlH8-KUuR5-qTP8dkvRf8Wk2hHoUr2sz5YO_xDFCMMTrt8ahiMyfjo5ih5Fwo3riFbFUGKibniTLXspFd4spcNK_WchlZLRgkPK4jh6Z_X8JJkHxvQhpyouHQFyGxgBrl24x-_EB1zbWMhJthmm8DiKt-nzKaJz8Cju1-HwCpg76CRqRsEz2hyKEpbb4M5KQSj3AsENCroVmQ5QIv3K2XNRkve4vjBmP6sV2b6GSY_UeRvPElA7SUgBGTKbn-c0aYhBuB8plPhRTBa55_cFqAmNmavF1-fdMktJuIaH2f-K0zZCzbHw54998T7kIWgyMsyGCAvynEB_khOqwT7tCjg5HQ8SIjdnRYW0kjZfjt5LJbGA-PnRo8gPVQVGeYDP2vsSXhNJY94AitKCY1srcSsuYDrhNBKrnoJ1uEsMPVHsgFw_ZHMyAEaVQughSNW4fm8q6_1Nv4zLutDITzmAL6a6i6-WS6QRIs_4VUtwr5cXXIFDDeHVWeGcNivQ6W9urEUP4crguiq7z_DTiYaGfUksub-T7mw0zU8ZoOSd5pUTpJLv-IYIUAl6CscHvunnRLEKqpW1Sa1dcFZs5VP4AfR3mg7wX4Vlq1AHnpFxE2L1LZiKoTc9jDEOvTDkxr86gMkwMm6RdyPF_q48AVJ1br8Qp88-4B84X52zZ5cw-IJYe-HiVJ29LpeYm340_rWivpy-UB5i9TKlMrxf94y1okzZTPbP3_v1_XX0nE7RTLz98EA96euJ7l3EpbEqks7mh6i1FJNnvvlM_u29sYobJ6PUT-i1VlQnF_JBARKEz74pBXm1l5Y5Lo15rsIlaQHinUBCO8fHCHI59LAfKusN4JmodDqLYwkWijEL_sfrC6LtrbXqpM1pw09zSrs_tS1RQ-LnWHuPrU5KLCzv53JKrh8lU_cdBowe_F-Ib_Ui4bQ2FME-0mnyG0XijHUsrGMZ9dfowvIkr83JpqwlFOZAwMmSGPNPEJRw9kDshjotndUB5S1UCfv_U4IoVn7WgvxeCS-BBxqyWfh7YTdf73EnmGwVYxVjlXaHCeeTZmUacnT4MQUAcbFjTq6BBlboAQGWP2FZWpd6HNnruv744VeWmfgLk9z5567wFhwuXMkmE2xvDo4wP80xutjUfsePx5YkLxhY1XsWqTZr19tInxJWWq8RLZsWPmtq5wZ5ucBMasCLpOABenYZdSAcQNhC73wLS0Z2s1HQhBoIl7lr1p372LZs_Seu1u_8Fo7DoJqRpKaNoc2_JUMmn7TUZS8zLyzxgeq8R8iNbRP20DwDBNXocsTDBKaQrtB-QiEPySQtJa4G61XeNZyh5aGzfoWZ9OmjZG9pbbehcqwIrt-ESjPyeT6sfSrvOfTZr7fBXwpUs2rS4BrlNse5g_h8CQiik8aaOTOEPkXiyg4s5DewRlgDZHS-3g-YXPUIBNO62_HxknkMpkJvKW-tkvDbgtxvy4nG80ul6W_KeRsoEKDTRYNKZWxXjZITNa0h6agnwNCJKEbFg3Qhre394c0i60mfP9YIgKTXrCX3Yt2eX-6mPzYmLbSbV5jH69v6WZqYV2WAj-9DU0diR4hOfYQaJnBZhTtKb-SQsYiFuN1BDJ3v9eM9K8hq91NBdCHVa-Thk9Dov-JkcTZnZGRRyW5yXHUV4NOEltBXh8GkjjDvs5Yo3u-2rPCXjK1aGPSI1W8BaUJLQY5sbfAVCAuUHBv-Vlh5Qamt-lgeKguhqTSuy-tjabOb5kiBOG7xGQt3z-XYXtnWFDCii-5h11XfZsQ-xQxy8gSfdMz4hDK9Nw_VQt6fzWiQY0Th_dHzVki0MUfVfsDUjgblhD6j0wgbs3zdj-GM3rtt8oit0wXx11bIOaOKgf07tP0wimVXMRqRWe7LCUAKTE5PkRKU1x_h4iusrzi5uwKDhc4SmRwm6KssNrmCAkiNDZCREVKd3yMnrjA4PAGDzdKWVplcHJ6jKmrsbrEztHd9QAAAAAAAAAAAAAAABIfMEQ', }, }, }, { title: 'ietf-cose-dilithium - ML-DSA-65', deterministic: false, input: { payload: 'It’s a dangerous business, Frodo, going out your door.', key: { kty: 'AKP', alg: 'ML-DSA-65', pub: 'QksvJn5Y1bO0TXGs_Gpla7JpUNV8YdsciAvPof6rRD8JQquL2619cIq7w1YHj22ZolInH-YsdAkeuUr7m5JkxQqIjg3-2AzV-yy9NmfmDVOevkSTAhnNT67RXbs0VaJkgCufSbzkLudVD-_91GQqVa3mk4aKRgy-wD9PyZpOMLzP-opHXlOVOWZ067galJN1h4gPbb0nvxxPWp7kPN2LDlOzt_tJxzrfvC1PjFQwNSDCm_l-Ju5X2zQtlXyJOTZSLQlCtB2C7jdyoAVwrftUXBFDkisElvgmoKlwBks23fU0tfjhwc0LVWXqhGtFQx8GGBQ-zol3e7P2EXmtIClf4KbgYq5u7Lwu848qwaItyTt7EmM2IjxVth64wHlVQruy3GXnIurcaGb_qWg764qZmteoPl5uAWwuTDX292Sa071S7GfsHFxue5lydxIYvpVUu6dyfwuExEubCovYMfz_LJd5zNTKMMatdbBJg-Qd6JPuXznqc1UYC3CccEXCLTOgg_auB6EUdG0b_cy-5bkEOHm7Wi4SDipGNig_ShzUkkot5qSqPZnd2I9IqqToi_0ep2nYLBB3ny3teW21Qpccoom3aGPt5Zl7fpzhg7Q8zsJ4sQ2SuHRCzgQ1uxYlFx21VUtHAjnFDSoMOkGyo4gH2wcLR7-z59EPPNl51pljyNefgCnMSkjrBPyz1wiET-uqi23f8Bq2TVk1jmUFxOwdfLsU7SIS30WOzvwD_gMDexUFpMlEQyL1-Y36kaTLjEWGCi2tx1FTULttQx5JpryPW6lW5oKw5RMyGpfRliYCiRyQePYqipZGoxOHpvCWhCZIN4meDY7H0RxWWQEpiyCzRQgWkOtMViwao6Jb7wZWbLNMebwLJeQJXWunk-gTEeQaMykVJobwDUiX-E_E7fSybVRTZXherY1jrvZKh8C5Gi5VADg5Vs319uN8-dVILRyOOlvjjxclmsRcn6HEvTvxd9MS7lKm2gI8BXIqhzgnTdqNGwTpmDHPV8hygqJWxWXCltBSSgY6OkGkioMAmXjZjYq_Ya9o6AE7WU_hUdm-wZmQLExwtJWEIBdDxrUxA9L9JL3weNyQtaGItPjXcheZiNBBbJTUxXwIYLnXtT1M0mHzMqGFFWXVKsN_AIdHyv4yDzY9m-tuQRfbQ_2K7r5eDOL1Tj8DZ-s8yXG74MMBqOUvlglJNgNcbuPKLRPbSDoN0E3BYkfeDgiUrXy34a5-vU-PkAWCsgAh539wJUUBxqw90V1Du7eTHFKDJEMSFYwusbPhEX4ZTwoeTHg--8Ysn4HCFWLQ00pfBCteqvMvMflcWwVfTnogcPsJb1bEFVSc3nTzhk6Ln8J-MplyS0Y5mGBEtVko_WlyeFsoDCWj4hqrgU7L-ww8vsCRSQfskH8lodiLzj0xmugiKjWUXbYq98x1zSnB9dmPy5P3UNwwMQdpebtR38N9I-jup4Bzok0-JsaOe7EORZ8ld7kAgDWa4K7BAxjc2eD540Apwxs-VLGFVkXbQgYYeDNG2tW1Xt20-XezJqZVUl6-IZXsqc7DijwNInO3fT5o8ZAcLKUUlzSlEXe8sIlHaxjLoJ-oubRtlKKUbzWOHeyxmYZSxYqQhSQj4sheedGXJEYWJ-Y5DRqB-xpy-cftxL10fdXIUhe1hWFBAoQU3b5xRY8KCytYnfLhsFF4O49xhnax3vuumLpJbCqTXpLureoKg5PvWfnpFPB0P-ZWQN35mBzqbb3ZV6U0rU55DvyXTuiZOK2Z1TxbaAd1OZMmg0cpuzewgueV-Nh_UubIqNto5RXCd7vqgqdXDUKAiWyYegYIkD4wbGMqIjxV8Oo2ggOcSj9UQPS1rD5u0rLckAzsxyty9Q5JsmKa0w8Eh7Jwe4Yob4xPVWWbJfm916avRgzDxXo5gmY7txdGFYHhlolJKdhBU9h6f0gtKEtbiUzhp4IWsqAR8riHQs7lLVEz6P537a4kL1r5FjfDf_yjJDBQmy_kdWMDqaNln-MlKK8eENjUO-qZGy0Ql4bMZtNbHXjfJUuSzapA-RqYfkqSLKgQUOW8NTDKhUk73yqCU3TQqDEKaGAoTsPscyMm7u_8QrvUK8kbc-XnxrWZ0BZJBjdinzh2w-QvjbWQ5mqFp4OMgY94__tIU8vvCUNJiYA1RdyodlfPfH5-avpxOCvBD6C7ZIDyQ-6huGEQEAb6DP8ydWIZQ8xY603DoEKKXkJWcP6CJo3nHFEdj_vcEbDQ-WESDpcQFa1fRIiGuALj-sEWcjGdSHyE8QATOcuWl4TLVzRPKAf4tCXx1zyvhJbXQu0jf0yfzVpOhPun4n-xqK4SxPBCeuJOkQ2VG9jDXWH4pnjbAcrqjveJqVti7huMXTLGuqU2uoihBw6mGqu_WSlOP2-XTEyRyvxbv2t-z9V6GPt1V9ceBukA0oGwtJqgD-q7NXFK8zhw7desI5PZMXf3nuVgbJ3xdvAlzkmm5f9RoqQS6_hqwPQEcclq1MEZ3yML5hc99TDtZWy9gGkhR0Hs3QJxxgP7bEqGFP-HjTPnJsrGaT6TjKP7qCxJlcFKLUr5AU_kxMULeUysWWtSGJ9mpxBvsyW1Juo', priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', }, alg: 'ML-DSA-65', }, signing: { protected: { alg: 'ML-DSA-65', }, }, output: { compact: 'eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJTdWl1MjlxYmZ1YUJhUjRBdHMtYzZYUUJlUEJfT3BBeEF3Y1RSXzBLWFZNIn0.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.zmO9_0bLgJAegoVNymfRo4nGPK5lVtSFGnDbzfzYAD5mUEXpaBUg4itvZ8rAUZi4HLb59QqDQSBSpMXC0axajXOMV_YttfmwGgC6FMyaMRZkx-A92bGiNLutqX9jcwRLJqXjMkUGhz2YpHe_mV9QpxokRCH9K6jkyFZp4hZIwFXhRt1z0OGIa5rOoHKsxOCAUZhTXKiASb3vk9lUASW0-Y58WKT4rVmst7_dvk7FVbe9A9I21IH-Tqlg1zSMoI8ozh1aBSG92uPursBd5KRcOlJwhNUYJDgHScIHXM6Hzk6u98W5orKPHu1rDIK7rHJI4Zrui4wBjmQLsPE01LcZHRx4zexDCTMCGSojbL1FiT9CU3oUep4oWOytTEAf2eCi3qDD0iSrp5IslCueoNjtGOFSnUKlsnCeiZF-tNqTy1KpJ3ErTaNPcCzCvsEalhJwFa7NOWyQOEJUzcLaPY_VEFwcCX1Gk4bEI-1rLDiyZqkXgny-U2oRnll0d3u-e2S_Rg-_eL1H_XEbPs_km-822G7JY9li4muZ5KVvfQf_5hza1V4GweqvmeWuZL1gBU2HPS7x1tWL798ALOk1rMnxsvBOPiSLxAEdPoIuw0_qMlKjTavJcDFaihgCgGMUk5SjU65IWQS9t4rgxv9Idu0OCsozo9iCBqrVcnaOwUpkMhV6KeiXA7kQNcegVaMio40cjSyMiEkhGIOEOf8L6eohOh_bPPRYs-8NrZ-VOBJCa0ubJcDU1cTuGNCa7nWWxAqfVjcMyNDx9XHBYBnSOcFNfMP7S9nvqw3KC50U_t2PH5SfwS9w4DLvcgrlEP_gwSgOXuf-i0tRGLQly3IMB7O8QOnkofyFaCUDZeurFkGTpoBfT6lzbJznQAMIDPNcWUsRlNTXsH7atC1nxl4xDJLmmPLCxiErfxbCW5gMWox0kLDwfsFj57hsXG75cZ4jiBbq9b0VjD7Vkf8xlc06ExdzBhGXz8oJiaT5WHDsuzGtrFmh6diN1cO4Cxjr6KdNE8IlyxsfXxQ4AI-0ke3gMyi0DOGeHgHuNc-JHD7oZ6njUMSTBkR1aUMNT7n_2nfFTDCdqW1HaMsMwIHfLOk6dayKXE1oMqY5Op8S5k_SAaknR0vNxmhlTA5h3bZJ28NZxM6R7D00_eBEYrH20rmRP7G7kXKzLvmWeaKAh4oQHiqjVhgauiePDRiMmjx0OhdQnMCtO8PWbx06SiviRn_5hswdVV08B48MVHqbM2AxCLLJYinC2Ep0302Uo0DI-rTNZ1Znn58kM7VCskcxDLsH9AYvPz-HQr3H7Xg0ElwjYn-jJXgZ_cdnLFt4_TuKQdpw_qhvyrNjOx0Mdc-1PrwoWqpA9sSv_pS5lwI2qNVHI2Vj2mZHByod1QUeOQExf3SBjP_FHEAUzUu1OK8M-1SQZGzJT2su3a6ZnMnp0U5qdXyMONFoI2jJ2hDjt7QEQsLx-rvaLxZMJtc2z0MHdwJGAC_kug7XjH3SWQZzBu7zzreIaSwr2A2oobeZiAydwb8LX2QsY9Jr_NphGAMAqzrpkuaMyBd_pFTKMp9s0GYxwyG1ZD9uRuPI9imA4CS7bt-O8YvbWg6eQ-qa9OqDlxNt3Xc32TniQFVxVxN6PDY33XXU-Rpvd1w47NZ48nkyJzjD8Xlbvk9p2ynxWHr-Sto5HXZdru4j8ETUW7ri3mEG1m_dxAbAe2kVbsBp2I1vQppugbmRexuMRLdYFIKqNm0qpQoWTr_k2t5KHnWolrSbFH7Usm8Pwyi4sNhh4_yRHADO2q2o19zCCx2plDSMeYI74CQPRGLlK_GLM4E5Bzfny3E2eaE5_gQBTSGNHpQtJB0ipPwDjqsjDCXqXupCkRta1vxng4coi2-vWYvKu6mq9HhdovHAaWrZRyvuPPI4ZDN_NkmfQR8HogR6NLVhLlRp1cwMArSSDA3f8QlnjdbaeutxRXvFnCCjBk79ws8VGdWAuRmIWgoEFeVAVxkJjJ07zOW8I3kNfB6pnxsZmJwWAGqWc1UlPmkNBstmSXinAzbdl-W-kn1XRDuhzTafHnkCbKS5XgJKsWD2FrhcnCaxxRxuxIGxijofjD4ihmJoYDFh1FYs9IcC-szEfMSekanWOIZCHd1fVzTSbLr5bNaOXR2sO1muFX7w22m8pBVD3fyOHK2JnK4FBCnEBrruMIDaqqu8Z4xesAHKfxY67w-25eUuvVCGL3xpXSyp90684ICkG4STztP1shLVsxKDA-37sKKplqemERlMPY4vDM1Np8JlVawbSGIuom20g6p2KV_zpIPwx9vd1nAiaeZbryf3N5gtL-dOq-c6uZhTCx9OLBtLGE3BcAmn5JFjMGQFxyTL07BluNu24Kf-lttGj9jzbwPZYrok-SnMilXGFEqB3D3cKCOlWjsgg_3cUW1uMp4KlWQvkimV9Pd7cY70w607jcYBJ3MlFZ8EeWeYPZ9qu6xwidA8XlLHxXxfLIJOgfpU8MTppfxdnMhqNSvH_Hx57oDphbUks5K1Z8-O4dSnNqQ-ZWbhaAydYQFDKuUF6HYTAvaWhJmACxhTkTp2t6-P3bev-FcdFIdszJC9LxWtJ96LY_GV4Qvp0hiIdyP1BukWNHtsXK2Rxres3_4Cndg2BOGxVcKZ9YpQDCUy76GRbTCenqjD-SG5sVUEVha5yxbKArPr2-Xpgk8cuZBRSAdmPNRdxCgUtldfCLeL7xhJvryMouxfQ75PMBaImHcsMd95075ePt_VkClUaUj55Y9E81FbOEchPfud2w3TtSvRPvB8-RgY8sLJUAclxcUGE4PnKSZJ7TIBUtHD6uyZ0-nC5KGxbXZsBEzUeHns4ix0Wmo6-6vAM4PGK3qRA1VAhtKXyvNcAfVccVi8KJMK9Mz2eIOXPATvyRy34Ltrcg8tcgK0ftYqEWYpAZ2fVpZBXcYfTIinuLN0-qLra388EZuu59jvmRD7mUv1msMWVMGVeBoNP3lJaJGGWK8iYyu4q7Grq-6WXr5qCz_7kwAtVJdb-zW8U3jLJ3tRSYlyjlpzeVAGjDQ6Yni5y9x4BF-5QUqcoGMLLglyx2WOCELT8IW7nsV21QnqqAbtCzZ76UtEdmUuEOTyqiKQZ0lrjMRm3YrCvJKxtR5thhTRka708NzBvwSRs-JxGG__EWjHhT-aB4VL3IL_oz3mt3iQoszfA-SzHcKU1laZMBuUCyxks6KiJgQGZRPXyaxxDtqZdaRP8Ic5CmuPeyu3kafi0L6LFijsUxnSGxTpgu7hfvcmowQijfE9_ylvg8k_EbI2miG11giODVCYb7k9Yjyriwc9dSUUZ7XoiS24hWYUX6BGGQNN3wVHPkDkOVSDBYTjto99ulquryx4K_UMCu9sQVNxBfMh8tLN7O9-MXlnJbHfKfqFHiPGdIYOBpwuqJdAJiyiuSG3gJxMG_wuwNkBWoO--iOm6PIarCyvL8_P-tuUfT4zIgjJJ3o6YJhbo-q2K82ZFmHuILyzfDSGtHDZpZIR7XnRQWet90cJEHL5k653kvyEHJg0iUiE0iwNA5d_4gBq3vmw1J74hwAHx0Z_iYEcPS6hDGow8M8D7UJTZDkUV_86zj2YqGm_QC_aAeD__NP6sa61bI9-gTOzvYc0JiExKTDjOK9fIvHaV-HN4xr2vWner8o6jPyETvGM8D7aEezlUVOEFwALmhJPSMAq_Fk9JlcIUuC-ITJZNtNz9Awfiru3wkPja1bXN76WAuRHjia0x5ptgMCy2py_vSHZybfIS85ZjsOQ-i_e_niBzhyzXwzBaLEyEitbF4ZQx5c88lXKDMpe9tirAI6XAcqLf4UZkD8Wm2YV7hhVfxLQ1AWLekWE9DZljCtE-SbS1EWNGR8faXKCvaZznRyoqdWz8IN3w7KvaA_ZrEKkIXkkreztG6pI06DlDHCl_sU6rCOoyQf6y1AY77Ob4SdkSRoBHGgR6Uv-LrxHpyJ6trzccu0kqxubHrkW2yHcqe6enVf43zYwWKUeJJZ10bt3a92ziSne-3aj6v3guiKoJoLnV_9h8rUF6zorTWE-Tq58tYfb5SmGf4iCJ5cy9LTY0COIfwJtPkUmyBCZwUhWJnV24P5pOZPe_CckQ28xv5J7Zf4Bvqrq_rhubFEhTJ5JvdMfz8Whc56WSHX7GRKEMqXVp3pHohBvOyT9BmotzIlibVklJy4gzkzUcjJJOld-BOaM_cnMiHpoyKXSJAXTNwXngzEpbvDP2Y0fnrgqDpO3RR3gINaZLRmeG0WI4wWBMMfw8PHjpyV17C_1hmfRI-darbZcX7PD3N4Rw4lBACyk_wnOHBcAS-5cLZEzNmFmhc4iO4msz_seQ1N0drbB0NoUVWBmcY3pGC9TiY6f6Pn-FBUnQkuBhIyPtgAAAAAAAAAABgwVHCUv', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signatures: [ { protected: 'eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJTdWl1MjlxYmZ1YUJhUjRBdHMtYzZYUUJlUEJfT3BBeEF3Y1RSXzBLWFZNIn0', signature: 'zmO9_0bLgJAegoVNymfRo4nGPK5lVtSFGnDbzfzYAD5mUEXpaBUg4itvZ8rAUZi4HLb59QqDQSBSpMXC0axajXOMV_YttfmwGgC6FMyaMRZkx-A92bGiNLutqX9jcwRLJqXjMkUGhz2YpHe_mV9QpxokRCH9K6jkyFZp4hZIwFXhRt1z0OGIa5rOoHKsxOCAUZhTXKiASb3vk9lUASW0-Y58WKT4rVmst7_dvk7FVbe9A9I21IH-Tqlg1zSMoI8ozh1aBSG92uPursBd5KRcOlJwhNUYJDgHScIHXM6Hzk6u98W5orKPHu1rDIK7rHJI4Zrui4wBjmQLsPE01LcZHRx4zexDCTMCGSojbL1FiT9CU3oUep4oWOytTEAf2eCi3qDD0iSrp5IslCueoNjtGOFSnUKlsnCeiZF-tNqTy1KpJ3ErTaNPcCzCvsEalhJwFa7NOWyQOEJUzcLaPY_VEFwcCX1Gk4bEI-1rLDiyZqkXgny-U2oRnll0d3u-e2S_Rg-_eL1H_XEbPs_km-822G7JY9li4muZ5KVvfQf_5hza1V4GweqvmeWuZL1gBU2HPS7x1tWL798ALOk1rMnxsvBOPiSLxAEdPoIuw0_qMlKjTavJcDFaihgCgGMUk5SjU65IWQS9t4rgxv9Idu0OCsozo9iCBqrVcnaOwUpkMhV6KeiXA7kQNcegVaMio40cjSyMiEkhGIOEOf8L6eohOh_bPPRYs-8NrZ-VOBJCa0ubJcDU1cTuGNCa7nWWxAqfVjcMyNDx9XHBYBnSOcFNfMP7S9nvqw3KC50U_t2PH5SfwS9w4DLvcgrlEP_gwSgOXuf-i0tRGLQly3IMB7O8QOnkofyFaCUDZeurFkGTpoBfT6lzbJznQAMIDPNcWUsRlNTXsH7atC1nxl4xDJLmmPLCxiErfxbCW5gMWox0kLDwfsFj57hsXG75cZ4jiBbq9b0VjD7Vkf8xlc06ExdzBhGXz8oJiaT5WHDsuzGtrFmh6diN1cO4Cxjr6KdNE8IlyxsfXxQ4AI-0ke3gMyi0DOGeHgHuNc-JHD7oZ6njUMSTBkR1aUMNT7n_2nfFTDCdqW1HaMsMwIHfLOk6dayKXE1oMqY5Op8S5k_SAaknR0vNxmhlTA5h3bZJ28NZxM6R7D00_eBEYrH20rmRP7G7kXKzLvmWeaKAh4oQHiqjVhgauiePDRiMmjx0OhdQnMCtO8PWbx06SiviRn_5hswdVV08B48MVHqbM2AxCLLJYinC2Ep0302Uo0DI-rTNZ1Znn58kM7VCskcxDLsH9AYvPz-HQr3H7Xg0ElwjYn-jJXgZ_cdnLFt4_TuKQdpw_qhvyrNjOx0Mdc-1PrwoWqpA9sSv_pS5lwI2qNVHI2Vj2mZHByod1QUeOQExf3SBjP_FHEAUzUu1OK8M-1SQZGzJT2su3a6ZnMnp0U5qdXyMONFoI2jJ2hDjt7QEQsLx-rvaLxZMJtc2z0MHdwJGAC_kug7XjH3SWQZzBu7zzreIaSwr2A2oobeZiAydwb8LX2QsY9Jr_NphGAMAqzrpkuaMyBd_pFTKMp9s0GYxwyG1ZD9uRuPI9imA4CS7bt-O8YvbWg6eQ-qa9OqDlxNt3Xc32TniQFVxVxN6PDY33XXU-Rpvd1w47NZ48nkyJzjD8Xlbvk9p2ynxWHr-Sto5HXZdru4j8ETUW7ri3mEG1m_dxAbAe2kVbsBp2I1vQppugbmRexuMRLdYFIKqNm0qpQoWTr_k2t5KHnWolrSbFH7Usm8Pwyi4sNhh4_yRHADO2q2o19zCCx2plDSMeYI74CQPRGLlK_GLM4E5Bzfny3E2eaE5_gQBTSGNHpQtJB0ipPwDjqsjDCXqXupCkRta1vxng4coi2-vWYvKu6mq9HhdovHAaWrZRyvuPPI4ZDN_NkmfQR8HogR6NLVhLlRp1cwMArSSDA3f8QlnjdbaeutxRXvFnCCjBk79ws8VGdWAuRmIWgoEFeVAVxkJjJ07zOW8I3kNfB6pnxsZmJwWAGqWc1UlPmkNBstmSXinAzbdl-W-kn1XRDuhzTafHnkCbKS5XgJKsWD2FrhcnCaxxRxuxIGxijofjD4ihmJoYDFh1FYs9IcC-szEfMSekanWOIZCHd1fVzTSbLr5bNaOXR2sO1muFX7w22m8pBVD3fyOHK2JnK4FBCnEBrruMIDaqqu8Z4xesAHKfxY67w-25eUuvVCGL3xpXSyp90684ICkG4STztP1shLVsxKDA-37sKKplqemERlMPY4vDM1Np8JlVawbSGIuom20g6p2KV_zpIPwx9vd1nAiaeZbryf3N5gtL-dOq-c6uZhTCx9OLBtLGE3BcAmn5JFjMGQFxyTL07BluNu24Kf-lttGj9jzbwPZYrok-SnMilXGFEqB3D3cKCOlWjsgg_3cUW1uMp4KlWQvkimV9Pd7cY70w607jcYBJ3MlFZ8EeWeYPZ9qu6xwidA8XlLHxXxfLIJOgfpU8MTppfxdnMhqNSvH_Hx57oDphbUks5K1Z8-O4dSnNqQ-ZWbhaAydYQFDKuUF6HYTAvaWhJmACxhTkTp2t6-P3bev-FcdFIdszJC9LxWtJ96LY_GV4Qvp0hiIdyP1BukWNHtsXK2Rxres3_4Cndg2BOGxVcKZ9YpQDCUy76GRbTCenqjD-SG5sVUEVha5yxbKArPr2-Xpgk8cuZBRSAdmPNRdxCgUtldfCLeL7xhJvryMouxfQ75PMBaImHcsMd95075ePt_VkClUaUj55Y9E81FbOEchPfud2w3TtSvRPvB8-RgY8sLJUAclxcUGE4PnKSZJ7TIBUtHD6uyZ0-nC5KGxbXZsBEzUeHns4ix0Wmo6-6vAM4PGK3qRA1VAhtKXyvNcAfVccVi8KJMK9Mz2eIOXPATvyRy34Ltrcg8tcgK0ftYqEWYpAZ2fVpZBXcYfTIinuLN0-qLra388EZuu59jvmRD7mUv1msMWVMGVeBoNP3lJaJGGWK8iYyu4q7Grq-6WXr5qCz_7kwAtVJdb-zW8U3jLJ3tRSYlyjlpzeVAGjDQ6Yni5y9x4BF-5QUqcoGMLLglyx2WOCELT8IW7nsV21QnqqAbtCzZ76UtEdmUuEOTyqiKQZ0lrjMRm3YrCvJKxtR5thhTRka708NzBvwSRs-JxGG__EWjHhT-aB4VL3IL_oz3mt3iQoszfA-SzHcKU1laZMBuUCyxks6KiJgQGZRPXyaxxDtqZdaRP8Ic5CmuPeyu3kafi0L6LFijsUxnSGxTpgu7hfvcmowQijfE9_ylvg8k_EbI2miG11giODVCYb7k9Yjyriwc9dSUUZ7XoiS24hWYUX6BGGQNN3wVHPkDkOVSDBYTjto99ulquryx4K_UMCu9sQVNxBfMh8tLN7O9-MXlnJbHfKfqFHiPGdIYOBpwuqJdAJiyiuSG3gJxMG_wuwNkBWoO--iOm6PIarCyvL8_P-tuUfT4zIgjJJ3o6YJhbo-q2K82ZFmHuILyzfDSGtHDZpZIR7XnRQWet90cJEHL5k653kvyEHJg0iUiE0iwNA5d_4gBq3vmw1J74hwAHx0Z_iYEcPS6hDGow8M8D7UJTZDkUV_86zj2YqGm_QC_aAeD__NP6sa61bI9-gTOzvYc0JiExKTDjOK9fIvHaV-HN4xr2vWner8o6jPyETvGM8D7aEezlUVOEFwALmhJPSMAq_Fk9JlcIUuC-ITJZNtNz9Awfiru3wkPja1bXN76WAuRHjia0x5ptgMCy2py_vSHZybfIS85ZjsOQ-i_e_niBzhyzXwzBaLEyEitbF4ZQx5c88lXKDMpe9tirAI6XAcqLf4UZkD8Wm2YV7hhVfxLQ1AWLekWE9DZljCtE-SbS1EWNGR8faXKCvaZznRyoqdWz8IN3w7KvaA_ZrEKkIXkkreztG6pI06DlDHCl_sU6rCOoyQf6y1AY77Ob4SdkSRoBHGgR6Uv-LrxHpyJ6trzccu0kqxubHrkW2yHcqe6enVf43zYwWKUeJJZ10bt3a92ziSne-3aj6v3guiKoJoLnV_9h8rUF6zorTWE-Tq58tYfb5SmGf4iCJ5cy9LTY0COIfwJtPkUmyBCZwUhWJnV24P5pOZPe_CckQ28xv5J7Zf4Bvqrq_rhubFEhTJ5JvdMfz8Whc56WSHX7GRKEMqXVp3pHohBvOyT9BmotzIlibVklJy4gzkzUcjJJOld-BOaM_cnMiHpoyKXSJAXTNwXngzEpbvDP2Y0fnrgqDpO3RR3gINaZLRmeG0WI4wWBMMfw8PHjpyV17C_1hmfRI-darbZcX7PD3N4Rw4lBACyk_wnOHBcAS-5cLZEzNmFmhc4iO4msz_seQ1N0drbB0NoUVWBmcY3pGC9TiY6f6Pn-FBUnQkuBhIyPtgAAAAAAAAAABgwVHCUv', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', protected: 'eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJTdWl1MjlxYmZ1YUJhUjRBdHMtYzZYUUJlUEJfT3BBeEF3Y1RSXzBLWFZNIn0', signature: 'zmO9_0bLgJAegoVNymfRo4nGPK5lVtSFGnDbzfzYAD5mUEXpaBUg4itvZ8rAUZi4HLb59QqDQSBSpMXC0axajXOMV_YttfmwGgC6FMyaMRZkx-A92bGiNLutqX9jcwRLJqXjMkUGhz2YpHe_mV9QpxokRCH9K6jkyFZp4hZIwFXhRt1z0OGIa5rOoHKsxOCAUZhTXKiASb3vk9lUASW0-Y58WKT4rVmst7_dvk7FVbe9A9I21IH-Tqlg1zSMoI8ozh1aBSG92uPursBd5KRcOlJwhNUYJDgHScIHXM6Hzk6u98W5orKPHu1rDIK7rHJI4Zrui4wBjmQLsPE01LcZHRx4zexDCTMCGSojbL1FiT9CU3oUep4oWOytTEAf2eCi3qDD0iSrp5IslCueoNjtGOFSnUKlsnCeiZF-tNqTy1KpJ3ErTaNPcCzCvsEalhJwFa7NOWyQOEJUzcLaPY_VEFwcCX1Gk4bEI-1rLDiyZqkXgny-U2oRnll0d3u-e2S_Rg-_eL1H_XEbPs_km-822G7JY9li4muZ5KVvfQf_5hza1V4GweqvmeWuZL1gBU2HPS7x1tWL798ALOk1rMnxsvBOPiSLxAEdPoIuw0_qMlKjTavJcDFaihgCgGMUk5SjU65IWQS9t4rgxv9Idu0OCsozo9iCBqrVcnaOwUpkMhV6KeiXA7kQNcegVaMio40cjSyMiEkhGIOEOf8L6eohOh_bPPRYs-8NrZ-VOBJCa0ubJcDU1cTuGNCa7nWWxAqfVjcMyNDx9XHBYBnSOcFNfMP7S9nvqw3KC50U_t2PH5SfwS9w4DLvcgrlEP_gwSgOXuf-i0tRGLQly3IMB7O8QOnkofyFaCUDZeurFkGTpoBfT6lzbJznQAMIDPNcWUsRlNTXsH7atC1nxl4xDJLmmPLCxiErfxbCW5gMWox0kLDwfsFj57hsXG75cZ4jiBbq9b0VjD7Vkf8xlc06ExdzBhGXz8oJiaT5WHDsuzGtrFmh6diN1cO4Cxjr6KdNE8IlyxsfXxQ4AI-0ke3gMyi0DOGeHgHuNc-JHD7oZ6njUMSTBkR1aUMNT7n_2nfFTDCdqW1HaMsMwIHfLOk6dayKXE1oMqY5Op8S5k_SAaknR0vNxmhlTA5h3bZJ28NZxM6R7D00_eBEYrH20rmRP7G7kXKzLvmWeaKAh4oQHiqjVhgauiePDRiMmjx0OhdQnMCtO8PWbx06SiviRn_5hswdVV08B48MVHqbM2AxCLLJYinC2Ep0302Uo0DI-rTNZ1Znn58kM7VCskcxDLsH9AYvPz-HQr3H7Xg0ElwjYn-jJXgZ_cdnLFt4_TuKQdpw_qhvyrNjOx0Mdc-1PrwoWqpA9sSv_pS5lwI2qNVHI2Vj2mZHByod1QUeOQExf3SBjP_FHEAUzUu1OK8M-1SQZGzJT2su3a6ZnMnp0U5qdXyMONFoI2jJ2hDjt7QEQsLx-rvaLxZMJtc2z0MHdwJGAC_kug7XjH3SWQZzBu7zzreIaSwr2A2oobeZiAydwb8LX2QsY9Jr_NphGAMAqzrpkuaMyBd_pFTKMp9s0GYxwyG1ZD9uRuPI9imA4CS7bt-O8YvbWg6eQ-qa9OqDlxNt3Xc32TniQFVxVxN6PDY33XXU-Rpvd1w47NZ48nkyJzjD8Xlbvk9p2ynxWHr-Sto5HXZdru4j8ETUW7ri3mEG1m_dxAbAe2kVbsBp2I1vQppugbmRexuMRLdYFIKqNm0qpQoWTr_k2t5KHnWolrSbFH7Usm8Pwyi4sNhh4_yRHADO2q2o19zCCx2plDSMeYI74CQPRGLlK_GLM4E5Bzfny3E2eaE5_gQBTSGNHpQtJB0ipPwDjqsjDCXqXupCkRta1vxng4coi2-vWYvKu6mq9HhdovHAaWrZRyvuPPI4ZDN_NkmfQR8HogR6NLVhLlRp1cwMArSSDA3f8QlnjdbaeutxRXvFnCCjBk79ws8VGdWAuRmIWgoEFeVAVxkJjJ07zOW8I3kNfB6pnxsZmJwWAGqWc1UlPmkNBstmSXinAzbdl-W-kn1XRDuhzTafHnkCbKS5XgJKsWD2FrhcnCaxxRxuxIGxijofjD4ihmJoYDFh1FYs9IcC-szEfMSekanWOIZCHd1fVzTSbLr5bNaOXR2sO1muFX7w22m8pBVD3fyOHK2JnK4FBCnEBrruMIDaqqu8Z4xesAHKfxY67w-25eUuvVCGL3xpXSyp90684ICkG4STztP1shLVsxKDA-37sKKplqemERlMPY4vDM1Np8JlVawbSGIuom20g6p2KV_zpIPwx9vd1nAiaeZbryf3N5gtL-dOq-c6uZhTCx9OLBtLGE3BcAmn5JFjMGQFxyTL07BluNu24Kf-lttGj9jzbwPZYrok-SnMilXGFEqB3D3cKCOlWjsgg_3cUW1uMp4KlWQvkimV9Pd7cY70w607jcYBJ3MlFZ8EeWeYPZ9qu6xwidA8XlLHxXxfLIJOgfpU8MTppfxdnMhqNSvH_Hx57oDphbUks5K1Z8-O4dSnNqQ-ZWbhaAydYQFDKuUF6HYTAvaWhJmACxhTkTp2t6-P3bev-FcdFIdszJC9LxWtJ96LY_GV4Qvp0hiIdyP1BukWNHtsXK2Rxres3_4Cndg2BOGxVcKZ9YpQDCUy76GRbTCenqjD-SG5sVUEVha5yxbKArPr2-Xpgk8cuZBRSAdmPNRdxCgUtldfCLeL7xhJvryMouxfQ75PMBaImHcsMd95075ePt_VkClUaUj55Y9E81FbOEchPfud2w3TtSvRPvB8-RgY8sLJUAclxcUGE4PnKSZJ7TIBUtHD6uyZ0-nC5KGxbXZsBEzUeHns4ix0Wmo6-6vAM4PGK3qRA1VAhtKXyvNcAfVccVi8KJMK9Mz2eIOXPATvyRy34Ltrcg8tcgK0ftYqEWYpAZ2fVpZBXcYfTIinuLN0-qLra388EZuu59jvmRD7mUv1msMWVMGVeBoNP3lJaJGGWK8iYyu4q7Grq-6WXr5qCz_7kwAtVJdb-zW8U3jLJ3tRSYlyjlpzeVAGjDQ6Yni5y9x4BF-5QUqcoGMLLglyx2WOCELT8IW7nsV21QnqqAbtCzZ76UtEdmUuEOTyqiKQZ0lrjMRm3YrCvJKxtR5thhTRka708NzBvwSRs-JxGG__EWjHhT-aB4VL3IL_oz3mt3iQoszfA-SzHcKU1laZMBuUCyxks6KiJgQGZRPXyaxxDtqZdaRP8Ic5CmuPeyu3kafi0L6LFijsUxnSGxTpgu7hfvcmowQijfE9_ylvg8k_EbI2miG11giODVCYb7k9Yjyriwc9dSUUZ7XoiS24hWYUX6BGGQNN3wVHPkDkOVSDBYTjto99ulquryx4K_UMCu9sQVNxBfMh8tLN7O9-MXlnJbHfKfqFHiPGdIYOBpwuqJdAJiyiuSG3gJxMG_wuwNkBWoO--iOm6PIarCyvL8_P-tuUfT4zIgjJJ3o6YJhbo-q2K82ZFmHuILyzfDSGtHDZpZIR7XnRQWet90cJEHL5k653kvyEHJg0iUiE0iwNA5d_4gBq3vmw1J74hwAHx0Z_iYEcPS6hDGow8M8D7UJTZDkUV_86zj2YqGm_QC_aAeD__NP6sa61bI9-gTOzvYc0JiExKTDjOK9fIvHaV-HN4xr2vWner8o6jPyETvGM8D7aEezlUVOEFwALmhJPSMAq_Fk9JlcIUuC-ITJZNtNz9Awfiru3wkPja1bXN76WAuRHjia0x5ptgMCy2py_vSHZybfIS85ZjsOQ-i_e_niBzhyzXwzBaLEyEitbF4ZQx5c88lXKDMpe9tirAI6XAcqLf4UZkD8Wm2YV7hhVfxLQ1AWLekWE9DZljCtE-SbS1EWNGR8faXKCvaZznRyoqdWz8IN3w7KvaA_ZrEKkIXkkreztG6pI06DlDHCl_sU6rCOoyQf6y1AY77Ob4SdkSRoBHGgR6Uv-LrxHpyJ6trzccu0kqxubHrkW2yHcqe6enVf43zYwWKUeJJZ10bt3a92ziSne-3aj6v3guiKoJoLnV_9h8rUF6zorTWE-Tq58tYfb5SmGf4iCJ5cy9LTY0COIfwJtPkUmyBCZwUhWJnV24P5pOZPe_CckQ28xv5J7Zf4Bvqrq_rhubFEhTJ5JvdMfz8Whc56WSHX7GRKEMqXVp3pHohBvOyT9BmotzIlibVklJy4gzkzUcjJJOld-BOaM_cnMiHpoyKXSJAXTNwXngzEpbvDP2Y0fnrgqDpO3RR3gINaZLRmeG0WI4wWBMMfw8PHjpyV17C_1hmfRI-darbZcX7PD3N4Rw4lBACyk_wnOHBcAS-5cLZEzNmFmhc4iO4msz_seQ1N0drbB0NoUVWBmcY3pGC9TiY6f6Pn-FBUnQkuBhIyPtgAAAAAAAAAABgwVHCUv', }, }, }, { title: 'ietf-cose-dilithium - ML-DSA-87', deterministic: false, input: { payload: 'It’s a dangerous business, Frodo, going out your door.', key: { kty: 'AKP', alg: 'ML-DSA-87', pub: '5F_8jMc9uIXcZi5ioYzY44AylxF_pWWIFKmFtf8dt7Roz8gruSnx2Gt37RT1rhamU2h3LOUZEkEBBeBFaXWukf22Q7US8STV5gvWi4x-Mf4Bx7DcZa5HBQHMVlpuHfz8_RJWVDPEr-3VEYIeLpYQxFJ14oNt7jXO1p1--mcv0eQxi-9etuiX6LRRqiAt7QQrKq73envj9pkUbaIpqL2z_6SWRFln51IXv7yQSPmVZEPYcx-DPrMN4Q2slv_-fPZeoERcPjHoYB4TO-ahAHZP4xluJncmRB8xdR-_mm9YgGRPTnJ15X3isPEF5NsFXVDdHJyTT931NbjeKLDHTARJ8iLNLtC7j7x3XM7oyUBmW0D3EvT34AdQ6eHkzZz_JdGUXD6bylPM1PEu7nWBhW69aPJoRZVuPnvrdh8P51vdMb_i-gGBEzl7OHvVnWKmi4r3-iRauTLmn3eOLO79ITBPu4CZ6hPY6lfBgTGXovda4lEHW1Ha04-FNmnp1fmKNlUJiUGZOhWUhg-6cf5TDuXCn1jyl4r2iMy3Wlg4o1nBEumOJahYOsjawfhh_Vjir7pd5aUuAgkE9bQrwIdONb788-YRloR2jzbgCPBHEhd86-YnYHOB5W6q7hYcFym43lHb3kdNSMxoJJ6icWK4eZPmDITtbMZCPLNnbZ61CyyrWjoEnvExOB1iP6b7y8nbHnzAJeoEGLna0sxszU6V-izsJP7spwMYp1Fxa3IT9j7b9lpjM4NX-Dj5TsBxgiwkhRJIiFEHs9HE6SRnjHYU6hrwOBBGGfKuNylAvs-mninLtf9sPiCke-Sk90usNMEzwApqcGrMxv_T2OT71pqZcE4Sg8hQ2MWNHldTzZWHuDxMNGy5pYE3IT7BCDTGat_iu1xQGo7y7K3Rtnej3xpt64br8HIsT1Aw4g-QGN1bb8U-6iT9kre1tAJf6umW0-SP1MZQ2C261-r5NmOWmFEvJiU9LvaEfIUY6FZcyaVJXG__V83nMjiCxUp9tHCrLa-P_Sv3lPp8aS2ef71TLuzB14gOLKCzIWEovii0qfHRUfrJeAiwvZi3tDphKprIZYEr_qxvR0YCd4QLUqOwh_kWynztwPdo6ivRnqIRVfhLSgTEAArSrgWHFU1WC8Ckd6T5MpqJhN0x6x8qBePZGHAdYwz8qa9h7wiNLFWBrLRj5DmQLl1CVxnpVrjW33MFso4P8n060N4ghdKSSZsZozkNQ5b7O6yajYy-rSp6QpD8msb8oEX5imFKRaOcviQ2D4TRT45HJxKs63Tb9FtT1JoORzfkdv_E1bL3zSR6oYbTt2Stnpz-7kVqc8KR2N45EkFKxDkRw3IXOte0cq81xoU87S_ntf4KiVZaszuqb2XN2SgxnXBl4EDnpehPmqkD92SAlLrQcTaxaSe47G28K-8MwoVt4eeVkj4UEsSfJN7rbCH2yKl2XJx5huDaS0xn2ODQyNRmgk-5I9hXMUiZDNLvEzx4zuyrcu2d0oXFo3ZoUtVFNCB__TQCf2x27ej9GjLXLDAEi7qnl9Xfb94n0IfeVyGte3-j6NP3DWv8OrLiUjNTaLv6Fay1yzfUaU6LI86-Jd6ckloiGhg7kE0_hd-ZKakZxU1vh0Vzc6DW7MFAPky75iCZlDXoBpZjTNGo5HR-mCW_ozblu60U9zZA8bn-voANuu_hYwxh-uY1sHTFZOqp2xicnnMChz_GTm1Je8XCkICYegeiHUryEHA6T6B_L9gW8S_R4ptMD0Sv6b1KHqqKeubwKltCWPUsr2En9iYypnz06DEL5Wp8KMhrLid2AMPpLI0j1CWGJExXHpBWjfIC8vbYH4YKVl-euRo8eDcuKosb5hxUGM9Jvy1siVXUpIKpkZt2YLP5pEBP_EVOoHPh5LJomrLMpORr1wBKbEkfom7npX1g817bK4IeYmZELI8zXUUtUkx3LgNTckwjx90Vt6oVXpFEICIUDF_LAVMUftzz6JUvbwOZo8iAZqcnVslAmRXeY_ZPp5eEHFfHlsb8VQ73Rd_p8XlFf5R1WuWiUGp2TzJ-VQvj3BTdQfOwSxR9RUk4xjqNabLqTFcQ7As246bHJXH6XVnd4DbEIDPfNa8FaWb_DNEgQAiXGqa6n7l7aFq5_6Kp0XeBBM0sOzJt4fy8JC6U0DEcMnWxKFDtMM7q06LubQYFCEEdQ5b1Qh2LbQZ898tegmeF--EZ4F4hvYebZPV8sM0ZcsKBXyCr585qs00PRxr0S6rReekGRBIvXzMojmid3dxc6DPpdV3x5zxlxaIBxO3i_6axknSSdxnS04_bemWqQ3CLf6mpSqfTIQJT1407GB4QINAAC9Ch3AXUR_n1jr64TGWzbIr8uDcnoVCJlOgmlXpmOwubigAzJattbWRi7k4QYBnA3_4QMjt73n2Co4-F_Qh4boYLpmwWG2SwcIw2PeXGr2LY2zwkPR4bcSyx1Z6UK5trQpWlpQCxgsvV_RvGzpN22RtHoihPH74K0cBIzCz7tK-jqeuWl1A7af7KmQ66fpRBr5ykTLOsa17WblkcIB_jDvqKfEcdxhPWJUwmOo4TIQS-xH8arLOy_NQFG2m14_yxwUemXC-QxLUYi6_FIcqwPBKjCdpQtadRdyftQSKO0SP-GxUvamMZzWI780rXuOBkq5kyYLy9QF9bf_-bL6QLpe1WMCQlOeXZaCPoncgYoT0WZ17jB52Xb2lPWsyXYK54npszkbKJ4OIqfvF8xqRXcVe22VwJuqT9Uy4-4KKQgQ7TXla7Gdm2H7mKl8YXQlsGCT2Ypc8O4t0Sfw7qYAuaDGf752Hbm3fl1bupcB2huIPlIaDP6IRR9XvTYIW2flbwYfhKLmoVKnG85uUi2qtqCjPOIuU3-peT0othfmwKQXaoOqO-V4r6wPL1VHxVFtIYmEdVt0RccUOvpOVR_OAHG9uHOzTmueK5557Qxp0ojtZCHyN-hgoMZJLrvdKkTCxPNo2-mZQbHoVh2FnThZ9JbO49dB8lKXP4_MU5xAnjXMgKXtbfI8w6ZWATE_XWgf2VQMUpGp4wpy44yWQTxHxh_4T9540BGwG0FU0bkgrwA_erseGZnepqdmz5_ScCs84O5Xr5MbYhJLCGGxY6O5GqS-ooB2w0Mt87KbbE4bpYje9CAHH8FX3pDrJyLsyasA3zxmk4OmGpG7Z70ofONJtHRe56R5287vFmuazEEutXn81kNzB-3aJT1ga3vnWZw4CSvFKoWYSA7auLgrHSHFZdITfOrgtmQmGbFhM9kSBdY1UCnpzf65oos3PZWRa2twfUxxLAnPNtrxpRGyvtsapw7ljUagZmuyh3hLCjhAxYmnoE1dbyIWvpCqSlEtVjL1yb_nuLEzgvmZuV02fHxGuWgHTOMVGXpf81Rce3eoBK3lapW1wkzezlk3tcA2bZOtA9qbxdsbVR37kemzQ9K1e3Y0OWhtSj', priv: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', }, alg: 'ML-DSA-87', }, signing: { protected: { alg: 'ML-DSA-87', }, }, output: { compact: 'eyJhbGciOiJNTC1EU0EtODciLCJraWQiOiJ0Um4xSk5Ja2dNc0FCVlFCbFhlREh4QUljY2xoLTJJWDBVZERFelB0NVhVIn0.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.hmMrKkUgZwGPQV_WUoXUVq_Z9WOenDZbfMmHpKritl0btWi29TC8eIyQyT1FAuW2kg3h6ALsvCrjX5tn3QKFQZYC0sBdRt0VNiDm0BjyJ4jWcomSCgb0-cGXaLlODAz-njGridYfO1DpGMwHHshuKuvECv4qnX3XgZPE-6C8La43TZrYO8brzBXGiuyGMLq-TSmXavOeiadtpp6iTUqJDBgQSYvPB6PvipeCPlQH2ZQi8qkraxspi0lgy8Jh2aRYj44DX2ZKq-Ml-hfBJB4iHRpWmwPpEH7Ed4LkBIlaqZoPccrPgpGQpyz4_FcahrJc8CGGtTO5I34o5BcuZej7WOQvJ6mRmvYqIrYwoLs-3_YFZkVdX4KU38oprMvAHjObOhy_vZZArMnCgfYlCKrANbhOZG8O0BXgqow5Bqv_oRIztGQZMrivp_1CS0hELarwkwjdqyH5R747ndV26IQkeyn6y9daXRZIWxaC9KmAaDSm5-YsRVpiAAr0QmfaV51z065_r5qZmOMFIBERVi9Bbm_Z7ipJkoIL2SqVsePATfHeWB8huFpVFxdeEkJUPDuBtthax0HhxpRuECpFNJf2xA70Hp5C5VZIsi5EO21HuRpixiNKmXP5whhsn_uv_B7R4f4DX6X6A53lFrUfpFIrTfOQvBAvmEUUTSGcPeT-F7f_1lz34uFyN3ZT4FCeCh4n4yyZY1fSPVMNtOfK8GrLrRoWdi8gMk30oTKgb9zFkFU7uZhVEVRV86A_060bgFSHWDz5dlXLfyCoJsbsHlO9WBibTCkrMv6lnjh4czprro2prRtJAJB2jVwS1dv2mo4wP1lFYqY63yM9I9deU4fxy6mkwig7XwcVJskg8jX_0agATqmrKfYWMI4yGQ9fciYacgN8X2uSHqiPU1cgQ8VUGsSAsw4POdZpmcUt_DacVLT8-qwnq6NWpm8bqm_uUQu3JjqcHKLz7zWKopeLG_ZY7a45IqUQpwbMg9ICE1ZNTe5nsMHAJnevgLfWk14wnvVQyRVvlSvatdUTg0EjBc6P35a4lY12vIOq2ENpA-m52TfXeXxXK0vtZfT9SY33thi4EfZABWL_jQyiio6b6Akrh6_PgQ-bh2H2Fpu8Z3GImrbHodcbnqFpmKYlMLwxDHnKPxY7PpyyV8HsWfEjqVlAX56stAIIG4_owwzMZMcFwgucAP176TwjaXJqm9v2-DXisD2cNjyGlJ_rec670rv61thjiJF2uZrB9Z2zoQVYnc3Y9sJMMPPmunUcXpNVZWSsPlFDoPa1ABoFnRbP8rO-qbNGP5N7xY2DuPRYOp3CdyxeyDPmGBC2556FNeLRj-PhPAkd61fgXsQZyS9N2jHmFUIKbL8o-e3bQnqW7ebEn7zAjS_LQ2DtgIdIneUu84hh8AduoW9ky_aOpqvBUmdnHUwZHQiSSdeCPnEOssVBbuDd3gbcQf_VWvplwcjTTrJPsqqZpirjfVGPFUCVAz6kD0vhFcvTdQt6DGqys61xg_VOfj6wxpKsXuXDuqwaeb4KpGniHx-23nECgKG86N_1BBX8RRAvYnksxIIxIxgyrng-y44CV9FL_wGfP0Plx6JjSUFOL1gDZTc5NrAPoOztEo1FbJ2Lq8gqBR9Ku9Yza3aYANAJQvAraTXzA0t1j6qcmh-WtXeI1GE-8neOJtlRVbzT5RvPiRJZAVmu9Pg97wbLLQNPJoqIYp-c9mieGsDxAi75C2M1ArRnCa4kJJXrupgzQzzFefWyaRkIvC2MP9MwB_Z_NY3mp3opcNlT1TdKLr1sncLUkk3qJ0Pwyr-5dsKrC6aenapBHO7G0OnA0qTi8-Oy91VqJYYcVjcOUQaxNeMtnk-pLJL7j3MzqNiDkc-OfR19fcWvDmmd9Z8wtj20khL4mTDn7qTUo-PsVR7GnpqkImmEmE8sa4ZlPHa4_IcZGFbdcwp9xuOndINlzWGrIKywFPQ1x26zXDEa7fOx5f01aX8dIU_KWNAGdaZxPIlqLW5qbC6dipSqf9NwblZLJs5DCiLV8nHS-QM26xQJVUNH22n_3Z_8z1SA8AX8d7j0-g1Pf7NZC8e8Ipnm4B3YGpA7nn471aTbJb4OUamfgys17MV_hPDK_f7FF7NXp06-dtVYDmcs-87ZkrDuluOkUaRivKULwjEtSbiiKZAKirGfAOuwyCbbzygEpqYvEztABSmDYd_F_autklob_0deKuvvRYFpVCaxeaYQ7WIkpfBbMxeh9Qci7kPfgyB5H9ajWEJV3fgRk10Q1RaWyTUddQ_jWaluiDa3GD_t39sUrG7QhXc2Oz1NPPNoY6-A4jFbFCtXSF1muztqy0xaworcNiHY18yeL4Cw2iYLJ1Q3O4NnFo3E-wIXmYF4CLxZifr2Jkd6Ix1w-wlsN6vyCcDs8JeAgeJn0_Oahk1mgvRhVz8FFeidSdFqJBxGKbfZ32F_auJwrsLyjN_ShxTSFofyKQy2XCfoVMko4eu5o6md66xBmjZvTvItXL7f-eD0JxISBsBkZG3mFrApZKbdpI1lEa681ZbCxRTYpxUR7McTbs0Q5S9PCN5ElUz_axfeupIIbCTE4S0-ZQuIdQcQ2pn1j-4t2c04jtLE6WFI-1ASBCedlZmrZUiRegbezE01hMiFnfN32BhBu7ZcnlBCdWwj9hUfpEduJIgaA3acXhysGs40nqRzR9imvX9CBQYJZjrCHr-wORF6svmvF5FADRgwbM7Cc9puJgLBiQwXrhD43B6kjX_OXi5O2UNZFkAPr0WONBJsip8CgR6pt1u_mIKlIrYM9kM-idJGGT0DZ9UU4LMx0-9_2KCCkjDqgYN1rS9DA__GP9tS3dJ-XLSlk2URQuoHm4Xubv4vwgjUS7JzAxcQWHB0HtHFoZ3-tYVw_GRbRwyODm3E-N5O3L_R-pva9fvlPjkCNMrf2IlxAxBKML1gCxsSqhFr5yoPeW40LTxMF_dYPNLjC3l7mRRl_wfY_FhvayI7hrgCYfMgWeb-cXyx5eXumt9lMFOD3dQtEG1IUbdE7pVXG-barWK0Zl43DtQMNQzoCK_BLxfCsambyRRcI6E4QTfqe5lWtVf8Wi4KproenWyCjjzEjJQdWw4g-ae_bjGjfZCp38RgsXtWgI_tuzKyRF5WwjyN9VEoRXd8W2DctmBejHF2XDYzbMFkJ-384SokPX6intnlqBGMs0ssxriJhsFOA-vgDra6REx3DUMb8_u_Umc-zp4E6isX4D-eRYgElmj0ez945nqxp3YliO8mRLMW6E4OupLthfw4vmK3YqTAuXcnGxYrf7JqAkMfz5uAPi0SqPWDQZq7ycu9BmkMXAIhMb19XBDjL7hZGDwDRrn9yBBcYlPaFPNXjMJWJH_xxUKNsTFGg5-J_WdxXi8Zn6tDMxbxqqjIpw_FUaM00jJ2MhpbkzhEx7X85pBR47ScRgr6WJpf4ZLSFuV7NT1WI3PIBa_bYeCiq29fp3ShM-1bRFdJG_lGZd97TuAMF_QU6-KDXBv5i8kUZ1NXdJUz-YaA0RRVNFgMGM5n0pKB5IFncAPK-taTzHLIZJ9uuBdP2y2Hxwbw8YQlmy2-MT5XE5Ae_9kxuvIIlSzjpfLN9012HSnX4tZ8x3aWwof3E7s3jjzw7qbBtoUkYYpIGVOKf2EpmhEqevSlXYWpBYN3X2ZYjsrA9CL9PTvrPdyWLwKBmfh7cDJbjNXJSQLeKL7oHzicrllABzR9Ckkz7b24XGV1Klcat_Og4oB9qxiO2zJZWz2GDTAL0hosUlHLWnrQYvqFzzdIOzGlifwIyGgoRNb44IRMzzsErxuoqkdjZewVc4PzruHRlV3cWK6M7ZUiWLtxtMzas2sfAERy8BdS7ISLzj5PERoWyYXSW-898WD3ze5MJcpSsAYNEmPCBtdxF9l-Qz1LxuDa8hOCQ2Wzef1a2WFF5pCBaZRcAK_kef65xRst6WFpjWZGCLZUqHBhFDLEOd7Ikbw7d9V8dc4nAO65NQcxfT9JDUZadS2jmQJip8GLD4P9lGS1Ry-8rHCnMN7zXDp43TfyYhSgv9uj4xKi2wmAMMYBl0n2RNemx8nt-K_dknGgYYGOybDkg2uAUoXdxP33KfiRjbRpYqZVAiq0S45QLAIxxGiDJoZRnyIscdM6lryQtXj0PO67vRf6ifxC3wLv97HHUKergpXcAg-4_rNj_Zx_xiHMfCAe2q3DG1a_DcSmu5u1OPkBHmzHB9Vs8HV0E2-z44sl3Exqb5L8pMYpDnZ7QW-Qb1-S-zoESUy__AKhkRWPC7GmvmJJJHur6SRGSK0X2KyszkEYoe-8NhwpvLrYnNuVk7QknBS91KH2q8C0B8FKqcY40S5ILkImP9iOGIXYl5ZVRleoDBpH9BootWH2az5l7c_e-vfBGs7XpudoAq5wzhe_-AMBvKPCm0BoCX5B_NGUasXvEWobqUb61mpKCuVJdzVtexk-m8Jfvmdc8ooPJEYD_oosY5_S1LuHoc7GHLnoYdDVb2FhIPhOJCLQCef-Y3dtNThqOEo534Zg7R72nSeSQhdQ1hcBUsc50U2oF9OlOnV9z5hsfNwIxdUO9bdoXRYFmosmtpmDfGxAem0s5iPJ0EJ_8szlaX2pi6k6VP-ci-n7J8pEBwL2R3c-ei2iqB7JdLi7Gg6iXVMpQIFTxswh0HbgGtyZXgR_-AM91XRszm_kAlqAHTAJ7B-0Z5bJgMGEY2StBdhGzel_gNPVaxemC3DT0904GbCU2Z3avUHcedebI02_MdILdQxyXbw145KjqC15CqeaG--6x6WzpAuSjrFQRuz6Z5UyibW6Ay9R3P25c-gwmaRM8rPW5YkQtQdfzrtvGZ6wyhIcBXvbpU02OoChfRDF4xI2LvnaW3g6hQIUGe5lueI13ArYRAhZC0LHKPuVfv5OKeMqxYRtcN3YK6Ddc1t61rsA7MU1cAKzOGsiQ7aNyNBQHOV6z-W4-ws_DnZKYRMz0D_hwbeHO0ZKhciXng5VDCX4hyb47LExmO5N1mfihN3iHEkX_19rIgunfkSb9gd9B_AaazAttBEPPLtbsoZneQXBRl3PWiDpC_yXiLTWAd13AOBYHzBMKeJ4hplUqsAGTaGSztbpvV92wz_YX9kMEucHMu5hoM-TJbuWoheiiiKSFBNRK_g_rqXZo1UZjDOnHpHGJxOnlJBPp94Zvwh8sKLOpOd4qeOMLbnYKiag00al5x_3fBXq-KI0Y31OJfgDdCaKAQ0DUX71HN6XDOlvU1Iwh48iASJHdQGDmjhcS8YoeX9omwPiYhcbGJGzEVrn3H7h24eIf_7bVRpicMhjwghB0xtqTT0eVam1l8kr1-5kem7Dr2Kyqm2HpEwbi3KPXKYDXQRbHElEhazMCYr2wnjx_Bx2ai2uZa8uQyjN1zh1cjWHH0TicL2eAyc6YPKfKpmc5QwLrgT0ddQDhvXkCkN50fOR1Sbl56iFoAL8goFl3QA5wBk51vsDsquEt7nlz6sGTHzknENb-eEayrXnw-Q5FueFwqzoJpUrEYDXTxgOU8XVhrPv0Ot-BO6ORfzn3_1gREcHjhrc6RdF01NNqyzyVG0BdckywvAnzUGskWdCfP62dKdx46lAIRVPd3xG4tViaQ79GAeMVnqSeCLXbOyqfnJwhOT2fgQzLwxcj1tqGBBd3Pfx2d5-10WiL_mis0ven6golqaLq1EQsveb9AJpkYgJxdBeyHZXxNLMh4_XAuK1ZIs9F8Cz1vFEVcAFipev-cFyRvsdcNI2-HK2nOGkypEcuVATyLtA0jKeyPtE4TJ3_l8KXltEZjWycQAd_8Tj9is3wisC8bfzjll8UBjFZp-rzmCr8kA4cZih9gl27TiCmhyKhgMfDUIUmuDL_Rn9DLxEAT3Ebl1SW0ToCciNtKTH9oO-wnkPd-jg1HCooLcg-K_QkOTptJNZRFbXpooKqwH5Z9qsCxurZxnS_MscnE0qTa4EqrlpiDnj4FBs4q9SEPlKequfYzFmjQis1iwsReutf6pHmsvRmz9gx5vd6NMIkI05IeLNDElvlOGD04m1vR4ZISdmdHaAgaW9_AUPGx0vP1Rqe36cvebwUYSnzdbZ7y1s7PH7GXF5r7zNEzY9bHmXvsjb3N_u9BkenwkQfZGS6ez0AAAAAAAAAAALGSAlKzg7Qw', json: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signatures: [ { protected: 'eyJhbGciOiJNTC1EU0EtODciLCJraWQiOiJ0Um4xSk5Ja2dNc0FCVlFCbFhlREh4QUljY2xoLTJJWDBVZERFelB0NVhVIn0', signature: 'hmMrKkUgZwGPQV_WUoXUVq_Z9WOenDZbfMmHpKritl0btWi29TC8eIyQyT1FAuW2kg3h6ALsvCrjX5tn3QKFQZYC0sBdRt0VNiDm0BjyJ4jWcomSCgb0-cGXaLlODAz-njGridYfO1DpGMwHHshuKuvECv4qnX3XgZPE-6C8La43TZrYO8brzBXGiuyGMLq-TSmXavOeiadtpp6iTUqJDBgQSYvPB6PvipeCPlQH2ZQi8qkraxspi0lgy8Jh2aRYj44DX2ZKq-Ml-hfBJB4iHRpWmwPpEH7Ed4LkBIlaqZoPccrPgpGQpyz4_FcahrJc8CGGtTO5I34o5BcuZej7WOQvJ6mRmvYqIrYwoLs-3_YFZkVdX4KU38oprMvAHjObOhy_vZZArMnCgfYlCKrANbhOZG8O0BXgqow5Bqv_oRIztGQZMrivp_1CS0hELarwkwjdqyH5R747ndV26IQkeyn6y9daXRZIWxaC9KmAaDSm5-YsRVpiAAr0QmfaV51z065_r5qZmOMFIBERVi9Bbm_Z7ipJkoIL2SqVsePATfHeWB8huFpVFxdeEkJUPDuBtthax0HhxpRuECpFNJf2xA70Hp5C5VZIsi5EO21HuRpixiNKmXP5whhsn_uv_B7R4f4DX6X6A53lFrUfpFIrTfOQvBAvmEUUTSGcPeT-F7f_1lz34uFyN3ZT4FCeCh4n4yyZY1fSPVMNtOfK8GrLrRoWdi8gMk30oTKgb9zFkFU7uZhVEVRV86A_060bgFSHWDz5dlXLfyCoJsbsHlO9WBibTCkrMv6lnjh4czprro2prRtJAJB2jVwS1dv2mo4wP1lFYqY63yM9I9deU4fxy6mkwig7XwcVJskg8jX_0agATqmrKfYWMI4yGQ9fciYacgN8X2uSHqiPU1cgQ8VUGsSAsw4POdZpmcUt_DacVLT8-qwnq6NWpm8bqm_uUQu3JjqcHKLz7zWKopeLG_ZY7a45IqUQpwbMg9ICE1ZNTe5nsMHAJnevgLfWk14wnvVQyRVvlSvatdUTg0EjBc6P35a4lY12vIOq2ENpA-m52TfXeXxXK0vtZfT9SY33thi4EfZABWL_jQyiio6b6Akrh6_PgQ-bh2H2Fpu8Z3GImrbHodcbnqFpmKYlMLwxDHnKPxY7PpyyV8HsWfEjqVlAX56stAIIG4_owwzMZMcFwgucAP176TwjaXJqm9v2-DXisD2cNjyGlJ_rec670rv61thjiJF2uZrB9Z2zoQVYnc3Y9sJMMPPmunUcXpNVZWSsPlFDoPa1ABoFnRbP8rO-qbNGP5N7xY2DuPRYOp3CdyxeyDPmGBC2556FNeLRj-PhPAkd61fgXsQZyS9N2jHmFUIKbL8o-e3bQnqW7ebEn7zAjS_LQ2DtgIdIneUu84hh8AduoW9ky_aOpqvBUmdnHUwZHQiSSdeCPnEOssVBbuDd3gbcQf_VWvplwcjTTrJPsqqZpirjfVGPFUCVAz6kD0vhFcvTdQt6DGqys61xg_VOfj6wxpKsXuXDuqwaeb4KpGniHx-23nECgKG86N_1BBX8RRAvYnksxIIxIxgyrng-y44CV9FL_wGfP0Plx6JjSUFOL1gDZTc5NrAPoOztEo1FbJ2Lq8gqBR9Ku9Yza3aYANAJQvAraTXzA0t1j6qcmh-WtXeI1GE-8neOJtlRVbzT5RvPiRJZAVmu9Pg97wbLLQNPJoqIYp-c9mieGsDxAi75C2M1ArRnCa4kJJXrupgzQzzFefWyaRkIvC2MP9MwB_Z_NY3mp3opcNlT1TdKLr1sncLUkk3qJ0Pwyr-5dsKrC6aenapBHO7G0OnA0qTi8-Oy91VqJYYcVjcOUQaxNeMtnk-pLJL7j3MzqNiDkc-OfR19fcWvDmmd9Z8wtj20khL4mTDn7qTUo-PsVR7GnpqkImmEmE8sa4ZlPHa4_IcZGFbdcwp9xuOndINlzWGrIKywFPQ1x26zXDEa7fOx5f01aX8dIU_KWNAGdaZxPIlqLW5qbC6dipSqf9NwblZLJs5DCiLV8nHS-QM26xQJVUNH22n_3Z_8z1SA8AX8d7j0-g1Pf7NZC8e8Ipnm4B3YGpA7nn471aTbJb4OUamfgys17MV_hPDK_f7FF7NXp06-dtVYDmcs-87ZkrDuluOkUaRivKULwjEtSbiiKZAKirGfAOuwyCbbzygEpqYvEztABSmDYd_F_autklob_0deKuvvRYFpVCaxeaYQ7WIkpfBbMxeh9Qci7kPfgyB5H9ajWEJV3fgRk10Q1RaWyTUddQ_jWaluiDa3GD_t39sUrG7QhXc2Oz1NPPNoY6-A4jFbFCtXSF1muztqy0xaworcNiHY18yeL4Cw2iYLJ1Q3O4NnFo3E-wIXmYF4CLxZifr2Jkd6Ix1w-wlsN6vyCcDs8JeAgeJn0_Oahk1mgvRhVz8FFeidSdFqJBxGKbfZ32F_auJwrsLyjN_ShxTSFofyKQy2XCfoVMko4eu5o6md66xBmjZvTvItXL7f-eD0JxISBsBkZG3mFrApZKbdpI1lEa681ZbCxRTYpxUR7McTbs0Q5S9PCN5ElUz_axfeupIIbCTE4S0-ZQuIdQcQ2pn1j-4t2c04jtLE6WFI-1ASBCedlZmrZUiRegbezE01hMiFnfN32BhBu7ZcnlBCdWwj9hUfpEduJIgaA3acXhysGs40nqRzR9imvX9CBQYJZjrCHr-wORF6svmvF5FADRgwbM7Cc9puJgLBiQwXrhD43B6kjX_OXi5O2UNZFkAPr0WONBJsip8CgR6pt1u_mIKlIrYM9kM-idJGGT0DZ9UU4LMx0-9_2KCCkjDqgYN1rS9DA__GP9tS3dJ-XLSlk2URQuoHm4Xubv4vwgjUS7JzAxcQWHB0HtHFoZ3-tYVw_GRbRwyODm3E-N5O3L_R-pva9fvlPjkCNMrf2IlxAxBKML1gCxsSqhFr5yoPeW40LTxMF_dYPNLjC3l7mRRl_wfY_FhvayI7hrgCYfMgWeb-cXyx5eXumt9lMFOD3dQtEG1IUbdE7pVXG-barWK0Zl43DtQMNQzoCK_BLxfCsambyRRcI6E4QTfqe5lWtVf8Wi4KproenWyCjjzEjJQdWw4g-ae_bjGjfZCp38RgsXtWgI_tuzKyRF5WwjyN9VEoRXd8W2DctmBejHF2XDYzbMFkJ-384SokPX6intnlqBGMs0ssxriJhsFOA-vgDra6REx3DUMb8_u_Umc-zp4E6isX4D-eRYgElmj0ez945nqxp3YliO8mRLMW6E4OupLthfw4vmK3YqTAuXcnGxYrf7JqAkMfz5uAPi0SqPWDQZq7ycu9BmkMXAIhMb19XBDjL7hZGDwDRrn9yBBcYlPaFPNXjMJWJH_xxUKNsTFGg5-J_WdxXi8Zn6tDMxbxqqjIpw_FUaM00jJ2MhpbkzhEx7X85pBR47ScRgr6WJpf4ZLSFuV7NT1WI3PIBa_bYeCiq29fp3ShM-1bRFdJG_lGZd97TuAMF_QU6-KDXBv5i8kUZ1NXdJUz-YaA0RRVNFgMGM5n0pKB5IFncAPK-taTzHLIZJ9uuBdP2y2Hxwbw8YQlmy2-MT5XE5Ae_9kxuvIIlSzjpfLN9012HSnX4tZ8x3aWwof3E7s3jjzw7qbBtoUkYYpIGVOKf2EpmhEqevSlXYWpBYN3X2ZYjsrA9CL9PTvrPdyWLwKBmfh7cDJbjNXJSQLeKL7oHzicrllABzR9Ckkz7b24XGV1Klcat_Og4oB9qxiO2zJZWz2GDTAL0hosUlHLWnrQYvqFzzdIOzGlifwIyGgoRNb44IRMzzsErxuoqkdjZewVc4PzruHRlV3cWK6M7ZUiWLtxtMzas2sfAERy8BdS7ISLzj5PERoWyYXSW-898WD3ze5MJcpSsAYNEmPCBtdxF9l-Qz1LxuDa8hOCQ2Wzef1a2WFF5pCBaZRcAK_kef65xRst6WFpjWZGCLZUqHBhFDLEOd7Ikbw7d9V8dc4nAO65NQcxfT9JDUZadS2jmQJip8GLD4P9lGS1Ry-8rHCnMN7zXDp43TfyYhSgv9uj4xKi2wmAMMYBl0n2RNemx8nt-K_dknGgYYGOybDkg2uAUoXdxP33KfiRjbRpYqZVAiq0S45QLAIxxGiDJoZRnyIscdM6lryQtXj0PO67vRf6ifxC3wLv97HHUKergpXcAg-4_rNj_Zx_xiHMfCAe2q3DG1a_DcSmu5u1OPkBHmzHB9Vs8HV0E2-z44sl3Exqb5L8pMYpDnZ7QW-Qb1-S-zoESUy__AKhkRWPC7GmvmJJJHur6SRGSK0X2KyszkEYoe-8NhwpvLrYnNuVk7QknBS91KH2q8C0B8FKqcY40S5ILkImP9iOGIXYl5ZVRleoDBpH9BootWH2az5l7c_e-vfBGs7XpudoAq5wzhe_-AMBvKPCm0BoCX5B_NGUasXvEWobqUb61mpKCuVJdzVtexk-m8Jfvmdc8ooPJEYD_oosY5_S1LuHoc7GHLnoYdDVb2FhIPhOJCLQCef-Y3dtNThqOEo534Zg7R72nSeSQhdQ1hcBUsc50U2oF9OlOnV9z5hsfNwIxdUO9bdoXRYFmosmtpmDfGxAem0s5iPJ0EJ_8szlaX2pi6k6VP-ci-n7J8pEBwL2R3c-ei2iqB7JdLi7Gg6iXVMpQIFTxswh0HbgGtyZXgR_-AM91XRszm_kAlqAHTAJ7B-0Z5bJgMGEY2StBdhGzel_gNPVaxemC3DT0904GbCU2Z3avUHcedebI02_MdILdQxyXbw145KjqC15CqeaG--6x6WzpAuSjrFQRuz6Z5UyibW6Ay9R3P25c-gwmaRM8rPW5YkQtQdfzrtvGZ6wyhIcBXvbpU02OoChfRDF4xI2LvnaW3g6hQIUGe5lueI13ArYRAhZC0LHKPuVfv5OKeMqxYRtcN3YK6Ddc1t61rsA7MU1cAKzOGsiQ7aNyNBQHOV6z-W4-ws_DnZKYRMz0D_hwbeHO0ZKhciXng5VDCX4hyb47LExmO5N1mfihN3iHEkX_19rIgunfkSb9gd9B_AaazAttBEPPLtbsoZneQXBRl3PWiDpC_yXiLTWAd13AOBYHzBMKeJ4hplUqsAGTaGSztbpvV92wz_YX9kMEucHMu5hoM-TJbuWoheiiiKSFBNRK_g_rqXZo1UZjDOnHpHGJxOnlJBPp94Zvwh8sKLOpOd4qeOMLbnYKiag00al5x_3fBXq-KI0Y31OJfgDdCaKAQ0DUX71HN6XDOlvU1Iwh48iASJHdQGDmjhcS8YoeX9omwPiYhcbGJGzEVrn3H7h24eIf_7bVRpicMhjwghB0xtqTT0eVam1l8kr1-5kem7Dr2Kyqm2HpEwbi3KPXKYDXQRbHElEhazMCYr2wnjx_Bx2ai2uZa8uQyjN1zh1cjWHH0TicL2eAyc6YPKfKpmc5QwLrgT0ddQDhvXkCkN50fOR1Sbl56iFoAL8goFl3QA5wBk51vsDsquEt7nlz6sGTHzknENb-eEayrXnw-Q5FueFwqzoJpUrEYDXTxgOU8XVhrPv0Ot-BO6ORfzn3_1gREcHjhrc6RdF01NNqyzyVG0BdckywvAnzUGskWdCfP62dKdx46lAIRVPd3xG4tViaQ79GAeMVnqSeCLXbOyqfnJwhOT2fgQzLwxcj1tqGBBd3Pfx2d5-10WiL_mis0ven6golqaLq1EQsveb9AJpkYgJxdBeyHZXxNLMh4_XAuK1ZIs9F8Cz1vFEVcAFipev-cFyRvsdcNI2-HK2nOGkypEcuVATyLtA0jKeyPtE4TJ3_l8KXltEZjWycQAd_8Tj9is3wisC8bfzjll8UBjFZp-rzmCr8kA4cZih9gl27TiCmhyKhgMfDUIUmuDL_Rn9DLxEAT3Ebl1SW0ToCciNtKTH9oO-wnkPd-jg1HCooLcg-K_QkOTptJNZRFbXpooKqwH5Z9qsCxurZxnS_MscnE0qTa4EqrlpiDnj4FBs4q9SEPlKequfYzFmjQis1iwsReutf6pHmsvRmz9gx5vd6NMIkI05IeLNDElvlOGD04m1vR4ZISdmdHaAgaW9_AUPGx0vP1Rqe36cvebwUYSnzdbZ7y1s7PH7GXF5r7zNEzY9bHmXvsjb3N_u9BkenwkQfZGS6ez0AAAAAAAAAAALGSAlKzg7Qw', }, ], }, json_flat: { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', protected: 'eyJhbGciOiJNTC1EU0EtODciLCJraWQiOiJ0Um4xSk5Ja2dNc0FCVlFCbFhlREh4QUljY2xoLTJJWDBVZERFelB0NVhVIn0', signature: 'hmMrKkUgZwGPQV_WUoXUVq_Z9WOenDZbfMmHpKritl0btWi29TC8eIyQyT1FAuW2kg3h6ALsvCrjX5tn3QKFQZYC0sBdRt0VNiDm0BjyJ4jWcomSCgb0-cGXaLlODAz-njGridYfO1DpGMwHHshuKuvECv4qnX3XgZPE-6C8La43TZrYO8brzBXGiuyGMLq-TSmXavOeiadtpp6iTUqJDBgQSYvPB6PvipeCPlQH2ZQi8qkraxspi0lgy8Jh2aRYj44DX2ZKq-Ml-hfBJB4iHRpWmwPpEH7Ed4LkBIlaqZoPccrPgpGQpyz4_FcahrJc8CGGtTO5I34o5BcuZej7WOQvJ6mRmvYqIrYwoLs-3_YFZkVdX4KU38oprMvAHjObOhy_vZZArMnCgfYlCKrANbhOZG8O0BXgqow5Bqv_oRIztGQZMrivp_1CS0hELarwkwjdqyH5R747ndV26IQkeyn6y9daXRZIWxaC9KmAaDSm5-YsRVpiAAr0QmfaV51z065_r5qZmOMFIBERVi9Bbm_Z7ipJkoIL2SqVsePATfHeWB8huFpVFxdeEkJUPDuBtthax0HhxpRuECpFNJf2xA70Hp5C5VZIsi5EO21HuRpixiNKmXP5whhsn_uv_B7R4f4DX6X6A53lFrUfpFIrTfOQvBAvmEUUTSGcPeT-F7f_1lz34uFyN3ZT4FCeCh4n4yyZY1fSPVMNtOfK8GrLrRoWdi8gMk30oTKgb9zFkFU7uZhVEVRV86A_060bgFSHWDz5dlXLfyCoJsbsHlO9WBibTCkrMv6lnjh4czprro2prRtJAJB2jVwS1dv2mo4wP1lFYqY63yM9I9deU4fxy6mkwig7XwcVJskg8jX_0agATqmrKfYWMI4yGQ9fciYacgN8X2uSHqiPU1cgQ8VUGsSAsw4POdZpmcUt_DacVLT8-qwnq6NWpm8bqm_uUQu3JjqcHKLz7zWKopeLG_ZY7a45IqUQpwbMg9ICE1ZNTe5nsMHAJnevgLfWk14wnvVQyRVvlSvatdUTg0EjBc6P35a4lY12vIOq2ENpA-m52TfXeXxXK0vtZfT9SY33thi4EfZABWL_jQyiio6b6Akrh6_PgQ-bh2H2Fpu8Z3GImrbHodcbnqFpmKYlMLwxDHnKPxY7PpyyV8HsWfEjqVlAX56stAIIG4_owwzMZMcFwgucAP176TwjaXJqm9v2-DXisD2cNjyGlJ_rec670rv61thjiJF2uZrB9Z2zoQVYnc3Y9sJMMPPmunUcXpNVZWSsPlFDoPa1ABoFnRbP8rO-qbNGP5N7xY2DuPRYOp3CdyxeyDPmGBC2556FNeLRj-PhPAkd61fgXsQZyS9N2jHmFUIKbL8o-e3bQnqW7ebEn7zAjS_LQ2DtgIdIneUu84hh8AduoW9ky_aOpqvBUmdnHUwZHQiSSdeCPnEOssVBbuDd3gbcQf_VWvplwcjTTrJPsqqZpirjfVGPFUCVAz6kD0vhFcvTdQt6DGqys61xg_VOfj6wxpKsXuXDuqwaeb4KpGniHx-23nECgKG86N_1BBX8RRAvYnksxIIxIxgyrng-y44CV9FL_wGfP0Plx6JjSUFOL1gDZTc5NrAPoOztEo1FbJ2Lq8gqBR9Ku9Yza3aYANAJQvAraTXzA0t1j6qcmh-WtXeI1GE-8neOJtlRVbzT5RvPiRJZAVmu9Pg97wbLLQNPJoqIYp-c9mieGsDxAi75C2M1ArRnCa4kJJXrupgzQzzFefWyaRkIvC2MP9MwB_Z_NY3mp3opcNlT1TdKLr1sncLUkk3qJ0Pwyr-5dsKrC6aenapBHO7G0OnA0qTi8-Oy91VqJYYcVjcOUQaxNeMtnk-pLJL7j3MzqNiDkc-OfR19fcWvDmmd9Z8wtj20khL4mTDn7qTUo-PsVR7GnpqkImmEmE8sa4ZlPHa4_IcZGFbdcwp9xuOndINlzWGrIKywFPQ1x26zXDEa7fOx5f01aX8dIU_KWNAGdaZxPIlqLW5qbC6dipSqf9NwblZLJs5DCiLV8nHS-QM26xQJVUNH22n_3Z_8z1SA8AX8d7j0-g1Pf7NZC8e8Ipnm4B3YGpA7nn471aTbJb4OUamfgys17MV_hPDK_f7FF7NXp06-dtVYDmcs-87ZkrDuluOkUaRivKULwjEtSbiiKZAKirGfAOuwyCbbzygEpqYvEztABSmDYd_F_autklob_0deKuvvRYFpVCaxeaYQ7WIkpfBbMxeh9Qci7kPfgyB5H9ajWEJV3fgRk10Q1RaWyTUddQ_jWaluiDa3GD_t39sUrG7QhXc2Oz1NPPNoY6-A4jFbFCtXSF1muztqy0xaworcNiHY18yeL4Cw2iYLJ1Q3O4NnFo3E-wIXmYF4CLxZifr2Jkd6Ix1w-wlsN6vyCcDs8JeAgeJn0_Oahk1mgvRhVz8FFeidSdFqJBxGKbfZ32F_auJwrsLyjN_ShxTSFofyKQy2XCfoVMko4eu5o6md66xBmjZvTvItXL7f-eD0JxISBsBkZG3mFrApZKbdpI1lEa681ZbCxRTYpxUR7McTbs0Q5S9PCN5ElUz_axfeupIIbCTE4S0-ZQuIdQcQ2pn1j-4t2c04jtLE6WFI-1ASBCedlZmrZUiRegbezE01hMiFnfN32BhBu7ZcnlBCdWwj9hUfpEduJIgaA3acXhysGs40nqRzR9imvX9CBQYJZjrCHr-wORF6svmvF5FADRgwbM7Cc9puJgLBiQwXrhD43B6kjX_OXi5O2UNZFkAPr0WONBJsip8CgR6pt1u_mIKlIrYM9kM-idJGGT0DZ9UU4LMx0-9_2KCCkjDqgYN1rS9DA__GP9tS3dJ-XLSlk2URQuoHm4Xubv4vwgjUS7JzAxcQWHB0HtHFoZ3-tYVw_GRbRwyODm3E-N5O3L_R-pva9fvlPjkCNMrf2IlxAxBKML1gCxsSqhFr5yoPeW40LTxMF_dYPNLjC3l7mRRl_wfY_FhvayI7hrgCYfMgWeb-cXyx5eXumt9lMFOD3dQtEG1IUbdE7pVXG-barWK0Zl43DtQMNQzoCK_BLxfCsambyRRcI6E4QTfqe5lWtVf8Wi4KproenWyCjjzEjJQdWw4g-ae_bjGjfZCp38RgsXtWgI_tuzKyRF5WwjyN9VEoRXd8W2DctmBejHF2XDYzbMFkJ-384SokPX6intnlqBGMs0ssxriJhsFOA-vgDra6REx3DUMb8_u_Umc-zp4E6isX4D-eRYgElmj0ez945nqxp3YliO8mRLMW6E4OupLthfw4vmK3YqTAuXcnGxYrf7JqAkMfz5uAPi0SqPWDQZq7ycu9BmkMXAIhMb19XBDjL7hZGDwDRrn9yBBcYlPaFPNXjMJWJH_xxUKNsTFGg5-J_WdxXi8Zn6tDMxbxqqjIpw_FUaM00jJ2MhpbkzhEx7X85pBR47ScRgr6WJpf4ZLSFuV7NT1WI3PIBa_bYeCiq29fp3ShM-1bRFdJG_lGZd97TuAMF_QU6-KDXBv5i8kUZ1NXdJUz-YaA0RRVNFgMGM5n0pKB5IFncAPK-taTzHLIZJ9uuBdP2y2Hxwbw8YQlmy2-MT5XE5Ae_9kxuvIIlSzjpfLN9012HSnX4tZ8x3aWwof3E7s3jjzw7qbBtoUkYYpIGVOKf2EpmhEqevSlXYWpBYN3X2ZYjsrA9CL9PTvrPdyWLwKBmfh7cDJbjNXJSQLeKL7oHzicrllABzR9Ckkz7b24XGV1Klcat_Og4oB9qxiO2zJZWz2GDTAL0hosUlHLWnrQYvqFzzdIOzGlifwIyGgoRNb44IRMzzsErxuoqkdjZewVc4PzruHRlV3cWK6M7ZUiWLtxtMzas2sfAERy8BdS7ISLzj5PERoWyYXSW-898WD3ze5MJcpSsAYNEmPCBtdxF9l-Qz1LxuDa8hOCQ2Wzef1a2WFF5pCBaZRcAK_kef65xRst6WFpjWZGCLZUqHBhFDLEOd7Ikbw7d9V8dc4nAO65NQcxfT9JDUZadS2jmQJip8GLD4P9lGS1Ry-8rHCnMN7zXDp43TfyYhSgv9uj4xKi2wmAMMYBl0n2RNemx8nt-K_dknGgYYGOybDkg2uAUoXdxP33KfiRjbRpYqZVAiq0S45QLAIxxGiDJoZRnyIscdM6lryQtXj0PO67vRf6ifxC3wLv97HHUKergpXcAg-4_rNj_Zx_xiHMfCAe2q3DG1a_DcSmu5u1OPkBHmzHB9Vs8HV0E2-z44sl3Exqb5L8pMYpDnZ7QW-Qb1-S-zoESUy__AKhkRWPC7GmvmJJJHur6SRGSK0X2KyszkEYoe-8NhwpvLrYnNuVk7QknBS91KH2q8C0B8FKqcY40S5ILkImP9iOGIXYl5ZVRleoDBpH9BootWH2az5l7c_e-vfBGs7XpudoAq5wzhe_-AMBvKPCm0BoCX5B_NGUasXvEWobqUb61mpKCuVJdzVtexk-m8Jfvmdc8ooPJEYD_oosY5_S1LuHoc7GHLnoYdDVb2FhIPhOJCLQCef-Y3dtNThqOEo534Zg7R72nSeSQhdQ1hcBUsc50U2oF9OlOnV9z5hsfNwIxdUO9bdoXRYFmosmtpmDfGxAem0s5iPJ0EJ_8szlaX2pi6k6VP-ci-n7J8pEBwL2R3c-ei2iqB7JdLi7Gg6iXVMpQIFTxswh0HbgGtyZXgR_-AM91XRszm_kAlqAHTAJ7B-0Z5bJgMGEY2StBdhGzel_gNPVaxemC3DT0904GbCU2Z3avUHcedebI02_MdILdQxyXbw145KjqC15CqeaG--6x6WzpAuSjrFQRuz6Z5UyibW6Ay9R3P25c-gwmaRM8rPW5YkQtQdfzrtvGZ6wyhIcBXvbpU02OoChfRDF4xI2LvnaW3g6hQIUGe5lueI13ArYRAhZC0LHKPuVfv5OKeMqxYRtcN3YK6Ddc1t61rsA7MU1cAKzOGsiQ7aNyNBQHOV6z-W4-ws_DnZKYRMz0D_hwbeHO0ZKhciXng5VDCX4hyb47LExmO5N1mfihN3iHEkX_19rIgunfkSb9gd9B_AaazAttBEPPLtbsoZneQXBRl3PWiDpC_yXiLTWAd13AOBYHzBMKeJ4hplUqsAGTaGSztbpvV92wz_YX9kMEucHMu5hoM-TJbuWoheiiiKSFBNRK_g_rqXZo1UZjDOnHpHGJxOnlJBPp94Zvwh8sKLOpOd4qeOMLbnYKiag00al5x_3fBXq-KI0Y31OJfgDdCaKAQ0DUX71HN6XDOlvU1Iwh48iASJHdQGDmjhcS8YoeX9omwPiYhcbGJGzEVrn3H7h24eIf_7bVRpicMhjwghB0xtqTT0eVam1l8kr1-5kem7Dr2Kyqm2HpEwbi3KPXKYDXQRbHElEhazMCYr2wnjx_Bx2ai2uZa8uQyjN1zh1cjWHH0TicL2eAyc6YPKfKpmc5QwLrgT0ddQDhvXkCkN50fOR1Sbl56iFoAL8goFl3QA5wBk51vsDsquEt7nlz6sGTHzknENb-eEayrXnw-Q5FueFwqzoJpUrEYDXTxgOU8XVhrPv0Ot-BO6ORfzn3_1gREcHjhrc6RdF01NNqyzyVG0BdckywvAnzUGskWdCfP62dKdx46lAIRVPd3xG4tViaQ79GAeMVnqSeCLXbOyqfnJwhOT2fgQzLwxcj1tqGBBd3Pfx2d5-10WiL_mis0ven6golqaLq1EQsveb9AJpkYgJxdBeyHZXxNLMh4_XAuK1ZIs9F8Cz1vFEVcAFipev-cFyRvsdcNI2-HK2nOGkypEcuVATyLtA0jKeyPtE4TJ3_l8KXltEZjWycQAd_8Tj9is3wisC8bfzjll8UBjFZp-rzmCr8kA4cZih9gl27TiCmhyKhgMfDUIUmuDL_Rn9DLxEAT3Ebl1SW0ToCciNtKTH9oO-wnkPd-jg1HCooLcg-K_QkOTptJNZRFbXpooKqwH5Z9qsCxurZxnS_MscnE0qTa4EqrlpiDnj4FBs4q9SEPlKequfYzFmjQis1iwsReutf6pHmsvRmz9gx5vd6NMIkI05IeLNDElvlOGD04m1vR4ZISdmdHaAgaW9_AUPGx0vP1Rqe36cvebwUYSnzdbZ7y1s7PH7GXF5r7zNEzY9bHmXvsjb3N_u9BkenwkQfZGS6ez0AAAAAAAAAAALGSAlKzg7Qw', }, }, }, ] ================================================ FILE: docs/README.md ================================================ # `jose` API Documentation `jose` is JavaScript module for JSON Object Signing and Encryption, providing support for JSON Web Tokens (JWT), JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Key Set (JWKS), and more. The module is designed to work across various Web-interoperable runtimes including Node.js, browsers, Cloudflare Workers, Deno, Bun, and others. ## Sponsor Auth0 by Okta If you want to quickly add JWT authentication to JavaScript apps, feel free to check out Auth0's JavaScript SDK and free plan. [Create an Auth0 account; it's free!][sponsor-auth0]

## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Available modules `jose` is distributed via [npmjs.com](https://www.npmjs.com/package/jose), [jsr.io](https://jsr.io/@panva/jose), [jsdelivr.com](https://www.jsdelivr.com/package/npm/jose), and [github.com](https://github.com/panva/jose). **`example`** ESM import[^cjs] ```js import * as jose from 'jose' ``` ### JSON Web Tokens (JWT) The `jose` module supports JSON Web Tokens (JWT) and provides functionality for signing and verifying tokens, as well as their JWT Claims Set validation. - [JWT Claims Set Validation & Signature Verification](jwt/verify/functions/jwtVerify.md) using the `jwtVerify` function - [Using a remote JSON Web Key Set (JWKS)](jwks/remote/functions/createRemoteJWKSet.md) - [Using a local JSON Web Key Set (JWKS)](jwks/local/functions/createLocalJWKSet.md) - [Signing](jwt/sign/classes/SignJWT.md) using the `SignJWT` class - Utility functions - [Decoding Token's Protected Header](util/decode_protected_header/functions/decodeProtectedHeader.md) - [Decoding JWT Claims Set](util/decode_jwt/functions/decodeJwt.md) prior to its validation ### Encrypted JSON Web Tokens The `jose` module supports encrypted JSON Web Tokens and provides functionality for encrypting and decrypting tokens, as well as their JWT Claims Set validation. - [Decryption & JWT Claims Set Validation](jwt/decrypt/functions/jwtDecrypt.md) using the `jwtDecrypt` function - [Encryption](jwt/encrypt/classes/EncryptJWT.md) using the `EncryptJWT` class - Utility functions - [Decoding Token's Protected Header](util/decode_protected_header/functions/decodeProtectedHeader.md) ### Key Utilities The `jose` module supports importing, exporting, and generating keys and secrets in various formats, including PEM formats like SPKI, X.509 certificate, and PKCS #8, as well as JSON Web Key (JWK). - Key Import Functions - [JWK Import](key/import/functions/importJWK.md) - [Public Key Import (SPKI)](key/import/functions/importSPKI.md) - [Public Key Import (X.509 Certificate)](key/import/functions/importX509.md) - [Private Key Import (PKCS #8)](key/import/functions/importPKCS8.md) - Key and Secret Generation Functions - [Asymmetric Key Pair Generation](key/generate_key_pair/functions/generateKeyPair.md) - [Symmetric Secret Generation](key/generate_secret/functions/generateSecret.md) - Key Export Functions - [JWK Export](key/export/functions/exportJWK.md) - [Private Key Export](key/export/functions/exportPKCS8.md) - [Public Key Export](key/export/functions/exportSPKI.md) ### JSON Web Signature (JWS) The `jose` module supports signing and verification of JWS messages with arbitrary payloads in Compact, Flattened JSON, and General JSON serialization syntaxes. - Signing - [Compact](jws/compact/sign/classes/CompactSign.md), [Flattened JSON](jws/flattened/sign/classes/FlattenedSign.md), [General JSON](jws/general/sign/classes/GeneralSign.md) - Verification - [Compact](jws/compact/verify/functions/compactVerify.md), [Flattened JSON](jws/flattened/verify/functions/flattenedVerify.md), [General JSON](jws/general/verify/functions/generalVerify.md) - [Using a remote JSON Web Key Set (JWKS)](jwks/remote/functions/createRemoteJWKSet.md) - [Using a local JSON Web Key Set (JWKS)](jwks/local/functions/createLocalJWKSet.md) - Utility functions - [Decoding Token's Protected Header](util/decode_protected_header/functions/decodeProtectedHeader.md) ### JSON Web Encryption (JWE) The `jose` module supports encryption and decryption of JWE messages with arbitrary plaintext in Compact, Flattened JSON, and General JSON serialization syntaxes. - Encryption - [Compact](jwe/compact/encrypt/classes/CompactEncrypt.md), [Flattened JSON](jwe/flattened/encrypt/classes/FlattenedEncrypt.md), [General JSON](jwe/general/encrypt/classes/GeneralEncrypt.md) - Decryption - [Compact](jwe/compact/decrypt/functions/compactDecrypt.md), [Flattened JSON](jwe/flattened/decrypt/functions/flattenedDecrypt.md), [General JSON](jwe/general/decrypt/functions/generalDecrypt.md) - Utility functions - [Decoding Token's Protected Header](util/decode_protected_header/functions/decodeProtectedHeader.md) ### Other The following are additional features and utilities provided by the `jose` module: - [Calculating JWK Thumbprint](jwk/thumbprint/functions/calculateJwkThumbprint.md) - [Calculating JWK Thumbprint URI](jwk/thumbprint/functions/calculateJwkThumbprintUri.md) - [Verification using a JWK Embedded in a JWS Header](jwk/embedded/functions/EmbeddedJWK.md) - [Unsecured JWT](jwt/unsecured/classes/UnsecuredJWT.md) - [JOSE Errors](util/errors/README.md) [sponsor-auth0]: https://a0.to/signup/panva [^cjs]: CJS style `let jose = require('jose')` is possible in Node.js versions where `process.features.require_module` is `true` or with the `--experimental-require-module` Node.js CLI flag. ================================================ FILE: docs/jwe/compact/decrypt/README.md ================================================ # jwe/compact/decrypt Decrypting JSON Web Encryption (JWE) in Compact Serialization ## Interfaces - [CompactDecryptGetKey](interfaces/CompactDecryptGetKey.md) ## Functions - [compactDecrypt](functions/compactDecrypt.md) ================================================ FILE: docs/jwe/compact/decrypt/functions/compactDecrypt.md ================================================ # Function: compactDecrypt() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **compactDecrypt**(`jwe`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactDecryptResult`](../../../../types/interfaces/CompactDecryptResult.md)\> Decrypts a Compact JWE. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/compact/decrypt'`. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Compact JWE. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactDecryptResult`](../../../../types/interfaces/CompactDecryptResult.md)\> ### Example ```js const jwe = 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.nyQ19eq9ogh9wA7fFtnI2oouzy5_8b5DeLkoRMfi2yijgfTs2zEnayCEofz_qhnL-nwszabd9qUeHv0-IwvhhJJS7GUJOU3ikiIe42qcIAFme1A_Fo9CTxw4XTOy-I5qanl8So91u6hwfyN1VxAqVLsSE7_23EC-gfGEg_5znew9PyXXsOIE-K_HH7IQowRrlZ1X_bM_Liu53RzDpLDvRz59mp3S8L56YqpM8FexFGTGpEaoTcEIst375qncYt3-79IVR7gZN1RWsWgjPatfvVbnh74PglQcATSf3UUhaW0OAKn6q7r3PDx6DIKQ35bgHQg5QopuN00eIfLQL2trGw.W3grIVj5HVuAb76X.6PcuDe5D6ttWFYyv0oqqdDXfI2R8wBg1F2Q80UUA_Gv8eEimNWfxIWdLxrjzgQGSvIhxmFKuLM0.a93_Ug3uZHuczj70Zavx8Q' const { plaintext, protectedHeader } = await jose.compactDecrypt(jwe, privateKey) console.log(protectedHeader) console.log(new TextDecoder().decode(plaintext)) ``` ## Call Signature ▸ **compactDecrypt**(`jwe`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactDecryptResult`](../../../../types/interfaces/CompactDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Compact JWE. | | `getKey` | [`CompactDecryptGetKey`](../interfaces/CompactDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactDecryptResult`](../../../../types/interfaces/CompactDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jwe/compact/decrypt/interfaces/CompactDecryptGetKey.md ================================================ # Interface: CompactDecryptGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for Compact JWE Decryption dynamic key resolution. No token components have been verified at the time of this function call. ▸ **CompactDecryptGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWEHeaderParameters`](../../../../types/interfaces/CompactJWEHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwe/compact/encrypt/README.md ================================================ # jwe/compact/encrypt Encrypting JSON Web Encryption (JWE) in Compact Serialization ## Classes - [CompactEncrypt](classes/CompactEncrypt.md) ================================================ FILE: docs/jwe/compact/encrypt/classes/CompactEncrypt.md ================================================ # Class: CompactEncrypt ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The CompactEncrypt class is used to build and encrypt Compact JWE strings. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/compact/encrypt'`. ## Example ```js const jwe = await new jose.CompactEncrypt( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' }) .encrypt(publicKey) console.log(jwe) ``` ## Constructors ### Constructor ▸ **new CompactEncrypt**(`plaintext`): `CompactEncrypt` CompactEncrypt constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `plaintext` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the plaintext to encrypt. | #### Returns `CompactEncrypt` ## Methods ### encrypt() ▸ **encrypt**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Encrypts and resolves the value of the Compact JWE string. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Public Key or Secret to encrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../../../../types/interfaces/EncryptOptions.md) | JWE Encryption options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> *** ### ~~setContentEncryptionKey()~~ ▸ **setContentEncryptionKey**(`cek`): `this` Sets a content encryption key to use, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `cek` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Content Encryption Key. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### ~~setInitializationVector()~~ ▸ **setInitializationVector**(`iv`): `this` Sets the JWE Initialization Vector to use for content encryption, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `iv` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Initialization Vector. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### setKeyManagementParameters() ▸ **setKeyManagementParameters**(`parameters`): `this` Sets the JWE Key Management parameters to be used when encrypting. (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `parameters` | [`JWEKeyManagementHeaderParameters`](../../../../types/interfaces/JWEKeyManagementHeaderParameters.md) | JWE Key Management parameters. | #### Returns `this` *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWE Protected Header on the CompactEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWEHeaderParameters`](../../../../types/interfaces/CompactJWEHeaderParameters.md) | JWE Protected Header object. | #### Returns `this` ================================================ FILE: docs/jwe/flattened/decrypt/README.md ================================================ # jwe/flattened/decrypt Decrypting JSON Web Encryption (JWE) in Flattened JSON Serialization ## Interfaces - [FlattenedDecryptGetKey](interfaces/FlattenedDecryptGetKey.md) ## Functions - [flattenedDecrypt](functions/flattenedDecrypt.md) ================================================ FILE: docs/jwe/flattened/decrypt/functions/flattenedDecrypt.md ================================================ # Function: flattenedDecrypt() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **flattenedDecrypt**(`jwe`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedDecryptResult`](../../../../types/interfaces/FlattenedDecryptResult.md)\> Decrypts a Flattened JWE. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/flattened/decrypt'`. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | [`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md) | Flattened JWE. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedDecryptResult`](../../../../types/interfaces/FlattenedDecryptResult.md)\> ### Example ```js const jwe = { ciphertext: '9EzjFISUyoG-ifC2mSihfP0DPC80yeyrxhTzKt1C_VJBkxeBG0MI4Te61Pk45RAGubUvBpU9jm4', iv: '8Fy7A_IuoX5VXG9s', tag: 'W76IYV6arGRuDSaSyWrQNg', encrypted_key: 'Z6eD4UK_yFb5ZoKvKkGAdqywEG_m0e4IYo0x8Vf30LAMJcsc-_zSgIeiF82teZyYi2YYduHKoqImk7MRnoPZOlEs0Q5BNK1OgBmSOhCE8DFyqh9Zh48TCTP6lmBQ52naqoUJFMtHzu-0LwZH26hxos0GP3Dt19O379MJB837TdKKa87skq0zHaVLAquRHOBF77GI54Bc7O49d8aOrSu1VEFGMThlW2caspPRiTSePDMDPq7_WGk50izRhB3Asl9wmP9wEeaTrkJKRnQj5ips1SAZ1hDBsqEQKKukxP1HtdcopHV5_qgwU8Hjm5EwSLMluMQuiE6hwlkXGOujZLVizA', aad: 'VGhlIEZlbGxvd3NoaXAgb2YgdGhlIFJpbmc', protected: 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0', } const { plaintext, protectedHeader, additionalAuthenticatedData } = await jose.flattenedDecrypt(jwe, privateKey) console.log(protectedHeader) const decoder = new TextDecoder() console.log(decoder.decode(plaintext)) console.log(decoder.decode(additionalAuthenticatedData)) ``` ## Call Signature ▸ **flattenedDecrypt**(`jwe`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedDecryptResult`](../../../../types/interfaces/FlattenedDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | [`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md) | Flattened JWE. | | `getKey` | [`FlattenedDecryptGetKey`](../interfaces/FlattenedDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedDecryptResult`](../../../../types/interfaces/FlattenedDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jwe/flattened/decrypt/interfaces/FlattenedDecryptGetKey.md ================================================ # Interface: FlattenedDecryptGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for Flattened JWE Decryption dynamic key resolution. No token components have been verified at the time of this function call. ▸ **FlattenedDecryptGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) \| `undefined` | JWE or JWS Protected Header. | | `token` | [`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwe/flattened/encrypt/README.md ================================================ # jwe/flattened/encrypt Encrypting JSON Web Encryption (JWE) in Flattened JSON Serialization ## Classes - [FlattenedEncrypt](classes/FlattenedEncrypt.md) ================================================ FILE: docs/jwe/flattened/encrypt/classes/FlattenedEncrypt.md ================================================ # Class: FlattenedEncrypt ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/flattened/encrypt'`. ## Example ```js const jwe = await new jose.FlattenedEncrypt( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' }) .setAdditionalAuthenticatedData(encoder.encode('The Fellowship of the Ring')) .encrypt(publicKey) console.log(jwe) ``` ## Constructors ### Constructor ▸ **new FlattenedEncrypt**(`plaintext`): `FlattenedEncrypt` FlattenedEncrypt constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `plaintext` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the plaintext to encrypt. | #### Returns `FlattenedEncrypt` ## Methods ### encrypt() ▸ **encrypt**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md)\> Encrypts and resolves the value of the Flattened JWE object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Public Key or Secret to encrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../../../../types/interfaces/EncryptOptions.md) | JWE Encryption options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md)\> *** ### setAdditionalAuthenticatedData() ▸ **setAdditionalAuthenticatedData**(`aad`): `this` Sets the Additional Authenticated Data on the FlattenedEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `aad` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Additional Authenticated Data. | #### Returns `this` *** ### ~~setContentEncryptionKey()~~ ▸ **setContentEncryptionKey**(`cek`): `this` Sets a content encryption key to use, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `cek` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Content Encryption Key. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### ~~setInitializationVector()~~ ▸ **setInitializationVector**(`iv`): `this` Sets the JWE Initialization Vector to use for content encryption, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `iv` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Initialization Vector. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### setKeyManagementParameters() ▸ **setKeyManagementParameters**(`parameters`): `this` Sets the JWE Key Management parameters to be used when encrypting. (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `parameters` | [`JWEKeyManagementHeaderParameters`](../../../../types/interfaces/JWEKeyManagementHeaderParameters.md) | JWE Key Management parameters. | #### Returns `this` *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWE Protected Header on the FlattenedEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Protected Header. | #### Returns `this` *** ### setSharedUnprotectedHeader() ▸ **setSharedUnprotectedHeader**(`sharedUnprotectedHeader`): `this` Sets the JWE Shared Unprotected Header on the FlattenedEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sharedUnprotectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Shared Unprotected Header. | #### Returns `this` *** ### setUnprotectedHeader() ▸ **setUnprotectedHeader**(`unprotectedHeader`): `this` Sets the JWE Per-Recipient Unprotected Header on the FlattenedEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `unprotectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Per-Recipient Unprotected Header. | #### Returns `this` ================================================ FILE: docs/jwe/general/decrypt/README.md ================================================ # jwe/general/decrypt Decrypting JSON Web Encryption (JWE) in General JSON Serialization ## Interfaces - [GeneralDecryptGetKey](interfaces/GeneralDecryptGetKey.md) ## Functions - [generalDecrypt](functions/generalDecrypt.md) ================================================ FILE: docs/jwe/general/decrypt/functions/generalDecrypt.md ================================================ # Function: generalDecrypt() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **generalDecrypt**(`jwe`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralDecryptResult`](../../../../types/interfaces/GeneralDecryptResult.md)\> Decrypts a General JWE. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/general/decrypt'`. > [!NOTE]\ > The function iterates over the `recipients` array in the General JWE and returns the decryption > result of the first recipient entry that can be successfully decrypted. The result only contains > the plaintext and headers of that successfully decrypted recipient entry. Other recipient entries > in the General JWE are not validated, and their headers are not included in the returned result. > Recipients of a General JWE should only rely on the returned (decrypted) data. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | [`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md) | General JWE. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralDecryptResult`](../../../../types/interfaces/GeneralDecryptResult.md)\> ### Example ```js const jwe = { ciphertext: '9EzjFISUyoG-ifC2mSihfP0DPC80yeyrxhTzKt1C_VJBkxeBG0MI4Te61Pk45RAGubUvBpU9jm4', iv: '8Fy7A_IuoX5VXG9s', tag: 'W76IYV6arGRuDSaSyWrQNg', aad: 'VGhlIEZlbGxvd3NoaXAgb2YgdGhlIFJpbmc', protected: 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0', recipients: [ { encrypted_key: 'Z6eD4UK_yFb5ZoKvKkGAdqywEG_m0e4IYo0x8Vf30LAMJcsc-_zSgIeiF82teZyYi2YYduHKoqImk7MRnoPZOlEs0Q5BNK1OgBmSOhCE8DFyqh9Zh48TCTP6lmBQ52naqoUJFMtHzu-0LwZH26hxos0GP3Dt19O379MJB837TdKKa87skq0zHaVLAquRHOBF77GI54Bc7O49d8aOrSu1VEFGMThlW2caspPRiTSePDMDPq7_WGk50izRhB3Asl9wmP9wEeaTrkJKRnQj5ips1SAZ1hDBsqEQKKukxP1HtdcopHV5_qgwU8Hjm5EwSLMluMQuiE6hwlkXGOujZLVizA', }, ], } const { plaintext, protectedHeader, additionalAuthenticatedData } = await jose.generalDecrypt(jwe, privateKey) console.log(protectedHeader) const decoder = new TextDecoder() console.log(decoder.decode(plaintext)) console.log(decoder.decode(additionalAuthenticatedData)) ``` ## Call Signature ▸ **generalDecrypt**(`jwe`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralDecryptResult`](../../../../types/interfaces/GeneralDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwe` | [`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md) | General JWE. | | `getKey` | [`GeneralDecryptGetKey`](../interfaces/GeneralDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt the JWE with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`DecryptOptions`](../../../../types/interfaces/DecryptOptions.md) | JWE Decryption options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralDecryptResult`](../../../../types/interfaces/GeneralDecryptResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jwe/general/decrypt/interfaces/GeneralDecryptGetKey.md ================================================ # Interface: GeneralDecryptGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for General JWE Decryption dynamic key resolution. No token components have been verified at the time of this function call. ▸ **GeneralDecryptGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWE`](../../../../types/interfaces/FlattenedJWE.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwe/general/encrypt/README.md ================================================ # jwe/general/encrypt Encrypting JSON Web Encryption (JWE) in General JSON Serialization ## Classes - [GeneralEncrypt](classes/GeneralEncrypt.md) ## Interfaces - [Recipient](interfaces/Recipient.md) ================================================ FILE: docs/jwe/general/encrypt/classes/GeneralEncrypt.md ================================================ # Class: GeneralEncrypt ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The GeneralEncrypt class is used to build and encrypt General JWE objects. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwe/general/encrypt'`. ## Example ```js const jwe = await new jose.GeneralEncrypt( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ enc: 'A256GCM' }) .addRecipient(ecPublicKey) .setUnprotectedHeader({ alg: 'ECDH-ES+A256KW' }) .addRecipient(rsaPublicKey) .setUnprotectedHeader({ alg: 'RSA-OAEP-384' }) .encrypt() console.log(jwe) ``` ## Constructors ### Constructor ▸ **new GeneralEncrypt**(`plaintext`): `GeneralEncrypt` GeneralEncrypt constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `plaintext` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the plaintext to encrypt. | #### Returns `GeneralEncrypt` ## Methods ### addRecipient() ▸ **addRecipient**(`key`, `options?`): [`Recipient`](../interfaces/Recipient.md) Adds an additional recipient for the General JWE object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Public Key or Secret to encrypt the Content Encryption Key for the recipient with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`CritOption`](../../../../types/interfaces/CritOption.md) | JWE Encryption options. | #### Returns [`Recipient`](../interfaces/Recipient.md) *** ### encrypt() ▸ **encrypt**(): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md)\> Encrypts and resolves the value of the General JWE object. #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md)\> *** ### setAdditionalAuthenticatedData() ▸ **setAdditionalAuthenticatedData**(`aad`): `this` Sets the Additional Authenticated Data on the GeneralEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `aad` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Additional Authenticated Data. | #### Returns `this` *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWE Protected Header on the GeneralEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Protected Header object. | #### Returns `this` *** ### setSharedUnprotectedHeader() ▸ **setSharedUnprotectedHeader**(`sharedUnprotectedHeader`): `this` Sets the JWE Shared Unprotected Header on the GeneralEncrypt object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `sharedUnprotectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Shared Unprotected Header object. | #### Returns `this` ================================================ FILE: docs/jwe/general/encrypt/interfaces/Recipient.md ================================================ # Interface: Recipient ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Used to build General JWE object's individual recipients. ## Methods ### addRecipient() ▸ **addRecipient**(...`args`): `Recipient` A shorthand for calling addRecipient() on the enclosing [GeneralEncrypt](../classes/GeneralEncrypt.md) instance #### Parameters | Parameter | Type | | ------ | ------ | | ...`args` | \[[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md), [`CritOption`](../../../../types/interfaces/CritOption.md)\] | #### Returns `Recipient` *** ### done() ▸ **done**(): [`GeneralEncrypt`](../classes/GeneralEncrypt.md) Returns the enclosing [GeneralEncrypt](../classes/GeneralEncrypt.md) instance #### Returns [`GeneralEncrypt`](../classes/GeneralEncrypt.md) *** ### encrypt() ▸ **encrypt**(...`args`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md)\> A shorthand for calling encrypt() on the enclosing [GeneralEncrypt](../classes/GeneralEncrypt.md) instance #### Parameters | Parameter | Type | | ------ | ------ | | ...`args` | \[\] | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWE`](../../../../types/interfaces/GeneralJWE.md)\> *** ### setKeyManagementParameters() ▸ **setKeyManagementParameters**(`parameters`): `Recipient` Sets the JWE Key Management parameters to be used when encrypting. (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `parameters` | [`JWEKeyManagementHeaderParameters`](../../../../types/interfaces/JWEKeyManagementHeaderParameters.md) | JWE Key Management parameters. | #### Returns `Recipient` *** ### setUnprotectedHeader() ▸ **setUnprotectedHeader**(`unprotectedHeader`): `Recipient` Sets the JWE Per-Recipient Unprotected Header on the Recipient object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `unprotectedHeader` | [`JWEHeaderParameters`](../../../../types/interfaces/JWEHeaderParameters.md) | JWE Per-Recipient Unprotected Header. | #### Returns `Recipient` ================================================ FILE: docs/jwk/embedded/README.md ================================================ # jwk/embedded Verification using a JWK Embedded in a JWS Header ## Functions - [EmbeddedJWK](functions/EmbeddedJWK.md) ================================================ FILE: docs/jwk/embedded/functions/EmbeddedJWK.md ================================================ # Function: EmbeddedJWK() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **EmbeddedJWK**(`protectedHeader?`, `token?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> EmbeddedJWK is an implementation of a GetKeyFunction intended to be used with the JWS/JWT verify operations whenever you need to opt-in to verify signatures with a public key embedded in the token's "jwk" (JSON Web Key) Header Parameter. It is recommended to combine this with the verify function's `algorithms` option to define accepted JWS "alg" (Algorithm) Header Parameter values. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwk/embedded'`. ## Parameters | Parameter | Type | | ------ | ------ | | `protectedHeader?` | [`JWSHeaderParameters`](../../../types/interfaces/JWSHeaderParameters.md) | | `token?` | [`FlattenedJWSInput`](../../../types/interfaces/FlattenedJWSInput.md) | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const jwt = 'eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJ4IjoiVU05ZzVuS25aWFlvdldBbE03NmNMejl2VG96UmpfX0NIVV9kT2wtZ09vRSIsInkiOiJkczhhZVF3MWwyY0RDQTdiQ2tPTnZ3REtwWEFidFhqdnFDbGVZSDhXc19VIiwia3R5IjoiRUMifSwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSIsImlhdCI6MTYwNDU4MDc5NH0.60boak3_dErnW47ZPty1C0nrjeVq86EN_eK0GOq6K8w2OA0thKoBxFK4j-NuU9yZ_A9UKGxPT_G87DladBaV9g' const { payload, protectedHeader } = await jose.jwtVerify(jwt, jose.EmbeddedJWK, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` ================================================ FILE: docs/jwk/thumbprint/README.md ================================================ # jwk/thumbprint JSON Web Key Thumbprint and JSON Web Key Thumbprint URI ## Functions - [calculateJwkThumbprint](functions/calculateJwkThumbprint.md) - [calculateJwkThumbprintUri](functions/calculateJwkThumbprintUri.md) ================================================ FILE: docs/jwk/thumbprint/functions/calculateJwkThumbprint.md ================================================ # Function: calculateJwkThumbprint() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **calculateJwkThumbprint**(`key`, `digestAlgorithm?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Calculates a base64url-encoded JSON Web Key (JWK) Thumbprint This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwk/thumbprint'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to calculate the thumbprint for. | | `digestAlgorithm?` | `"sha256"` \| `"sha384"` \| `"sha512"` | Digest Algorithm to use for calculating the thumbprint. Default is "sha256". | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ## Example ```js const thumbprint = await jose.calculateJwkThumbprint({ kty: 'EC', crv: 'P-256', x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo', y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww', }) console.log(thumbprint) // 'w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8' ``` ## See [RFC7638](https://www.rfc-editor.org/rfc/rfc7638) ================================================ FILE: docs/jwk/thumbprint/functions/calculateJwkThumbprintUri.md ================================================ # Function: calculateJwkThumbprintUri() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **calculateJwkThumbprintUri**(`key`, `digestAlgorithm?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Calculates a JSON Web Key (JWK) Thumbprint URI This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwk/thumbprint'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to calculate the thumbprint for. | | `digestAlgorithm?` | `"sha256"` \| `"sha384"` \| `"sha512"` | Digest Algorithm to use for calculating the thumbprint. Default is "sha256". | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ## Example ```js const thumbprintUri = await jose.calculateJwkThumbprintUri({ kty: 'EC', crv: 'P-256', x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo', y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww', }) console.log(thumbprintUri) // 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8' ``` ## See [RFC9278](https://www.rfc-editor.org/rfc/rfc9278) ================================================ FILE: docs/jwks/local/README.md ================================================ # jwks/local Verification using a JSON Web Key Set (JWKS) available locally ## Functions - [createLocalJWKSet](functions/createLocalJWKSet.md) ================================================ FILE: docs/jwks/local/functions/createLocalJWKSet.md ================================================ # Function: createLocalJWKSet() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **createLocalJWKSet**(`jwks`): (`protectedHeader?`, `token?`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Returns a function that resolves a JWS JOSE Header to a public key object from a locally stored, or otherwise available, JSON Web Key Set. It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key Operations) Parameters (if they are present on the JWK). Only a single public key must match the selection process. As shown in the example below when multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt verification in an iterative manner. > [!NOTE]\ > The function's purpose is to resolve public keys used for verifying signatures and will not work > for public encryption keys. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwks/local'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwks` | [`JSONWebKeySet`](../../../types/interfaces/JSONWebKeySet.md) | JSON Web Key Set formatted object. | ## Returns (`protectedHeader?`, `token?`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Examples ```js const JWKS = jose.createLocalJWKSet({ keys: [ { kty: 'RSA', e: 'AQAB', n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ', alg: 'PS256', }, { crv: 'P-256', kty: 'EC', x: 'ySK38C1jBdLwDsNWKzzBHqKYEE5Cgv-qjWvorUXk9fw', y: '_LeQBw07cf5t57Iavn4j-BqJsAD1dpoz8gokd3sBsOo', alg: 'ES256', }, ], }) const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` Opting-in to multiple JWKS matches using `createLocalJWKSet` ```js const options = { issuer: 'urn:example:issuer', audience: 'urn:example:audience', } const { payload, protectedHeader } = await jose .jwtVerify(jwt, JWKS, options) .catch(async (error) => { if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { for await (const publicKey of error) { try { return await jose.jwtVerify(jwt, publicKey, options) } catch (innerError) { if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { continue } throw innerError } } throw new jose.errors.JWSSignatureVerificationFailed() } throw error }) console.log(protectedHeader) console.log(payload) ``` ================================================ FILE: docs/jwks/remote/README.md ================================================ # jwks/remote Verification using a JSON Web Key Set (JWKS) available on an HTTP(S) URL ## Interfaces - [ExportedJWKSCache](interfaces/ExportedJWKSCache.md) - [RemoteJWKSetOptions](interfaces/RemoteJWKSetOptions.md) ## Type Aliases - [FetchImplementation](type-aliases/FetchImplementation.md) - [JWKSCacheInput](type-aliases/JWKSCacheInput.md) ## Variables - [customFetch](variables/customFetch.md) - [jwksCache](variables/jwksCache.md) ## Functions - [createRemoteJWKSet](functions/createRemoteJWKSet.md) ================================================ FILE: docs/jwks/remote/functions/createRemoteJWKSet.md ================================================ # Function: createRemoteJWKSet() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **createRemoteJWKSet**(`url`, `options?`): (`protectedHeader?`, `token?`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Returns a function that resolves a JWS JOSE Header to a public key object downloaded from a remote endpoint returning a JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC jwks_uri. The JSON Web Key Set is fetched when no key matches the selection process but only as frequently as the `cooldownDuration` option allows to prevent abuse. It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key Operations) Parameters (if they are present on the JWK). Only a single public key must match the selection process. As shown in the example below when multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt verification in an iterative manner. > [!NOTE]\ > The function's purpose is to resolve public keys used for verifying signatures and will not work > for public encryption keys. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwks/remote'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | [`URL`](https://developer.mozilla.org/docs/Web/API/URL) | URL to fetch the JSON Web Key Set from. | | `options?` | [`RemoteJWKSetOptions`](../interfaces/RemoteJWKSetOptions.md) | Options for the remote JSON Web Key Set. | ## Returns (`protectedHeader?`, `token?`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Examples ```js const JWKS = jose.createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')) const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` Opting-in to multiple JWKS matches using `createRemoteJWKSet` ```js const options = { issuer: 'urn:example:issuer', audience: 'urn:example:audience', } const { payload, protectedHeader } = await jose .jwtVerify(jwt, JWKS, options) .catch(async (error) => { if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { for await (const publicKey of error) { try { return await jose.jwtVerify(jwt, publicKey, options) } catch (innerError) { if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { continue } throw innerError } } throw new jose.errors.JWSSignatureVerificationFailed() } throw error }) console.log(protectedHeader) console.log(payload) ``` ================================================ FILE: docs/jwks/remote/interfaces/ExportedJWKSCache.md ================================================ # Interface: ExportedJWKSCache ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). See [jwksCache](../variables/jwksCache.md). ## Properties ### jwks • **jwks**: [`JSONWebKeySet`](../../../types/interfaces/JSONWebKeySet.md) Current cached JSON Web Key Set *** ### uat • **uat**: `number` Last updated at timestamp (seconds since epoch) ================================================ FILE: docs/jwks/remote/interfaces/RemoteJWKSetOptions.md ================================================ # Interface: RemoteJWKSetOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Options for the remote JSON Web Key Set. ## Properties ### \[customFetch\]? • `optional` **\[customFetch\]?**: [`FetchImplementation`](../type-aliases/FetchImplementation.md) See [customFetch](../variables/customFetch.md). *** ### \[jwksCache\]? • `optional` **\[jwksCache\]?**: [`JWKSCacheInput`](../type-aliases/JWKSCacheInput.md) See [jwksCache](../variables/jwksCache.md). *** ### cacheMaxAge? • `optional` **cacheMaxAge?**: `number` Maximum time (in milliseconds) between successful HTTP requests. Default is 600000 (10 minutes). *** ### cooldownDuration? • `optional` **cooldownDuration?**: `number` Duration (in milliseconds) for which no more HTTP requests will be triggered after a previous successful fetch. Default is 30000 (30 seconds). *** ### headers? • `optional` **headers?**: [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type)\<`string`, `string`\> Headers to be sent with the HTTP request. *** ### timeoutDuration? • `optional` **timeoutDuration?**: `number` Timeout (in milliseconds) for the HTTP request. When reached the request will be aborted and the verification will fail. Default is 5000 (5 seconds). ================================================ FILE: docs/jwks/remote/type-aliases/FetchImplementation.md ================================================ # Type Alias: FetchImplementation ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • **FetchImplementation** = (`url`, `options`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\> See [customFetch](../variables/customFetch.md). ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `url` | `string` | - | | `options` | \{ `headers`: [`Headers`](https://developer.mozilla.org/docs/Web/API/Headers); `method`: `"GET"`; `redirect`: `"manual"`; `signal`: [`AbortSignal`](https://developer.mozilla.org/docs/Web/API/AbortSignal); \} | - | | `options.headers` | [`Headers`](https://developer.mozilla.org/docs/Web/API/Headers) | HTTP Headers | | `options.method` | `"GET"` | The [request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) | | `options.redirect` | `"manual"` | See [Request.redirect](https://developer.mozilla.org/docs/Web/API/Request/redirect) | | `options.signal` | [`AbortSignal`](https://developer.mozilla.org/docs/Web/API/AbortSignal) | - | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\> ================================================ FILE: docs/jwks/remote/type-aliases/JWKSCacheInput.md ================================================ # Type Alias: JWKSCacheInput ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • **JWKSCacheInput** = [`ExportedJWKSCache`](../interfaces/ExportedJWKSCache.md) \| [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type)\<`string`, `never`\> See [jwksCache](../variables/jwksCache.md). ================================================ FILE: docs/jwks/remote/variables/customFetch.md ================================================ # Variable: customFetch ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • `const` **customFetch**: unique `symbol` When passed to [createRemoteJWKSet](../functions/createRemoteJWKSet.md) this allows the resolver to make use of advanced fetch configurations, HTTP Proxies, retry on network errors, etc. > [!NOTE]\ > Known caveat: Expect Type-related issues when passing the inputs through to fetch-like modules, > they hardly ever get their typings inline with actual fetch, you should `@ts-expect-error` them. ## Examples Using [sindresorhus/ky](https://github.com/sindresorhus/ky) for retries and its hooks feature for logging outgoing requests and their responses. ```ts import ky from 'ky' let logRequest!: (request: Request) => void let logResponse!: (request: Request, response: Response) => void let logRetry!: (request: Request, error: Error, retryCount: number) => void const JWKS = jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) => ky(args[0], { ...args[1], hooks: { beforeRequest: [ (request) => { logRequest(request) }, ], beforeRetry: [ ({ request, error, retryCount }) => { logRetry(request, error, retryCount) }, ], afterResponse: [ (request, _, response) => { logResponse(request, response) }, ], }, }), }) ``` Using [nodejs/undici](https://github.com/nodejs/undici) to detect and use HTTP proxies. ```ts import * as undici from 'undici' // see https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent let envHttpProxyAgent = new undici.EnvHttpProxyAgent() // @ts-ignore const JWKS = jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) => { // @ts-ignore return undici.fetch(args[0], { ...args[1], dispatcher: envHttpProxyAgent }) // prettier-ignore }, }) ``` Using [nodejs/undici](https://github.com/nodejs/undici) to automatically retry network errors. ```ts import * as undici from 'undici' // see https://undici.nodejs.org/#/docs/api/RetryAgent let retryAgent = new undici.RetryAgent(new undici.Agent(), { statusCodes: [], errorCodes: [ 'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'UND_ERR_SOCKET', ], }) // @ts-ignore const JWKS = jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) => { // @ts-ignore return undici.fetch(args[0], { ...args[1], dispatcher: retryAgent }) // prettier-ignore }, }) ``` Using [nodejs/undici](https://github.com/nodejs/undici) to mock responses in tests. ```ts import * as undici from 'undici' // see https://undici.nodejs.org/#/docs/api/MockAgent let mockAgent = new undici.MockAgent() mockAgent.disableNetConnect() // @ts-ignore const JWKS = jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) => { // @ts-ignore return undici.fetch(args[0], { ...args[1], dispatcher: mockAgent }) // prettier-ignore }, }) ``` ================================================ FILE: docs/jwks/remote/variables/jwksCache.md ================================================ # Variable: jwksCache ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • `const` **jwksCache**: unique `symbol` > [!WARNING]\ > This option has security implications that must be understood, assessed for applicability, and > accepted before use. It is critical that the JSON Web Key Set cache only be writable by your own > code. This option is intended for cloud computing runtimes that cannot keep an in memory cache between their code's invocations. Use in runtimes where an in memory cache between requests is available is not desirable. When passed to [createRemoteJWKSet](../functions/createRemoteJWKSet.md) this allows the passed in object to: - Serve as an initial value for the JSON Web Key Set that the module would otherwise need to trigger an HTTP request for - Have the JSON Web Key Set the function optionally ended up triggering an HTTP request for assigned to it as properties The intended use pattern is: - Before verifying with [createRemoteJWKSet](../functions/createRemoteJWKSet.md) you pull the previously cached object from a low-latency key-value store offered by the cloud computing runtime it is executed on; - Default to an empty object `{}` instead when there's no previously cached value; - Pass it in as [\[jwksCache\]](../interfaces/RemoteJWKSetOptions.md); - Afterwards, update the key-value storage if the [`uat`](../interfaces/ExportedJWKSCache.md#uat) property of the object has changed. ## Example ```ts // Prerequisites let url!: URL let jwt!: string let getPreviouslyCachedJWKS!: () => Promise let storeNewJWKScache!: (cache: jose.ExportedJWKSCache) => Promise // Load JSON Web Key Set cache const jwksCache: jose.JWKSCacheInput = (await getPreviouslyCachedJWKS()) || {} const { uat } = jwksCache const JWKS = jose.createRemoteJWKSet(url, { [jose.jwksCache]: jwksCache, }) // Use JSON Web Key Set cache await jose.jwtVerify(jwt, JWKS) if (uat !== jwksCache.uat) { // Update JSON Web Key Set cache await storeNewJWKScache(jwksCache) } ``` ================================================ FILE: docs/jws/compact/sign/README.md ================================================ # jws/compact/sign Signing JSON Web Signature (JWS) in Compact Serialization ## Classes - [CompactSign](classes/CompactSign.md) ================================================ FILE: docs/jws/compact/sign/classes/CompactSign.md ================================================ # Class: CompactSign ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The CompactSign class is used to build and sign Compact JWS strings. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/compact/sign'`. ## Example ```js const jws = await new jose.CompactSign( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256' }) .sign(privateKey) console.log(jws) ``` ## Constructors ### Constructor ▸ **new CompactSign**(`payload`): `CompactSign` CompactSign constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the payload to sign. | #### Returns `CompactSign` ## Methods ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWS Protected Header on the CompactSign object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWSHeaderParameters`](../../../../types/interfaces/CompactJWSHeaderParameters.md) | JWS Protected Header. | #### Returns `this` *** ### sign() ▸ **sign**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Signs and resolves the value of the Compact JWS string. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to sign the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../../../../types/interfaces/SignOptions.md) | JWS Sign options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ================================================ FILE: docs/jws/compact/verify/README.md ================================================ # jws/compact/verify Verifying JSON Web Signature (JWS) in Compact Serialization ## Interfaces - [CompactVerifyGetKey](interfaces/CompactVerifyGetKey.md) ## Functions - [compactVerify](functions/compactVerify.md) ================================================ FILE: docs/jws/compact/verify/functions/compactVerify.md ================================================ # Function: compactVerify() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **compactVerify**(`jws`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactVerifyResult`](../../../../types/interfaces/CompactVerifyResult.md)\> Verifies the signature and format of and afterwards decodes the Compact JWS. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/compact/verify'`. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Compact JWS. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactVerifyResult`](../../../../types/interfaces/CompactVerifyResult.md)\> ### Example ```js const jws = 'eyJhbGciOiJFUzI1NiJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.kkAs_gPPxWMI3rHuVlxHaTPfDWDoqdI8jSvuSmqV-8IHIWXg9mcAeC9ggV-45ZHRbiRJ3obUIFo1rHphPA5URg' const { payload, protectedHeader } = await jose.compactVerify(jws, publicKey) console.log(protectedHeader) console.log(new TextDecoder().decode(payload)) ``` ## Call Signature ▸ **compactVerify**(`jws`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactVerifyResult`](../../../../types/interfaces/CompactVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Compact JWS. | | `getKey` | [`CompactVerifyGetKey`](../interfaces/CompactVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CompactVerifyResult`](../../../../types/interfaces/CompactVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jws/compact/verify/interfaces/CompactVerifyGetKey.md ================================================ # Interface: CompactVerifyGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for Compact JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. ## See [createRemoteJWKSet](../../../../jwks/remote/functions/createRemoteJWKSet.md) to verify using a remote JSON Web Key Set. ▸ **CompactVerifyGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWSHeaderParameters`](../../../../types/interfaces/CompactJWSHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWSInput`](../../../../types/interfaces/FlattenedJWSInput.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jws/flattened/sign/README.md ================================================ # jws/flattened/sign Signing JSON Web Signature (JWS) in Flattened JSON Serialization ## Classes - [FlattenedSign](classes/FlattenedSign.md) ================================================ FILE: docs/jws/flattened/sign/classes/FlattenedSign.md ================================================ # Class: FlattenedSign ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The FlattenedSign class is used to build and sign Flattened JWS objects. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/flattened/sign'`. ## Example ```js const jws = await new jose.FlattenedSign( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256' }) .sign(privateKey) console.log(jws) ``` ## Constructors ### Constructor ▸ **new FlattenedSign**(`payload`): `FlattenedSign` FlattenedSign constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the payload to sign. | #### Returns `FlattenedSign` ## Methods ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWS Protected Header on the FlattenedSign object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) | JWS Protected Header. | #### Returns `this` *** ### setUnprotectedHeader() ▸ **setUnprotectedHeader**(`unprotectedHeader`): `this` Sets the JWS Unprotected Header on the FlattenedSign object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `unprotectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) | JWS Unprotected Header. | #### Returns `this` *** ### sign() ▸ **sign**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedJWS`](../../../../types/interfaces/FlattenedJWS.md)\> Signs and resolves the value of the Flattened JWS object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to sign the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../../../../types/interfaces/SignOptions.md) | JWS Sign options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedJWS`](../../../../types/interfaces/FlattenedJWS.md)\> ================================================ FILE: docs/jws/flattened/verify/README.md ================================================ # jws/flattened/verify Verifying JSON Web Signature (JWS) in Flattened JSON Serialization ## Interfaces - [FlattenedVerifyGetKey](interfaces/FlattenedVerifyGetKey.md) ## Functions - [flattenedVerify](functions/flattenedVerify.md) ================================================ FILE: docs/jws/flattened/verify/functions/flattenedVerify.md ================================================ # Function: flattenedVerify() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **flattenedVerify**(`jws`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedVerifyResult`](../../../../types/interfaces/FlattenedVerifyResult.md)\> Verifies the signature and format of and afterwards decodes the Flattened JWS. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/flattened/verify'`. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | [`FlattenedJWSInput`](../../../../types/interfaces/FlattenedJWSInput.md) | Flattened JWS. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedVerifyResult`](../../../../types/interfaces/FlattenedVerifyResult.md)\> ### Example ```js const decoder = new TextDecoder() const jws = { signature: 'FVVOXwj6kD3DqdfD9yYqfT2W9jv-Nop4kOehp_DeDGNB5dQNSPRvntBY6xH3uxlCxE8na9d_kyhYOcanpDJ0EA', payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', protected: 'eyJhbGciOiJFUzI1NiJ9', } const { payload, protectedHeader } = await jose.flattenedVerify(jws, publicKey) console.log(protectedHeader) console.log(decoder.decode(payload)) ``` ## Call Signature ▸ **flattenedVerify**(`jws`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedVerifyResult`](../../../../types/interfaces/FlattenedVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | [`FlattenedJWSInput`](../../../../types/interfaces/FlattenedJWSInput.md) | Flattened JWS. | | `getKey` | [`FlattenedVerifyGetKey`](../interfaces/FlattenedVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`FlattenedVerifyResult`](../../../../types/interfaces/FlattenedVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jws/flattened/verify/interfaces/FlattenedVerifyGetKey.md ================================================ # Interface: FlattenedVerifyGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for Flattened JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. ## See [createRemoteJWKSet](../../../../jwks/remote/functions/createRemoteJWKSet.md) to verify using a remote JSON Web Key Set. ▸ **FlattenedVerifyGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) \| `undefined` | JWE or JWS Protected Header. | | `token` | [`FlattenedJWSInput`](../../../../types/interfaces/FlattenedJWSInput.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jws/general/sign/README.md ================================================ # jws/general/sign Signing JSON Web Signature (JWS) in General JSON Serialization ## Classes - [GeneralSign](classes/GeneralSign.md) ## Interfaces - [Signature](interfaces/Signature.md) ================================================ FILE: docs/jws/general/sign/classes/GeneralSign.md ================================================ # Class: GeneralSign ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The GeneralSign class is used to build and sign General JWS objects. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/general/sign'`. ## Example ```js const jws = await new jose.GeneralSign( new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), ) .addSignature(ecPrivateKey) .setProtectedHeader({ alg: 'ES256' }) .addSignature(rsaPrivateKey) .setProtectedHeader({ alg: 'PS256' }) .sign() console.log(jws) ``` ## Constructors ### Constructor ▸ **new GeneralSign**(`payload`): `GeneralSign` GeneralSign constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Binary representation of the payload to sign. | #### Returns `GeneralSign` ## Methods ### addSignature() ▸ **addSignature**(`key`, `options?`): [`Signature`](../interfaces/Signature.md) Adds an additional signature for the General JWS object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Private Key or Secret to sign the individual JWS signature with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../../../../types/interfaces/SignOptions.md) | JWS Sign options. | #### Returns [`Signature`](../interfaces/Signature.md) *** ### sign() ▸ **sign**(): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWS`](../../../../types/interfaces/GeneralJWS.md)\> Signs and resolves the value of the General JWS object. #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWS`](../../../../types/interfaces/GeneralJWS.md)\> ================================================ FILE: docs/jws/general/sign/interfaces/Signature.md ================================================ # Interface: Signature ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Used to build General JWS object's individual signatures. ## Methods ### addSignature() ▸ **addSignature**(...`args`): `Signature` A shorthand for calling addSignature() on the enclosing [GeneralSign](../classes/GeneralSign.md) instance #### Parameters | Parameter | Type | | ------ | ------ | | ...`args` | \[[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md), [`SignOptions`](../../../../types/interfaces/SignOptions.md)\] | #### Returns `Signature` *** ### done() ▸ **done**(): [`GeneralSign`](../classes/GeneralSign.md) Returns the enclosing [GeneralSign](../classes/GeneralSign.md) instance #### Returns [`GeneralSign`](../classes/GeneralSign.md) *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `Signature` Sets the JWS Protected Header on the Signature object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) | JWS Protected Header. | #### Returns `Signature` *** ### setUnprotectedHeader() ▸ **setUnprotectedHeader**(`unprotectedHeader`): `Signature` Sets the JWS Unprotected Header on the Signature object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `unprotectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) | JWS Unprotected Header. | #### Returns `Signature` *** ### sign() ▸ **sign**(...`args`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWS`](../../../../types/interfaces/GeneralJWS.md)\> A shorthand for calling encrypt() on the enclosing [GeneralSign](../classes/GeneralSign.md) instance #### Parameters | Parameter | Type | | ------ | ------ | | ...`args` | \[\] | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralJWS`](../../../../types/interfaces/GeneralJWS.md)\> ================================================ FILE: docs/jws/general/verify/README.md ================================================ # jws/general/verify Verifying JSON Web Signature (JWS) in General JSON Serialization ## Interfaces - [GeneralVerifyGetKey](interfaces/GeneralVerifyGetKey.md) ## Functions - [generalVerify](functions/generalVerify.md) ================================================ FILE: docs/jws/general/verify/functions/generalVerify.md ================================================ # Function: generalVerify() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **generalVerify**(`jws`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralVerifyResult`](../../../../types/interfaces/GeneralVerifyResult.md)\> Verifies the signature and format of and afterwards decodes the General JWS. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jws/general/verify'`. > [!NOTE]\ > The function iterates over the `signatures` array in the General JWS and returns the verification > result of the first signature entry that can be successfully verified. The result only contains > the payload, protected header, and unprotected header of that successfully verified signature > entry. Other signature entries in the General JWS are not validated, and their headers are not > included in the returned result. Recipients of a General JWS should only rely on the returned > (verified) data. ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | [`GeneralJWSInput`](../../../../types/interfaces/GeneralJWSInput.md) | General JWS. | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) | Key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralVerifyResult`](../../../../types/interfaces/GeneralVerifyResult.md)\> ### Example ```js const jws = { payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signatures: [ { signature: 'FVVOXwj6kD3DqdfD9yYqfT2W9jv-Nop4kOehp_DeDGNB5dQNSPRvntBY6xH3uxlCxE8na9d_kyhYOcanpDJ0EA', protected: 'eyJhbGciOiJFUzI1NiJ9', }, ], } const { payload, protectedHeader } = await jose.generalVerify(jws, publicKey) console.log(protectedHeader) console.log(new TextDecoder().decode(payload)) ``` ## Call Signature ▸ **generalVerify**(`jws`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralVerifyResult`](../../../../types/interfaces/GeneralVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jws` | [`GeneralJWSInput`](../../../../types/interfaces/GeneralJWSInput.md) | General JWS. | | `getKey` | [`GeneralVerifyGetKey`](../interfaces/GeneralVerifyGetKey.md) | Function resolving a key to verify the JWS with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`VerifyOptions`](../../../../types/interfaces/VerifyOptions.md) | JWS Verify options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GeneralVerifyResult`](../../../../types/interfaces/GeneralVerifyResult.md) & [`ResolvedKey`](../../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jws/general/verify/interfaces/GeneralVerifyGetKey.md ================================================ # Interface: GeneralVerifyGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for General JWS Verification dynamic key resolution. No token components have been verified at the time of this function call. ## See [createRemoteJWKSet](../../../../jwks/remote/functions/createRemoteJWKSet.md) to verify using a remote JSON Web Key Set. ▸ **GeneralVerifyGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWSHeaderParameters`](../../../../types/interfaces/JWSHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWSInput`](../../../../types/interfaces/FlattenedJWSInput.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwt/decrypt/README.md ================================================ # jwt/decrypt JSON Web Token (JWT) Decryption (JWT is in JWE format) ## Interfaces - [JWTDecryptGetKey](interfaces/JWTDecryptGetKey.md) - [JWTDecryptOptions](interfaces/JWTDecryptOptions.md) ## Functions - [jwtDecrypt](functions/jwtDecrypt.md) ================================================ FILE: docs/jwt/decrypt/functions/jwtDecrypt.md ================================================ # Function: jwtDecrypt() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **jwtDecrypt**\<`PayloadType`\>(`jwt`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTDecryptResult`](../../../types/interfaces/JWTDecryptResult.md)\<`PayloadType`\>\> Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT Claims Set. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/decrypt'`. ### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JSON Web Token value (encoded as JWE). | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Private Key or Secret to decrypt and verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`JWTDecryptOptions`](../interfaces/JWTDecryptOptions.md) | JWT Decryption and JWT Claims Set validation options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTDecryptResult`](../../../types/interfaces/JWTDecryptResult.md)\<`PayloadType`\>\> ### Example ```js const secret = jose.base64url.decode('zH4NRP1HMALxxCFnRZABFA7GOJtzU_gIj02alfL1lvI') const jwt = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..MB66qstZBPxAXKdsjet_lA.WHbtJTl4taHp7otOHLq3hBvv0yNPsPEKHYInmCPdDDeyV1kU-f-tGEiU4FxlSqkqAT2hVs8_wMNiQFAzPU1PUgIqWCPsBrPP3TtxYsrtwagpn4SvCsUsx0Mhw9ZhliAO8CLmCBQkqr_T9AcYsz5uZw.7nX9m7BGUu_u1p1qFHzyIg' const { payload, protectedHeader } = await jose.jwtDecrypt(jwt, secret, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` ## Call Signature ▸ **jwtDecrypt**\<`PayloadType`\>(`jwt`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTDecryptResult`](../../../types/interfaces/JWTDecryptResult.md)\<`PayloadType`\> & [`ResolvedKey`](../../../types/interfaces/ResolvedKey.md)\> ### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JSON Web Token value (encoded as JWE). | | `getKey` | [`JWTDecryptGetKey`](../interfaces/JWTDecryptGetKey.md) | Function resolving Private Key or Secret to decrypt and verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`JWTDecryptOptions`](../interfaces/JWTDecryptOptions.md) | JWT Decryption and JWT Claims Set validation options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTDecryptResult`](../../../types/interfaces/JWTDecryptResult.md)\<`PayloadType`\> & [`ResolvedKey`](../../../types/interfaces/ResolvedKey.md)\> ================================================ FILE: docs/jwt/decrypt/interfaces/JWTDecryptGetKey.md ================================================ # Interface: JWTDecryptGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for JWT Decryption dynamic key resolution. No token components have been verified at the time of this function call. ▸ **JWTDecryptGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWEHeaderParameters`](../../../types/interfaces/CompactJWEHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWE`](../../../types/interfaces/FlattenedJWE.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwt/decrypt/interfaces/JWTDecryptOptions.md ================================================ # Interface: JWTDecryptOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Combination of JWE Decryption options and JWT Claims Set verification options. ## Properties ### audience? • `optional` **audience?**: `string` \| `string`[] Expected JWT "aud" (Audience) Claim value(s). This option makes the JWT "aud" (Audience) Claim presence required. *** ### clockTolerance? • `optional` **clockTolerance?**: `string` \| `number` Clock skew tolerance - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). Used when validating the JWT "nbf" (Not Before) and "exp" (Expiration Time) claims, and when validating the "iat" (Issued At) claim if the [`maxTokenAge` option](../../../types/interfaces/JWTClaimVerificationOptions.md#maxtokenage) is set. *** ### contentEncryptionAlgorithms? • `optional` **contentEncryptionAlgorithms?**: `string`[] A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed. *** ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` *** ### currentDate? • `optional` **currentDate?**: [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) Date to use when comparing NumericDate claims, defaults to `new Date()`. *** ### issuer? • `optional` **issuer?**: `string` \| `string`[] Expected JWT "iss" (Issuer) Claim value(s). This option makes the JWT "iss" (Issuer) Claim presence required. *** ### keyManagementAlgorithms? • `optional` **keyManagementAlgorithms?**: `string`[] A list of accepted JWE "alg" (Algorithm) Header Parameter values. By default all "alg" (Algorithm) Header Parameter values applicable for the used key/secret are allowed except for all PBES2 Key Management Algorithms, these need to be explicitly allowed using this option. *** ### maxDecompressedLength? • `optional` **maxDecompressedLength?**: `number` Maximum allowed size (in bytes) of the decompressed plaintext when the JWE `"zip"` (Compression Algorithm) Header Parameter is present. By default this value is set to 250000 (250 KB). The value must be `0`, a positive safe integer, or `Infinity`. Set to `0` to reject all compressed JWEs during decryption. Set to `Infinity` to disable the decompressed size limit. *** ### maxPBES2Count? • `optional` **maxPBES2Count?**: `number` (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter value. The PBKDF2 iteration count defines the algorithm's computational expense. By default this value is set to 10000. *** ### maxTokenAge? • `optional` **maxTokenAge?**: `string` \| `number` Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). This option makes the JWT "iat" (Issued At) Claim presence required. *** ### requiredClaims? • `optional` **requiredClaims?**: `string`[] Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if the [`issuer` option](../../../types/interfaces/JWTClaimVerificationOptions.md#issuer) is set, then JWT "iss" (Issuer) Claim must be present; if the [`audience` option](../../../types/interfaces/JWTClaimVerificationOptions.md#audience) is set, then JWT "aud" (Audience) Claim must be present; if the [`subject` option](../../../types/interfaces/JWTClaimVerificationOptions.md#subject) is set, then JWT "sub" (Subject) Claim must be present; if the [`maxTokenAge` option](../../../types/interfaces/JWTClaimVerificationOptions.md#maxtokenage) is set, then JWT "iat" (Issued At) Claim must be present. *** ### subject? • `optional` **subject?**: `string` Expected JWT "sub" (Subject) Claim value. This option makes the JWT "sub" (Subject) Claim presence required. *** ### typ? • `optional` **typ?**: `string` Expected JWT "typ" (Type) Header Parameter value. This option makes the JWT "typ" (Type) Header Parameter presence required. ================================================ FILE: docs/jwt/encrypt/README.md ================================================ # jwt/encrypt JSON Web Token (JWT) Encryption (JWT is in JWE format) ## Classes - [EncryptJWT](classes/EncryptJWT.md) ================================================ FILE: docs/jwt/encrypt/classes/EncryptJWT.md ================================================ # Class: EncryptJWT ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/encrypt'`. ## Example ```js const secret = jose.base64url.decode('zH4NRP1HMALxxCFnRZABFA7GOJtzU_gIj02alfL1lvI') const jwt = await new jose.EncryptJWT({ 'urn:example:claim': true }) .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256' }) .setIssuedAt() .setIssuer('urn:example:issuer') .setAudience('urn:example:audience') .setExpirationTime('2h') .encrypt(secret) console.log(jwt) ``` ## Constructors ### Constructor ▸ **new EncryptJWT**(`payload?`): `EncryptJWT` EncryptJWT constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | The JWT Claims Set object. Defaults to an empty object. | #### Returns `EncryptJWT` ## Methods ### encrypt() ▸ **encrypt**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Encrypts and returns the JWT. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Public Key or Secret to encrypt the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg). | | `options?` | [`EncryptOptions`](../../../types/interfaces/EncryptOptions.md) | JWE Encryption options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> *** ### replicateAudienceAsHeader() ▸ **replicateAudienceAsHeader**(): `this` Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. #### Returns `this` #### See [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) *** ### replicateIssuerAsHeader() ▸ **replicateIssuerAsHeader**(): `this` Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. #### Returns `this` #### See [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) *** ### replicateSubjectAsHeader() ▸ **replicateSubjectAsHeader**(): `this` Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. #### Returns `this` #### See [RFC7519#section-5.3](https://www.rfc-editor.org/rfc/rfc7519#section-5.3) *** ### setAudience() ▸ **setAudience**(`audience`): `this` Set the "aud" (Audience) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `audience` | `string` \| `string`[] | "aud" (Audience) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### ~~setContentEncryptionKey()~~ ▸ **setContentEncryptionKey**(`cek`): `this` Sets a content encryption key to use, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `cek` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Content Encryption Key. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### setExpirationTime() ▸ **setExpirationTime**(`input`): `this` Set the "exp" (Expiration Time) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "exp" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### ~~setInitializationVector()~~ ▸ **setInitializationVector**(`iv`): `this` Sets the JWE Initialization Vector to use for content encryption, by default a random suitable one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `iv` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JWE Initialization Vector. | #### Returns `this` #### Deprecated You should not use this method. It is only really intended for test and vector validation purposes. *** ### setIssuedAt() ▸ **setIssuedAt**(`input?`): `this` Set the "iat" (Issued At) Claim. - If no argument is used the current unix timestamp is used as the claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input?` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "iat" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuer() ▸ **setIssuer**(`issuer`): `this` Set the "iss" (Issuer) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `issuer` | `string` | "Issuer" Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setJti() ▸ **setJti**(`jwtId`): `this` Set the "jti" (JWT ID) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwtId` | `string` | "jti" (JWT ID) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setKeyManagementParameters() ▸ **setKeyManagementParameters**(`parameters`): `this` Sets the JWE Key Management parameters to be used when encrypting. (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `parameters` | [`JWEKeyManagementHeaderParameters`](../../../types/interfaces/JWEKeyManagementHeaderParameters.md) | JWE Key Management parameters. | #### Returns `this` *** ### setNotBefore() ▸ **setNotBefore**(`input`): `this` Set the "nbf" (Not Before) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "nbf" (Not Before) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWE Protected Header on the EncryptJWT object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`CompactJWEHeaderParameters`](../../../types/interfaces/CompactJWEHeaderParameters.md) | JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc" (JWE Encryption Algorithm) properties. | #### Returns `this` *** ### setSubject() ▸ **setSubject**(`subject`): `this` Set the "sub" (Subject) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `subject` | `string` | "sub" (Subject) Claim value to set on the JWT Claims Set. | #### Returns `this` ================================================ FILE: docs/jwt/sign/README.md ================================================ # jwt/sign JSON Web Token (JWT) Signing (JWT is in JWS format) ## Classes - [SignJWT](classes/SignJWT.md) ================================================ FILE: docs/jwt/sign/classes/SignJWT.md ================================================ # Class: SignJWT ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/sign'`. ## Examples Usage with a symmetric secret ```js const secret = new TextEncoder().encode( 'cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2', ) const alg = 'HS256' const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) .setProtectedHeader({ alg }) .setIssuedAt() .setIssuer('urn:example:issuer') .setAudience('urn:example:audience') .setExpirationTime('2h') .sign(secret) console.log(jwt) ``` Usage with a private PKCS#8 encoded RSA key ```js const alg = 'RS256' const pkcs8 = `-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCFg4UrY5xtulv /NXKmL1J4qI1SopAfTNMo3X7p+kJO7plqUYjzaztcre1qfh0m33Sm1Q8oPbO/GpP MU1/HgcceytgJ/b4UwufVVMl9BrMDYG8moDBylbVupFQS3Ly1L9i/iFG9Z9A9xzY Zzf799A45bnvNXL6s2glzvjiRvfQ2NDF0anTcnZLcYtC7ugq1IMM+ihAcPfw8Qw2 chN/SmP4qAM+PKaQwagmU7doqmmyN9u38AfoYZ1GCFhEs5TBBT6H6h9YdHeVtiIq 1c+fl03biSIfLrV7dUBD39gBmXBcL/30Ya3D82mCEUC4zg/UkOfQOmkmV3Lc8YUL QZ8EJkBLAgMBAAECggEAVuVE/KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34ls VOPK0XDegZkhAybMZHjRhp+gwVxX5ChC+J3cUpOBH5FNxElgW6HizD2Jcq6t6LoL YgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG/qJV1K00/Ly1G1QKoBffEs +v4fAMJrCbUdCz1qWto+PU+HLMEo+krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq/ aAP4a1SXmo+j0cvRU4W5Fj0RVwNesIpetX2ZFz4p/JmB5sWFEj/fC7h5z2lq+6Bm e2T3BHtXkIxoBW0/pYVnASC8P2puO5FnVxDmWuHDYQKBgQDTuuBd3+0tSFVEX+DU 5qpFmHm5nyGItZRJTS+71yg5pBxq1KqNCUjAtbxR0q//fwauakh+BwRVCPOrqsUG jBSb3NYE70Srp6elqxgkE54PwQx4Mr6exJPnseM9U4K+hULllf5yjM9edreJE1nV NVgFjeyafQhrHKwgr7PERJ/ikwKBgQDqqsT1M+EJLmI1HtCspOG6cu7q3gf/wKRh E8tu84i3YyBnI8uJkKy92RNVI5fvpBARe3tjSdM25rr2rcrcmF/5g6Q9ImxZPGCt 86eOgO9ErNtbc4TEgybsP319UE4O41aKeNiBTAZKoYCxv/dMqG0j4avmWzd+foHq gSNUvR2maQKBgQCYeqOsV2B6VPY7KIVFLd0AA9/dwvEmgAYLiA/RShDI+hwQ/5jX uxDu37KAhqeC65sHLrmIMUt4Zdr+DRyZK3aIDNEAesPMjw/X6lCXYp1ZISD2yyym MFGH8X8CIkstI9Faf9vf6PJKSFrC1/HA7wq17VCwrUzLvrljTMW8meM/CwKBgCpo 2leGHLFQFKeM/iF1WuYbR1pi7gcmhY6VyTowARFDdOOu8GXYI5/bz0afvCGvAMho DJCREv7lC/zww6zCTPYG+HOj+PjXlJFba3ixjIxYwPvyEJiDK1Ge18sB7Fl8dHNq C5ayaqCqN1voWYUdGzxU2IA1E/5kVo5O8FesJeOhAoGBAImJbZFf+D5kA32Xxhac 59lLWBCsocvvbd1cvDMNlRywAAyhsCb1SuX4nEAK9mrSBdfmoF2Nm3eilfsOds0f K5mX069IKG82CMqh3Mzptd7e7lyb9lsoGO0BAtjho3cWtha/UZ70vfaMzGuZ6JmQ ak6k+8+UFd93M4z0Qo74OhXB -----END PRIVATE KEY-----` const privateKey = await jose.importPKCS8(pkcs8, alg) const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) .setProtectedHeader({ alg }) .setIssuedAt() .setIssuer('urn:example:issuer') .setAudience('urn:example:audience') .setExpirationTime('2h') .sign(privateKey) console.log(jwt) ``` Usage with a private JWK encoded RSA key ```js const alg = 'RS256' const jwk = { kty: 'RSA', n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw', e: 'AQAB', d: 'VuVE_KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34lsVOPK0XDegZkhAybMZHjRhp-gwVxX5ChC-J3cUpOBH5FNxElgW6HizD2Jcq6t6LoLYgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG_qJV1K00_Ly1G1QKoBffEs-v4fAMJrCbUdCz1qWto-PU-HLMEo-krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq_aAP4a1SXmo-j0cvRU4W5Fj0RVwNesIpetX2ZFz4p_JmB5sWFEj_fC7h5z2lq-6Bme2T3BHtXkIxoBW0_pYVnASC8P2puO5FnVxDmWuHDYQ', p: '07rgXd_tLUhVRF_g1OaqRZh5uZ8hiLWUSU0vu9coOaQcatSqjQlIwLW8UdKv_38GrmpIfgcEVQjzq6rFBowUm9zWBO9Eq6enpasYJBOeD8EMeDK-nsST57HjPVOCvoVC5ZX-cozPXna3iRNZ1TVYBY3smn0IaxysIK-zxESf4pM', q: '6qrE9TPhCS5iNR7QrKThunLu6t4H_8CkYRPLbvOIt2MgZyPLiZCsvdkTVSOX76QQEXt7Y0nTNua69q3K3Jhf-YOkPSJsWTxgrfOnjoDvRKzbW3OExIMm7D99fVBODuNWinjYgUwGSqGAsb_3TKhtI-Gr5ls3fn6B6oEjVL0dpmk', dp: 'mHqjrFdgelT2OyiFRS3dAAPf3cLxJoAGC4gP0UoQyPocEP-Y17sQ7t-ygIanguubBy65iDFLeGXa_g0cmSt2iAzRAHrDzI8P1-pQl2KdWSEg9ssspjBRh_F_AiJLLSPRWn_b3-jySkhawtfxwO8Kte1QsK1My765Y0zFvJnjPws', dq: 'KmjaV4YcsVAUp4z-IXVa5htHWmLuByaFjpXJOjABEUN0467wZdgjn9vPRp-8Ia8AyGgMkJES_uUL_PDDrMJM9gb4c6P4-NeUkVtreLGMjFjA-_IQmIMrUZ7XywHsWXx0c2oLlrJqoKo3W-hZhR0bPFTYgDUT_mRWjk7wV6wl46E', qi: 'iYltkV_4PmQDfZfGFpzn2UtYEKyhy-9t3Vy8Mw2VHLAADKGwJvVK5ficQAr2atIF1-agXY2bd6KV-w52zR8rmZfTr0gobzYIyqHczOm13t7uXJv2WygY7QEC2OGjdxa2Fr9RnvS99ozMa5nomZBqTqT7z5QV33czjPRCjvg6FcE', } const privateKey = await jose.importJWK(jwk, alg) const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) .setProtectedHeader({ alg }) .setIssuedAt() .setIssuer('urn:example:issuer') .setAudience('urn:example:audience') .setExpirationTime('2h') .sign(privateKey) console.log(jwt) ``` ## Constructors ### Constructor ▸ **new SignJWT**(`payload?`): `SignJWT` SignJWT constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | The JWT Claims Set object. Defaults to an empty object. | #### Returns `SignJWT` ## Methods ### setAudience() ▸ **setAudience**(`audience`): `this` Set the "aud" (Audience) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `audience` | `string` \| `string`[] | "aud" (Audience) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setExpirationTime() ▸ **setExpirationTime**(`input`): `this` Set the "exp" (Expiration Time) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "exp" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuedAt() ▸ **setIssuedAt**(`input?`): `this` Set the "iat" (Issued At) Claim. - If no argument is used the current unix timestamp is used as the claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input?` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "iat" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuer() ▸ **setIssuer**(`issuer`): `this` Set the "iss" (Issuer) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `issuer` | `string` | "Issuer" Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setJti() ▸ **setJti**(`jwtId`): `this` Set the "jti" (JWT ID) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwtId` | `string` | "jti" (JWT ID) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setNotBefore() ▸ **setNotBefore**(`input`): `this` Set the "nbf" (Not Before) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "nbf" (Not Before) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setProtectedHeader() ▸ **setProtectedHeader**(`protectedHeader`): `this` Sets the JWS Protected Header on the SignJWT object. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWTHeaderParameters`](../../../types/interfaces/JWTHeaderParameters.md) | JWS Protected Header. Must contain an "alg" (JWS Algorithm) property. | #### Returns `this` *** ### setSubject() ▸ **setSubject**(`subject`): `this` Set the "sub" (Subject) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `subject` | `string` | "sub" (Subject) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### sign() ▸ **sign**(`key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Signs and returns the JWT. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Private Key or Secret to sign the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`SignOptions`](../../../types/interfaces/SignOptions.md) | JWT Sign options. | #### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ================================================ FILE: docs/jwt/unsecured/README.md ================================================ # jwt/unsecured Unsecured (unsigned & unencrypted) JSON Web Tokens (JWT) ## Classes - [UnsecuredJWT](classes/UnsecuredJWT.md) ## Interfaces - [UnsecuredResult](interfaces/UnsecuredResult.md) ================================================ FILE: docs/jwt/unsecured/classes/UnsecuredJWT.md ================================================ # Class: UnsecuredJWT ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. This class is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/unsecured'`. ## Examples Encoding ```js const unsecuredJwt = new jose.UnsecuredJWT({ 'urn:example:claim': true }) .setIssuedAt() .setIssuer('urn:example:issuer') .setAudience('urn:example:audience') .setExpirationTime('2h') .encode() console.log(unsecuredJwt) ``` Decoding ```js const payload = jose.UnsecuredJWT.decode(unsecuredJwt, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(payload) ``` ## Constructors ### Constructor ▸ **new UnsecuredJWT**(`payload?`): `UnsecuredJWT` UnsecuredJWT constructor #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `payload` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | The JWT Claims Set object. Defaults to an empty object. | #### Returns `UnsecuredJWT` ## Methods ### decode() ▸ `static` **decode**\<`PayloadType`\>(`jwt`, `options?`): [`UnsecuredResult`](../interfaces/UnsecuredResult.md)\<`PayloadType`\> Decodes an unsecured JWT. #### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` | Unsecured JWT to decode the payload of. | | `options?` | [`JWTClaimVerificationOptions`](../../../types/interfaces/JWTClaimVerificationOptions.md) | JWT Claims Set validation options. | #### Returns [`UnsecuredResult`](../interfaces/UnsecuredResult.md)\<`PayloadType`\> *** ### encode() ▸ **encode**(): `string` Encodes the Unsecured JWT. #### Returns `string` *** ### setAudience() ▸ **setAudience**(`audience`): `this` Set the "aud" (Audience) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `audience` | `string` \| `string`[] | "aud" (Audience) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setExpirationTime() ▸ **setExpirationTime**(`input`): `this` Set the "exp" (Expiration Time) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "exp" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuedAt() ▸ **setIssuedAt**(`input?`): `this` Set the "iat" (Issued At) Claim. - If no argument is used the current unix timestamp is used as the claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input?` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "iat" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuer() ▸ **setIssuer**(`issuer`): `this` Set the "iss" (Issuer) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `issuer` | `string` | "Issuer" Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setJti() ▸ **setJti**(`jwtId`): `this` Set the "jti" (JWT ID) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwtId` | `string` | "jti" (JWT ID) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setNotBefore() ▸ **setNotBefore**(`input`): `this` Set the "nbf" (Not Before) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "nbf" (Not Before) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setSubject() ▸ **setSubject**(`subject`): `this` Set the "sub" (Subject) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `subject` | `string` | "sub" (Subject) Claim value to set on the JWT Claims Set. | #### Returns `this` ================================================ FILE: docs/jwt/unsecured/interfaces/UnsecuredResult.md ================================================ # Interface: UnsecuredResult\ ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Result of decoding an Unsecured JWT. ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ## Properties ### header • **header**: [`JWSHeaderParameters`](../../../types/interfaces/JWSHeaderParameters.md) *** ### payload • **payload**: `PayloadType` & [`JWTPayload`](../../../types/interfaces/JWTPayload.md) ================================================ FILE: docs/jwt/verify/README.md ================================================ # jwt/verify JSON Web Token (JWT) Verification (JWT is in JWS format) ## Interfaces - [JWTVerifyGetKey](interfaces/JWTVerifyGetKey.md) - [JWTVerifyOptions](interfaces/JWTVerifyOptions.md) ## Functions - [jwtVerify](functions/jwtVerify.md) ================================================ FILE: docs/jwt/verify/functions/jwtVerify.md ================================================ # Function: jwtVerify() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ## Call Signature ▸ **jwtVerify**\<`PayloadType`\>(`jwt`, `key`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTVerifyResult`](../../../types/interfaces/JWTVerifyResult.md)\<`PayloadType`\>\> Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the JWT Claims Set. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/verify'`. ### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JSON Web Token value (encoded as JWS). | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`JWTVerifyOptions`](../interfaces/JWTVerifyOptions.md) | JWT Decryption and JWT Claims Set validation options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTVerifyResult`](../../../types/interfaces/JWTVerifyResult.md)\<`PayloadType`\>\> ### Examples Usage with a symmetric secret ```js const secret = new TextEncoder().encode( 'cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2', ) const jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2MjMxLCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.C4iSlLfAUMBq--wnC6VqD9gEOhwpRZpoRarE0m7KEnI' const { payload, protectedHeader } = await jose.jwtVerify(jwt, secret, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` Usage with a public SPKI encoded RSA key ```js const alg = 'RS256' const spki = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhYOFK2Ocbbpb/zVypi9 SeKiNUqKQH0zTKN1+6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4H HHsrYCf2+FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS/Yv4hRvWfQPcc2Gc3+/fQ OOW57zVy+rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj +KgDPjymkMGoJlO3aKppsjfbt/AH6GGdRghYRLOUwQU+h+ofWHR3lbYiKtXPn5dN 24kiHy61e3VAQ9/YAZlwXC/99GGtw/NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZA SwIDAQAB -----END PUBLIC KEY-----` const publicKey = await jose.importSPKI(spki, alg) const jwt = 'eyJhbGciOiJSUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2NDg4LCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.gXrPZ3yM_60dMXGE69dusbpzYASNA-XIOwsb5D5xYnSxyj6_D6OR_uR_1vqhUm4AxZxcrH1_-XJAve9HCw8az_QzHcN-nETt-v6stCsYrn6Bv1YOc-mSJRZ8ll57KVqLbCIbjKwerNX5r2_Qg2TwmJzQdRs-AQDhy-s_DlJd8ql6wR4n-kDZpar-pwIvz4fFIN0Fj57SXpAbLrV6Eo4Byzl0xFD8qEYEpBwjrMMfxCZXTlAVhAq6KCoGlDTwWuExps342-0UErEtyIqDnDGcrfNWiUsoo8j-29IpKd-w9-C388u-ChCxoHz--H8WmMSZzx3zTXsZ5lXLZ9IKfanDKg' const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` Usage with a public JWK encoded RSA key ```js const alg = 'RS256' const jwk = { kty: 'RSA', n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw', e: 'AQAB', } const publicKey = await jose.importJWK(jwk, alg) const jwt = 'eyJhbGciOiJSUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2NDg4LCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.gXrPZ3yM_60dMXGE69dusbpzYASNA-XIOwsb5D5xYnSxyj6_D6OR_uR_1vqhUm4AxZxcrH1_-XJAve9HCw8az_QzHcN-nETt-v6stCsYrn6Bv1YOc-mSJRZ8ll57KVqLbCIbjKwerNX5r2_Qg2TwmJzQdRs-AQDhy-s_DlJd8ql6wR4n-kDZpar-pwIvz4fFIN0Fj57SXpAbLrV6Eo4Byzl0xFD8qEYEpBwjrMMfxCZXTlAVhAq6KCoGlDTwWuExps342-0UErEtyIqDnDGcrfNWiUsoo8j-29IpKd-w9-C388u-ChCxoHz--H8WmMSZzx3zTXsZ5lXLZ9IKfanDKg' const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` ## Call Signature ▸ **jwtVerify**\<`PayloadType`\>(`jwt`, `getKey`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTVerifyResult`](../../../types/interfaces/JWTVerifyResult.md)\<`PayloadType`\> & [`ResolvedKey`](../../../types/interfaces/ResolvedKey.md)\> ### Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | JSON Web Token value (encoded as JWS). | | `getKey` | [`JWTVerifyGetKey`](../interfaces/JWTVerifyGetKey.md) | Function resolving a key to verify the JWT with. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg). | | `options?` | [`JWTVerifyOptions`](../interfaces/JWTVerifyOptions.md) | JWT Decryption and JWT Claims Set validation options. | ### Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWTVerifyResult`](../../../types/interfaces/JWTVerifyResult.md)\<`PayloadType`\> & [`ResolvedKey`](../../../types/interfaces/ResolvedKey.md)\> ### Example Usage with a public JSON Web Key Set hosted on a remote URL ```js const JWKS = jose.createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')) const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { issuer: 'urn:example:issuer', audience: 'urn:example:audience', }) console.log(protectedHeader) console.log(payload) ``` ================================================ FILE: docs/jwt/verify/interfaces/JWTVerifyGetKey.md ================================================ # Interface: JWTVerifyGetKey() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Interface for JWT Verification dynamic key resolution. No token components have been verified at the time of this function call. ## See [createRemoteJWKSet](../../../jwks/remote/functions/createRemoteJWKSet.md) to verify using a remote JSON Web Key Set. ▸ **JWTVerifyGetKey**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | [`JWTHeaderParameters`](../../../types/interfaces/JWTHeaderParameters.md) | JWE or JWS Protected Header. | | `token` | [`FlattenedJWSInput`](../../../types/interfaces/FlattenedJWSInput.md) | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](../../../types/interfaces/JWK.md) \| [`KeyObject`](../../../types/interfaces/KeyObject.md)\> ================================================ FILE: docs/jwt/verify/interfaces/JWTVerifyOptions.md ================================================ # Interface: JWTVerifyOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Combination of JWS Verification options and JWT Claims Set verification options. ## Properties ### algorithms? • `optional` **algorithms?**: `string`[] A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg" (Algorithm) values applicable for the used key/secret are allowed. > [!NOTE]\ > Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API. *** ### audience? • `optional` **audience?**: `string` \| `string`[] Expected JWT "aud" (Audience) Claim value(s). This option makes the JWT "aud" (Audience) Claim presence required. *** ### clockTolerance? • `optional` **clockTolerance?**: `string` \| `number` Clock skew tolerance - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). Used when validating the JWT "nbf" (Not Before) and "exp" (Expiration Time) claims, and when validating the "iat" (Issued At) claim if the [`maxTokenAge` option](../../../types/interfaces/JWTClaimVerificationOptions.md#maxtokenage) is set. *** ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` *** ### currentDate? • `optional` **currentDate?**: [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) Date to use when comparing NumericDate claims, defaults to `new Date()`. *** ### issuer? • `optional` **issuer?**: `string` \| `string`[] Expected JWT "iss" (Issuer) Claim value(s). This option makes the JWT "iss" (Issuer) Claim presence required. *** ### maxTokenAge? • `optional` **maxTokenAge?**: `string` \| `number` Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). This option makes the JWT "iat" (Issued At) Claim presence required. *** ### requiredClaims? • `optional` **requiredClaims?**: `string`[] Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if the [`issuer` option](../../../types/interfaces/JWTClaimVerificationOptions.md#issuer) is set, then JWT "iss" (Issuer) Claim must be present; if the [`audience` option](../../../types/interfaces/JWTClaimVerificationOptions.md#audience) is set, then JWT "aud" (Audience) Claim must be present; if the [`subject` option](../../../types/interfaces/JWTClaimVerificationOptions.md#subject) is set, then JWT "sub" (Subject) Claim must be present; if the [`maxTokenAge` option](../../../types/interfaces/JWTClaimVerificationOptions.md#maxtokenage) is set, then JWT "iat" (Issued At) Claim must be present. *** ### subject? • `optional` **subject?**: `string` Expected JWT "sub" (Subject) Claim value. This option makes the JWT "sub" (Subject) Claim presence required. *** ### typ? • `optional` **typ?**: `string` Expected JWT "typ" (Type) Header Parameter value. This option makes the JWT "typ" (Type) Header Parameter presence required. ================================================ FILE: docs/key/export/README.md ================================================ # key/export Cryptographic key export functions ## Functions - [exportJWK](functions/exportJWK.md) - [exportPKCS8](functions/exportPKCS8.md) - [exportSPKI](functions/exportSPKI.md) ================================================ FILE: docs/key/export/functions/exportJWK.md ================================================ # Function: exportJWK() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **exportJWK**(`key`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWK`](../../../types/interfaces/JWK.md)\> Exports a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey), [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject), or [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) to a JWK. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/export'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to export as JWK. | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`JWK`](../../../types/interfaces/JWK.md)\> ## Example ```js const privateJwk = await jose.exportJWK(privateKey) const publicJwk = await jose.exportJWK(publicKey) console.log(privateJwk) console.log(publicJwk) ``` ================================================ FILE: docs/key/export/functions/exportPKCS8.md ================================================ # Function: exportPKCS8() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **exportPKCS8**(`key`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Exports a private [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) or [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) to a PEM-encoded PKCS8 string format. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/export'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to export to a PEM-encoded PKCS8 string format. | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ## Example ```js const pkcs8Pem = await jose.exportPKCS8(privateKey) console.log(pkcs8Pem) ``` ================================================ FILE: docs/key/export/functions/exportSPKI.md ================================================ # Function: exportSPKI() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **exportSPKI**(`key`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> Exports a public [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) or [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) to a PEM-encoded SPKI string format. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/export'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `key` | [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`KeyObject`](../../../types/interfaces/KeyObject.md) | Key to export to a PEM-encoded SPKI string format. | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<`string`\> ## Example ```js const spkiPem = await jose.exportSPKI(publicKey) console.log(spkiPem) ``` ================================================ FILE: docs/key/generate_key_pair/README.md ================================================ # key/generate\_key\_pair Asymmetric key generation ## Interfaces - [GenerateKeyPairOptions](interfaces/GenerateKeyPairOptions.md) - [GenerateKeyPairResult](interfaces/GenerateKeyPairResult.md) ## Functions - [generateKeyPair](functions/generateKeyPair.md) ================================================ FILE: docs/key/generate_key_pair/functions/generateKeyPair.md ================================================ # Function: generateKeyPair() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **generateKeyPair**(`alg`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GenerateKeyPairResult`](../interfaces/GenerateKeyPairResult.md)\> Generates a private and a public key for a given JWA algorithm identifier. This can only generate asymmetric key pairs. For symmetric secrets use the `generateSecret` function. > [!NOTE]\ > The `privateKey` is generated with `extractable` set to `false` by default. See > [GenerateKeyPairOptions.extractable](../interfaces/GenerateKeyPairOptions.md#extractable) to generate an extractable `privateKey`. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/generate/keypair'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `alg` | `string` | JWA Algorithm Identifier to be used with the generated key pair. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`GenerateKeyPairOptions`](../interfaces/GenerateKeyPairOptions.md) | Additional options passed down to the key pair generation. | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`GenerateKeyPairResult`](../interfaces/GenerateKeyPairResult.md)\> ## Example ```js const { publicKey, privateKey } = await jose.generateKeyPair('PS256') console.log(publicKey) console.log(privateKey) ``` ================================================ FILE: docs/key/generate_key_pair/interfaces/GenerateKeyPairOptions.md ================================================ # Interface: GenerateKeyPairOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Asymmetric key pair generation function options. ## Properties ### crv? • `optional` **crv?**: `string` The EC "crv" (Curve) or OKP "crv" (Subtype of Key Pair) value to generate. The curve must be both supported on the runtime as well as applicable for the given JWA algorithm identifier. *** ### extractable? • `optional` **extractable?**: `boolean` The value to use as [SubtleCrypto.generateKey](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) `extractable` argument. Default is false. #### Example ```js const { publicKey, privateKey } = await jose.generateKeyPair('PS256', { extractable: true, }) console.log(await jose.exportJWK(privateKey)) console.log(await jose.exportPKCS8(privateKey)) ``` *** ### modulusLength? • `optional` **modulusLength?**: `number` A hint for RSA algorithms to generate an RSA key of a given `modulusLength` (Key size in bits). JOSE requires 2048 bits or larger. Default is 2048. ================================================ FILE: docs/key/generate_key_pair/interfaces/GenerateKeyPairResult.md ================================================ # Interface: GenerateKeyPairResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Asymmetric key pair generation function result. ## Properties ### privateKey • **privateKey**: [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) The generated Private Key. *** ### publicKey • **publicKey**: [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) Public Key corresponding to the generated Private Key. ================================================ FILE: docs/key/generate_secret/README.md ================================================ # key/generate\_secret Symmetric key generation ## Interfaces - [GenerateSecretOptions](interfaces/GenerateSecretOptions.md) ## Functions - [generateSecret](functions/generateSecret.md) ================================================ FILE: docs/key/generate_secret/functions/generateSecret.md ================================================ # Function: generateSecret() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **generateSecret**(`alg`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Generates a symmetric secret key for a given JWA algorithm identifier. > [!NOTE]\ > The secret key is generated with `extractable` set to `false` by default. > [!NOTE]\ > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as > [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) this method yields a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) for them instead. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/generate/secret'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `alg` | `string` | JWA Algorithm Identifier to be used with the generated secret. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`GenerateSecretOptions`](../interfaces/GenerateSecretOptions.md) | Additional options passed down to the secret generation. | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const secret = await jose.generateSecret('HS256') console.log(secret) ``` ================================================ FILE: docs/key/generate_secret/interfaces/GenerateSecretOptions.md ================================================ # Interface: GenerateSecretOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Secret generation function options. ## Properties ### extractable? • `optional` **extractable?**: `boolean` The value to use as [SubtleCrypto.generateKey](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey) `extractable` argument. Default is false. > [!NOTE]\ > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as > [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) this option has no effect for them. ================================================ FILE: docs/key/import/README.md ================================================ # key/import Cryptographic key import functions ## Interfaces - [KeyImportOptions](interfaces/KeyImportOptions.md) ## Functions - [importJWK](functions/importJWK.md) - [importPKCS8](functions/importPKCS8.md) - [importSPKI](functions/importSPKI.md) - [importX509](functions/importX509.md) ================================================ FILE: docs/key/import/functions/importJWK.md ================================================ # Function: importJWK() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **importJWK**(`jwk`, `alg?`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Imports a JWK to a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey). Either the JWK "alg" (Algorithm) Parameter, or the optional "alg" argument, must be present for asymmetric JSON Web Key imports. > [!NOTE]\ > The JSON Web Key parameters "use", "key_ops", and "ext" are also used in the [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) > import process. > [!NOTE]\ > Symmetric JSON Web Keys (i.e. `kty: "oct"`) yield back an [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) instead of a > [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey). This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/import'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwk` | [`JWK`](../../../types/interfaces/JWK.md) | JSON Web Key. | | `alg?` | `string` | JSON Web Algorithm identifier to be used with the imported key. Default is the "alg" property on the JWK. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`KeyImportOptions`](../interfaces/KeyImportOptions.md) | - | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const ecPublicKey = await jose.importJWK( { crv: 'P-256', kty: 'EC', x: 'ySK38C1jBdLwDsNWKzzBHqKYEE5Cgv-qjWvorUXk9fw', y: '_LeQBw07cf5t57Iavn4j-BqJsAD1dpoz8gokd3sBsOo', }, 'ES256', ) const rsaPublicKey = await jose.importJWK( { kty: 'RSA', e: 'AQAB', n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ', }, 'PS256', ) ``` ================================================ FILE: docs/key/import/functions/importPKCS8.md ================================================ # Function: importPKCS8() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **importPKCS8**(`pkcs8`, `alg`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Imports a PEM-encoded PKCS#8 string as a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey). > [!NOTE]\ > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in > [Web Cryptography API](https://w3c.github.io/webcrypto/), use the OID rsaEncryption > (1.2.840.113549.1.1.1) instead for all RSA algorithms. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/import'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `pkcs8` | `string` | PEM-encoded PKCS#8 string | | `alg` | `string` | JSON Web Algorithm identifier to be used with the imported key. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`KeyImportOptions`](../interfaces/KeyImportOptions.md) | - | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const algorithm = 'ES256' const pkcs8 = `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiyvo0X+VQ0yIrOaN nlrnUclopnvuuMfoc8HHly3505OhRANCAAQWUcdZ8uTSAsFuwtNy4KtsKqgeqYxg l6kwL5D4N3pEGYGIDjV69Sw0zAt43480WqJv7HCL0mQnyqFmSrxj8jMa -----END PRIVATE KEY-----` const ecPrivateKey = await jose.importPKCS8(pkcs8, algorithm) ``` ================================================ FILE: docs/key/import/functions/importSPKI.md ================================================ # Function: importSPKI() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **importSPKI**(`spki`, `alg`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Imports a PEM-encoded SPKI string as a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey). > [!NOTE]\ > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in > [Web Cryptography API](https://w3c.github.io/webcrypto/), use the OID rsaEncryption > (1.2.840.113549.1.1.1) instead for all RSA algorithms. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/import'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `spki` | `string` | PEM-encoded SPKI string | | `alg` | `string` | JSON Web Algorithm identifier to be used with the imported key. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`KeyImportOptions`](../interfaces/KeyImportOptions.md) | - | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const algorithm = 'ES256' const spki = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFlHHWfLk0gLBbsLTcuCrbCqoHqmM YJepMC+Q+Dd6RBmBiA41evUsNMwLeN+PNFqib+xwi9JkJ8qhZkq8Y/IzGg== -----END PUBLIC KEY-----` const ecPublicKey = await jose.importSPKI(spki, algorithm) ``` ================================================ FILE: docs/key/import/functions/importX509.md ================================================ # Function: importX509() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **importX509**(`x509`, `alg`, `options?`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> Imports the SPKI from an X.509 string certificate as a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey). > [!NOTE]\ > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in > [Web Cryptography API](https://w3c.github.io/webcrypto/), use the OID rsaEncryption > (1.2.840.113549.1.1.1) instead for all RSA algorithms. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/key/import'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `x509` | `string` | X.509 certificate string | | `alg` | `string` | JSON Web Algorithm identifier to be used with the imported key. See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210). | | `options?` | [`KeyImportOptions`](../interfaces/KeyImportOptions.md) | - | ## Returns [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey)\> ## Example ```js const algorithm = 'ES256' const x509 = `-----BEGIN CERTIFICATE----- MIIBXjCCAQSgAwIBAgIGAXvykuMKMAoGCCqGSM49BAMCMDYxNDAyBgNVBAMMK3Np QXBNOXpBdk1VaXhXVWVGaGtjZXg1NjJRRzFyQUhXaV96UlFQTVpQaG8wHhcNMjEw OTE3MDcwNTE3WhcNMjIwNzE0MDcwNTE3WjA2MTQwMgYDVQQDDCtzaUFwTTl6QXZN VWl4V1VlRmhrY2V4NTYyUUcxckFIV2lfelJRUE1aUGhvMFkwEwYHKoZIzj0CAQYI KoZIzj0DAQcDQgAE8PbPvCv5D5xBFHEZlBp/q5OEUymq7RIgWIi7tkl9aGSpYE35 UH+kBKDnphJO3odpPZ5gvgKs2nwRWcrDnUjYLDAKBggqhkjOPQQDAgNIADBFAiEA 1yyMTRe66MhEXID9+uVub7woMkNYd0LhSHwKSPMUUTkCIFQGsfm1ecXOpeGOufAh v+A1QWZMuTWqYt+uh/YSRNDn -----END CERTIFICATE-----` const ecPublicKey = await jose.importX509(x509, algorithm) ``` ================================================ FILE: docs/key/import/interfaces/KeyImportOptions.md ================================================ # Interface: KeyImportOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Key Import Function options. ## Properties ### extractable? • `optional` **extractable?**: `boolean` The value to use as [SubtleCrypto.importKey](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) `extractable` argument. Default is false for private keys, true otherwise. ================================================ FILE: docs/types/README.md ================================================ # types ## Interfaces - [CompactDecryptResult](interfaces/CompactDecryptResult.md) - [CompactJWEHeaderParameters](interfaces/CompactJWEHeaderParameters.md) - [CompactJWSHeaderParameters](interfaces/CompactJWSHeaderParameters.md) - [CompactVerifyResult](interfaces/CompactVerifyResult.md) - [CritOption](interfaces/CritOption.md) - [DecryptOptions](interfaces/DecryptOptions.md) - [EncryptOptions](interfaces/EncryptOptions.md) - [FlattenedDecryptResult](interfaces/FlattenedDecryptResult.md) - [FlattenedJWE](interfaces/FlattenedJWE.md) - [FlattenedJWS](interfaces/FlattenedJWS.md) - [FlattenedJWSInput](interfaces/FlattenedJWSInput.md) - [FlattenedVerifyResult](interfaces/FlattenedVerifyResult.md) - [GeneralDecryptResult](interfaces/GeneralDecryptResult.md) - [GeneralJWE](interfaces/GeneralJWE.md) - [GeneralJWS](interfaces/GeneralJWS.md) - [GeneralJWSInput](interfaces/GeneralJWSInput.md) - [GeneralVerifyResult](interfaces/GeneralVerifyResult.md) - [GetKeyFunction](interfaces/GetKeyFunction.md) - [JoseHeaderParameters](interfaces/JoseHeaderParameters.md) - [JSONWebKeySet](interfaces/JSONWebKeySet.md) - [JWEHeaderParameters](interfaces/JWEHeaderParameters.md) - [JWEKeyManagementHeaderParameters](interfaces/JWEKeyManagementHeaderParameters.md) - [JWK](interfaces/JWK.md) - [JWK\_AKP\_Private](interfaces/JWK_AKP_Private.md) - [JWK\_AKP\_Public](interfaces/JWK_AKP_Public.md) - [JWK\_EC\_Private](interfaces/JWK_EC_Private.md) - [JWK\_EC\_Public](interfaces/JWK_EC_Public.md) - [JWK\_oct](interfaces/JWK_oct.md) - [JWK\_OKP\_Private](interfaces/JWK_OKP_Private.md) - [JWK\_OKP\_Public](interfaces/JWK_OKP_Public.md) - [JWK\_RSA\_Private](interfaces/JWK_RSA_Private.md) - [JWK\_RSA\_Public](interfaces/JWK_RSA_Public.md) - [JWKParameters](interfaces/JWKParameters.md) - [JWSHeaderParameters](interfaces/JWSHeaderParameters.md) - [JWTClaimVerificationOptions](interfaces/JWTClaimVerificationOptions.md) - [JWTDecryptResult](interfaces/JWTDecryptResult.md) - [JWTHeaderParameters](interfaces/JWTHeaderParameters.md) - [JWTPayload](interfaces/JWTPayload.md) - [JWTVerifyResult](interfaces/JWTVerifyResult.md) - [KeyObject](interfaces/KeyObject.md) - [ProduceJWT](interfaces/ProduceJWT.md) - [ResolvedKey](interfaces/ResolvedKey.md) - [SignOptions](interfaces/SignOptions.md) - [VerifyOptions](interfaces/VerifyOptions.md) ## Type Aliases - [CryptoKey](type-aliases/CryptoKey.md) ================================================ FILE: docs/types/interfaces/CompactDecryptResult.md ================================================ # Interface: CompactDecryptResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Compact JWE decryption result ## Properties ### plaintext • **plaintext**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) Plaintext. *** ### protectedHeader • **protectedHeader**: [`CompactJWEHeaderParameters`](CompactJWEHeaderParameters.md) JWE Protected Header. ================================================ FILE: docs/types/interfaces/CompactJWEHeaderParameters.md ================================================ # Interface: CompactJWEHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized Compact JWE Header Parameters, any other Header Members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWE Header member. ## Properties ### alg • **alg**: `string` JWE "alg" (Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg) *** ### enc • **enc**: `string` JWE "enc" (Encryption Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg) *** ### crit? • `optional` **crit?**: `string`[] JWE "crit" (Critical) Header Parameter *** ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter *** ### zip? • `optional` **zip?**: `string` JWE "zip" (Compression Algorithm) Header Parameter. The only supported value is `"DEF"` (DEFLATE). Requires the `CompressionStream` / `DecompressionStream` APIs to be available in the runtime. #### See [JWE "zip" Header Parameter](https://www.rfc-editor.org/rfc/rfc7516#section-4.1.3) ================================================ FILE: docs/types/interfaces/CompactJWSHeaderParameters.md ================================================ # Interface: CompactJWSHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized Compact JWS Header Parameters, any other Header Members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWS Header member. ## Properties ### alg • **alg**: `string` JWS "alg" (Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg) *** ### b64? • `optional` **b64?**: `boolean` This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing Input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797). *** ### crit? • `optional` **crit?**: `string`[] JWS "crit" (Critical) Header Parameter *** ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter ================================================ FILE: docs/types/interfaces/CompactVerifyResult.md ================================================ # Interface: CompactVerifyResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Compact JWS verification result ## Properties ### payload • **payload**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) JWS Payload. *** ### protectedHeader • **protectedHeader**: [`CompactJWSHeaderParameters`](CompactJWSHeaderParameters.md) JWS Protected Header. ================================================ FILE: docs/types/interfaces/CritOption.md ================================================ # Interface: CritOption ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. ## Properties ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` ================================================ FILE: docs/types/interfaces/DecryptOptions.md ================================================ # Interface: DecryptOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JWE Decryption options. ## Properties ### contentEncryptionAlgorithms? • `optional` **contentEncryptionAlgorithms?**: `string`[] A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed. *** ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` *** ### keyManagementAlgorithms? • `optional` **keyManagementAlgorithms?**: `string`[] A list of accepted JWE "alg" (Algorithm) Header Parameter values. By default all "alg" (Algorithm) Header Parameter values applicable for the used key/secret are allowed except for all PBES2 Key Management Algorithms, these need to be explicitly allowed using this option. *** ### maxDecompressedLength? • `optional` **maxDecompressedLength?**: `number` Maximum allowed size (in bytes) of the decompressed plaintext when the JWE `"zip"` (Compression Algorithm) Header Parameter is present. By default this value is set to 250000 (250 KB). The value must be `0`, a positive safe integer, or `Infinity`. Set to `0` to reject all compressed JWEs during decryption. Set to `Infinity` to disable the decompressed size limit. *** ### maxPBES2Count? • `optional` **maxPBES2Count?**: `number` (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter value. The PBKDF2 iteration count defines the algorithm's computational expense. By default this value is set to 10000. ================================================ FILE: docs/types/interfaces/EncryptOptions.md ================================================ # Interface: EncryptOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JWE Encryption options. ## Properties ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` ================================================ FILE: docs/types/interfaces/FlattenedDecryptResult.md ================================================ # Interface: FlattenedDecryptResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Flattened JWE JSON Serialization Syntax decryption result ## Properties ### plaintext • **plaintext**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) Plaintext. *** ### additionalAuthenticatedData? • `optional` **additionalAuthenticatedData?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) JWE AAD. *** ### protectedHeader? • `optional` **protectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Protected Header. *** ### sharedUnprotectedHeader? • `optional` **sharedUnprotectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Shared Unprotected Header. *** ### unprotectedHeader? • `optional` **unprotectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Per-Recipient Unprotected Header. ================================================ FILE: docs/types/interfaces/FlattenedJWE.md ================================================ # Interface: FlattenedJWE ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Flattened JWE JSON Serialization Syntax token. ## Properties ### ciphertext • **ciphertext**: `string` The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). *** ### aad? • `optional` **aad?**: `string` The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a base64url-encoded value to be integrity protected but not encrypted. *** ### encrypted\_key? • `optional` **encrypted\_key?**: `string` The "encrypted_key" member MUST be present and contain the value BASE64URL(JWE Encrypted Key) when the JWE Encrypted Key value is non-empty; otherwise, it MUST be absent. *** ### header? • `optional` **header?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) The "header" member MUST be present and contain the value JWE Per- Recipient Unprotected Header when the JWE Per-Recipient Unprotected Header value is non-empty; otherwise, it MUST be absent. This value is represented as an unencoded JSON object, rather than as a string. These Header Parameter values are not integrity protected. *** ### iv? • `optional` **iv?**: `string` The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent. *** ### protected? • `optional` **protected?**: `string` The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These Header Parameter values are integrity protected. *** ### tag? • `optional` **tag?**: `string` The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent. *** ### unprotected? • `optional` **unprotected?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This value is represented as an unencoded JSON object, rather than as a string. These Header Parameter values are not integrity protected. ================================================ FILE: docs/types/interfaces/FlattenedJWS.md ================================================ # Interface: FlattenedJWS ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Flattened JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS Unencoded Payload ([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) is used. ## Properties ### payload • **payload**: `string` The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 "b64": false is used the value passed may also be a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). *** ### signature • **signature**: `string` The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). *** ### header? • `optional` **header?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented as an unencoded JSON object, rather than as a string. These Header Parameter values are not integrity protected. *** ### protected? • `optional` **protected?**: `string` The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These Header Parameter values are integrity protected. ================================================ FILE: docs/types/interfaces/FlattenedJWSInput.md ================================================ # Interface: FlattenedJWSInput ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Flattened JWS definition for verify function inputs, allows payload as [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) for detached signature validation. ## Properties ### payload • **payload**: `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 "b64": false is used the value passed may also be a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). *** ### signature • **signature**: `string` The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). *** ### header? • `optional` **header?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented as an unencoded JSON object, rather than as a string. These Header Parameter values are not integrity protected. *** ### protected? • `optional` **protected?**: `string` The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These Header Parameter values are integrity protected. ================================================ FILE: docs/types/interfaces/FlattenedVerifyResult.md ================================================ # Interface: FlattenedVerifyResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Flattened JWS JSON Serialization Syntax verification result ## Properties ### payload • **payload**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) JWS Payload. *** ### protectedHeader? • `optional` **protectedHeader?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) JWS Protected Header. *** ### unprotectedHeader? • `optional` **unprotectedHeader?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) JWS Unprotected Header. ================================================ FILE: docs/types/interfaces/GeneralDecryptResult.md ================================================ # Interface: GeneralDecryptResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). General JWE JSON Serialization Syntax decryption result ## Properties ### plaintext • **plaintext**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) Plaintext. *** ### additionalAuthenticatedData? • `optional` **additionalAuthenticatedData?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) JWE AAD. *** ### protectedHeader? • `optional` **protectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Protected Header. *** ### sharedUnprotectedHeader? • `optional` **sharedUnprotectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Shared Unprotected Header. *** ### unprotectedHeader? • `optional` **unprotectedHeader?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) JWE Per-Recipient Unprotected Header. ================================================ FILE: docs/types/interfaces/GeneralJWE.md ================================================ # Interface: GeneralJWE ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). General JWE JSON Serialization Syntax token. ## Properties ### ciphertext • **ciphertext**: `string` The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). *** ### recipients • **recipients**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`FlattenedJWE`](FlattenedJWE.md), `"header"` \| `"encrypted_key"`\>[] *** ### aad? • `optional` **aad?**: `string` The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a base64url-encoded value to be integrity protected but not encrypted. *** ### iv? • `optional` **iv?**: `string` The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent. *** ### protected? • `optional` **protected?**: `string` The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These Header Parameter values are integrity protected. *** ### tag? • `optional` **tag?**: `string` The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent. *** ### unprotected? • `optional` **unprotected?**: [`JWEHeaderParameters`](JWEHeaderParameters.md) The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This value is represented as an unencoded JSON object, rather than as a string. These Header Parameter values are not integrity protected. ================================================ FILE: docs/types/interfaces/GeneralJWS.md ================================================ # Interface: GeneralJWS ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). General JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS Unencoded Payload ([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) is used. ## Properties ### payload • **payload**: `string` *** ### signatures • **signatures**: [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys)\<[`FlattenedJWSInput`](FlattenedJWSInput.md), `"payload"`\>[] ================================================ FILE: docs/types/interfaces/GeneralJWSInput.md ================================================ # Interface: GeneralJWSInput ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). General JWS definition for verify function inputs, allows payload as [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) for detached signature validation. ## Properties ### payload • **payload**: `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when JWS Unencoded Payload ([RFC7797](https://www.rfc-editor.org/rfc/rfc7797)) "b64": false is used the value passed may also be a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). *** ### signatures • **signatures**: [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys)\<[`FlattenedJWSInput`](FlattenedJWSInput.md), `"payload"`\>[] The "signatures" member value MUST be an array of JSON objects. Each object represents a signature or MAC over the JWS Payload and the JWS Protected Header. ================================================ FILE: docs/types/interfaces/GeneralVerifyResult.md ================================================ # Interface: GeneralVerifyResult ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). General JWS JSON Serialization Syntax verification result ## Properties ### payload • **payload**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) JWS Payload. *** ### protectedHeader? • `optional` **protectedHeader?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) JWS Protected Header. *** ### unprotectedHeader? • `optional` **unprotectedHeader?**: [`JWSHeaderParameters`](JWSHeaderParameters.md) JWS Unprotected Header. ================================================ FILE: docs/types/interfaces/GetKeyFunction.md ================================================ # Interface: GetKeyFunction()\ ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Generic Interface for consuming operations dynamic key resolution. ## Type Parameters | Type Parameter | Description | | ------ | ------ | | `IProtectedHeader` | Type definition of the JWE or JWS Protected Header. | | `IToken` | Type definition of the consumed JWE or JWS token. | ▸ **GetKeyFunction**(`protectedHeader`, `token`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](JWK.md) \| [`KeyObject`](KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](JWK.md) \| [`KeyObject`](KeyObject.md)\> Dynamic key resolution function. No token components have been verified at the time of this function call. If a suitable key for the token cannot be matched, throw an error instead. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `protectedHeader` | `IProtectedHeader` | JWE or JWS Protected Header. | | `token` | `IToken` | The consumed JWE or JWS token. | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](JWK.md) \| [`KeyObject`](KeyObject.md) \| [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`JWK`](JWK.md) \| [`KeyObject`](KeyObject.md)\> ================================================ FILE: docs/types/interfaces/JSONWebKeySet.md ================================================ # Interface: JSONWebKeySet ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JSON Web Key Set ## Properties ### keys • **keys**: [`JWK`](JWK.md)[] ================================================ FILE: docs/types/interfaces/JWEHeaderParameters.md ================================================ # Interface: JWEHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized JWE Header Parameters, any other Header members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWE Header member. ## Properties ### alg? • `optional` **alg?**: `string` JWE "alg" (Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg) *** ### crit? • `optional` **crit?**: `string`[] JWE "crit" (Critical) Header Parameter *** ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### enc? • `optional` **enc?**: `string` JWE "enc" (Encryption Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jwe-alg) *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter *** ### zip? • `optional` **zip?**: `string` JWE "zip" (Compression Algorithm) Header Parameter. The only supported value is `"DEF"` (DEFLATE). Requires the `CompressionStream` / `DecompressionStream` APIs to be available in the runtime. #### See [JWE "zip" Header Parameter](https://www.rfc-editor.org/rfc/rfc7516#section-4.1.3) ================================================ FILE: docs/types/interfaces/JWEKeyManagementHeaderParameters.md ================================================ # Interface: JWEKeyManagementHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized JWE Key Management-related Header Parameters. ## Properties ### apu? • `optional` **apu?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) ECDH-ES "apu" (Agreement PartyUInfo). This will be used as a JOSE Header Parameter and will be used in ECDH's ConcatKDF. *** ### apv? • `optional` **apv?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) ECDH-ES "apv" (Agreement PartyVInfo). This will be used as a JOSE Header Parameter and will be used in ECDH's ConcatKDF. *** ### ~~epk?~~ • `optional` **epk?**: [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) \| [`KeyObject`](KeyObject.md) #### Deprecated You should not use this parameter. It is only intended for testing and vector validation purposes. *** ### ~~iv?~~ • `optional` **iv?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) #### Deprecated You should not use this parameter. It is only intended for testing and vector validation purposes. *** ### ~~p2c?~~ • `optional` **p2c?**: `number` #### Deprecated You should not use this parameter. It is only intended for testing and vector validation purposes. *** ### ~~p2s?~~ • `optional` **p2s?**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) #### Deprecated You should not use this parameter. It is only intended for testing and vector validation purposes. ================================================ FILE: docs/types/interfaces/JWK.md ================================================ # Interface: JWK ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JSON Web Key ([JWK](https://www.rfc-editor.org/rfc/rfc7517)). "RSA", "EC", "OKP", "AKP", and "oct" key types are supported. ## See - [JWK\_AKP\_Public](JWK_AKP_Public.md) - [JWK\_AKP\_Private](JWK_AKP_Private.md) - [JWK\_OKP\_Public](JWK_OKP_Public.md) - [JWK\_OKP\_Private](JWK_OKP_Private.md) - [JWK\_EC\_Public](JWK_EC_Public.md) - [JWK\_EC\_Private](JWK_EC_Private.md) - [JWK\_RSA\_Public](JWK_RSA_Public.md) - [JWK\_RSA\_Private](JWK_RSA_Private.md) - [JWK\_oct](JWK_oct.md) ## Properties ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### crv? • `optional` **crv?**: `string` - EC JWK "crv" (Curve) Parameter - OKP JWK "crv" (The Subtype of Key Pair) Parameter *** ### d? • `optional` **d?**: `string` - Private RSA JWK "d" (Private Exponent) Parameter - Private EC JWK "d" (ECC Private Key) Parameter - Private OKP JWK "d" (The Private Key) Parameter *** ### dp? • `optional` **dp?**: `string` Private RSA JWK "dp" (First Factor CRT Exponent) Parameter *** ### dq? • `optional` **dq?**: `string` Private RSA JWK "dq" (Second Factor CRT Exponent) Parameter *** ### e? • `optional` **e?**: `string` RSA JWK "e" (Exponent) Parameter *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### k? • `optional` **k?**: `string` Oct JWK "k" (Key Value) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### n? • `optional` **n?**: `string` RSA JWK "n" (Modulus) Parameter *** ### p? • `optional` **p?**: `string` Private RSA JWK "p" (First Prime Factor) Parameter *** ### priv? • `optional` **priv?**: `string` AKP JWK "priv" (Private key) Parameter *** ### pub? • `optional` **pub?**: `string` AKP JWK "pub" (Public Key) Parameter *** ### q? • `optional` **q?**: `string` Private RSA JWK "q" (Second Prime Factor) Parameter *** ### qi? • `optional` **qi?**: `string` Private RSA JWK "qi" (First CRT Coefficient) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x? • `optional` **x?**: `string` - EC JWK "x" (X Coordinate) Parameter - OKP JWK "x" (The public key) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter *** ### y? • `optional` **y?**: `string` EC JWK "y" (Y Coordinate) Parameter ================================================ FILE: docs/types/interfaces/JWKParameters.md ================================================ # Interface: JWKParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Generic JSON Web Key Parameters. ## Properties ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_AKP_Private.md ================================================ # Interface: JWK\_AKP\_Private ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Private AKP JSON Web Keys ## Properties ### alg • **alg**: `string` JWK "alg" (Algorithm) Parameter *** ### priv • **priv**: `string` AKP JWK "priv" (The Private Key) Parameter *** ### pub • **pub**: `string` AKP JWK "pub" (The Public key) Parameter *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_AKP_Public.md ================================================ # Interface: JWK\_AKP\_Public ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Public AKP JSON Web Keys ## Properties ### alg • **alg**: `string` JWK "alg" (Algorithm) Parameter *** ### pub • **pub**: `string` AKP JWK "pub" (The Public key) Parameter *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_EC_Private.md ================================================ # Interface: JWK\_EC\_Private ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Private EC JSON Web Keys ## Properties ### crv • **crv**: `string` EC JWK "crv" (Curve) Parameter *** ### d • **d**: `string` EC JWK "d" (ECC Private Key) Parameter *** ### x • **x**: `string` EC JWK "x" (X Coordinate) Parameter *** ### y • **y**: `string` EC JWK "y" (Y Coordinate) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_EC_Public.md ================================================ # Interface: JWK\_EC\_Public ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Public EC JSON Web Keys ## Properties ### crv • **crv**: `string` EC JWK "crv" (Curve) Parameter *** ### x • **x**: `string` EC JWK "x" (X Coordinate) Parameter *** ### y • **y**: `string` EC JWK "y" (Y Coordinate) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_OKP_Private.md ================================================ # Interface: JWK\_OKP\_Private ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Private OKP JSON Web Keys ## Properties ### crv • **crv**: `string` OKP JWK "crv" (The Subtype of Key Pair) Parameter *** ### d • **d**: `string` OKP JWK "d" (The Private Key) Parameter *** ### x • **x**: `string` OKP JWK "x" (The public key) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_OKP_Public.md ================================================ # Interface: JWK\_OKP\_Public ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Public OKP JSON Web Keys ## Properties ### crv • **crv**: `string` OKP JWK "crv" (The Subtype of Key Pair) Parameter *** ### x • **x**: `string` OKP JWK "x" (The public key) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_RSA_Private.md ================================================ # Interface: JWK\_RSA\_Private ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Private RSA JSON Web Keys ## Properties ### d • **d**: `string` RSA JWK "d" (Private Exponent) Parameter *** ### dp • **dp**: `string` RSA JWK "dp" (First Factor CRT Exponent) Parameter *** ### dq • **dq**: `string` RSA JWK "dq" (Second Factor CRT Exponent) Parameter *** ### e • **e**: `string` RSA JWK "e" (Exponent) Parameter *** ### n • **n**: `string` RSA JWK "n" (Modulus) Parameter *** ### p • **p**: `string` RSA JWK "p" (First Prime Factor) Parameter *** ### q • **q**: `string` RSA JWK "q" (Second Prime Factor) Parameter *** ### qi • **qi**: `string` RSA JWK "qi" (First CRT Coefficient) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_RSA_Public.md ================================================ # Interface: JWK\_RSA\_Public ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for Public RSA JSON Web Keys ## Properties ### e • **e**: `string` RSA JWK "e" (Exponent) Parameter *** ### n • **n**: `string` RSA JWK "n" (Modulus) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWK_oct.md ================================================ # Interface: JWK\_oct ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Convenience interface for oct JSON Web Keys ## Properties ### k • **k**: `string` Oct JWK "k" (Key Value) Parameter *** ### alg? • `optional` **alg?**: `string` JWK "alg" (Algorithm) Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210) *** ### ext? • `optional` **ext?**: `boolean` JWK "ext" (Extractable) Parameter *** ### key\_ops? • `optional` **key\_ops?**: `string`[] JWK "key_ops" (Key Operations) Parameter *** ### kid? • `optional` **kid?**: `string` JWK "kid" (Key ID) Parameter *** ### kty? • `optional` **kty?**: `string` JWK "kty" (Key Type) Parameter *** ### use? • `optional` **use?**: `string` JWK "use" (Public Key Use) Parameter *** ### x5c? • `optional` **x5c?**: `string`[] JWK "x5c" (X.509 Certificate Chain) Parameter *** ### x5t? • `optional` **x5t?**: `string` JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter *** ### x5t#S256? • `optional` **x5t#S256?**: `string` JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter *** ### x5u? • `optional` **x5u?**: `string` JWK "x5u" (X.509 URL) Parameter ================================================ FILE: docs/types/interfaces/JWSHeaderParameters.md ================================================ # Interface: JWSHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized JWS Header Parameters, any other Header Members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWS Header member. ## Properties ### alg? • `optional` **alg?**: `string` JWS "alg" (Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg) *** ### b64? • `optional` **b64?**: `boolean` This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing Input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797). *** ### crit? • `optional` **crit?**: `string`[] JWS "crit" (Critical) Header Parameter *** ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter ================================================ FILE: docs/types/interfaces/JWTClaimVerificationOptions.md ================================================ # Interface: JWTClaimVerificationOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JWT Claims Set verification options. ## Properties ### audience? • `optional` **audience?**: `string` \| `string`[] Expected JWT "aud" (Audience) Claim value(s). This option makes the JWT "aud" (Audience) Claim presence required. *** ### clockTolerance? • `optional` **clockTolerance?**: `string` \| `number` Clock skew tolerance - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). Used when validating the JWT "nbf" (Not Before) and "exp" (Expiration Time) claims, and when validating the "iat" (Issued At) claim if the [`maxTokenAge` option](#maxtokenage) is set. *** ### currentDate? • `optional` **currentDate?**: [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) Date to use when comparing NumericDate claims, defaults to `new Date()`. *** ### issuer? • `optional` **issuer?**: `string` \| `string`[] Expected JWT "iss" (Issuer) Claim value(s). This option makes the JWT "iss" (Issuer) Claim presence required. *** ### maxTokenAge? • `optional` **maxTokenAge?**: `string` \| `number` Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. - In seconds when number (e.g. 5) - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). This option makes the JWT "iat" (Issued At) Claim presence required. *** ### requiredClaims? • `optional` **requiredClaims?**: `string`[] Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if the [`issuer` option](#issuer) is set, then JWT "iss" (Issuer) Claim must be present; if the [`audience` option](#audience) is set, then JWT "aud" (Audience) Claim must be present; if the [`subject` option](#subject) is set, then JWT "sub" (Subject) Claim must be present; if the [`maxTokenAge` option](#maxtokenage) is set, then JWT "iat" (Issued At) Claim must be present. *** ### subject? • `optional` **subject?**: `string` Expected JWT "sub" (Subject) Claim value. This option makes the JWT "sub" (Subject) Claim presence required. *** ### typ? • `optional` **typ?**: `string` Expected JWT "typ" (Type) Header Parameter value. This option makes the JWT "typ" (Type) Header Parameter presence required. ================================================ FILE: docs/types/interfaces/JWTDecryptResult.md ================================================ # Interface: JWTDecryptResult\ ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Encrypted JSON Web Token (JWT) decryption result ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](JWTPayload.md) | ## Properties ### payload • **payload**: `PayloadType` & [`JWTPayload`](JWTPayload.md) JWT Claims Set. *** ### protectedHeader • **protectedHeader**: [`CompactJWEHeaderParameters`](CompactJWEHeaderParameters.md) JWE Protected Header. ================================================ FILE: docs/types/interfaces/JWTHeaderParameters.md ================================================ # Interface: JWTHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized Signed JWT Header Parameters, any other Header Members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWS Header member. ## Properties ### alg • **alg**: `string` JWS "alg" (Algorithm) Header Parameter #### See [Algorithm Key Requirements](https://github.com/panva/jose/issues/210#jws-alg) *** ### b64? • `optional` **b64?**: `true` This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing Input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797). *** ### crit? • `optional` **crit?**: `string`[] JWS "crit" (Critical) Header Parameter *** ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter ================================================ FILE: docs/types/interfaces/JWTPayload.md ================================================ # Interface: JWTPayload ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Recognized JWT Claims Set members, any other members may also be present. ## Indexable > \[`propName`: `string`\]: `unknown` Any other JWT Claim Set member. ## Properties ### aud? • `optional` **aud?**: `string` \| `string`[] JWT Audience #### See [RFC7519#section-4.1.3](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) *** ### exp? • `optional` **exp?**: `number` JWT Expiration Time #### See [RFC7519#section-4.1.4](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4) *** ### iat? • `optional` **iat?**: `number` JWT Issued At #### See [RFC7519#section-4.1.6](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6) *** ### iss? • `optional` **iss?**: `string` JWT Issuer #### See [RFC7519#section-4.1.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1) *** ### jti? • `optional` **jti?**: `string` JWT ID #### See [RFC7519#section-4.1.7](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7) *** ### nbf? • `optional` **nbf?**: `number` JWT Not Before #### See [RFC7519#section-4.1.5](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5) *** ### sub? • `optional` **sub?**: `string` JWT Subject #### See [RFC7519#section-4.1.2](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2) ================================================ FILE: docs/types/interfaces/JWTVerifyResult.md ================================================ # Interface: JWTVerifyResult\ ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Signed JSON Web Token (JWT) verification result ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](JWTPayload.md) | ## Properties ### payload • **payload**: `PayloadType` & [`JWTPayload`](JWTPayload.md) JWT Claims Set. *** ### protectedHeader • **protectedHeader**: [`JWTHeaderParameters`](JWTHeaderParameters.md) JWS Protected Header. ================================================ FILE: docs/types/interfaces/JoseHeaderParameters.md ================================================ # Interface: JoseHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Header Parameters common to JWE and JWS ## Properties ### cty? • `optional` **cty?**: `string` "cty" (Content Type) Header Parameter *** ### jku? • `optional` **jku?**: `string` "jku" (JWK Set URL) Header Parameter *** ### jwk? • `optional` **jwk?**: [`Pick`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)\<[`JWK`](JWK.md), `"x"` \| `"y"` \| `"crv"` \| `"e"` \| `"n"` \| `"pub"` \| `"kty"` \| `"alg"`\> "jwk" (JSON Web Key) Header Parameter *** ### kid? • `optional` **kid?**: `string` "kid" (Key ID) Header Parameter *** ### typ? • `optional` **typ?**: `string` "typ" (Type) Header Parameter *** ### x5c? • `optional` **x5c?**: `string`[] "x5c" (X.509 Certificate Chain) Header Parameter *** ### x5t? • `optional` **x5t?**: `string` "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter *** ### x5u? • `optional` **x5u?**: `string` "x5u" (X.509 URL) Header Parameter ================================================ FILE: docs/types/interfaces/KeyObject.md ================================================ # Interface: KeyObject ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) is a representation of a key/secret available in the Node.js runtime. You may use the Node.js runtime APIs [createPublicKey](https://nodejs.org/api/crypto.html#cryptocreatepublickeykey), [createPrivateKey](https://nodejs.org/api/crypto.html#cryptocreateprivatekeykey), and [createSecretKey](https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding) to obtain a [KeyObject](https://nodejs.org/api/crypto.html#class-keyobject) from your existing key material. ## Properties ### type • **type**: `string` ================================================ FILE: docs/types/interfaces/ProduceJWT.md ================================================ # Interface: ProduceJWT ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Generic interface for JWT producing classes. ## Methods ### setAudience() ▸ **setAudience**(`audience`): `this` Set the "aud" (Audience) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `audience` | `string` \| `string`[] | "aud" (Audience) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setExpirationTime() ▸ **setExpirationTime**(`input`): `this` Set the "exp" (Expiration Time) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "exp" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuedAt() ▸ **setIssuedAt**(`input?`): `this` Set the "iat" (Issued At) Claim. - If no argument is used the current unix timestamp is used as the claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input?` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "iat" (Expiration Time) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setIssuer() ▸ **setIssuer**(`issuer`): `this` Set the "iss" (Issuer) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `issuer` | `string` | "Issuer" Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setJti() ▸ **setJti**(`jwtId`): `this` Set the "jti" (JWT ID) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwtId` | `string` | "jti" (JWT ID) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setNotBefore() ▸ **setNotBefore**(`input`): `this` Set the "nbf" (Not Before) Claim. - If a `number` is passed as an argument it is used as the claim directly. - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the claim. - If a `string` is passed as an argument it is resolved to a time span, and then added to the current unix timestamp and used as the claim. Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 day". Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `input` | `string` \| `number` \| [`Date`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | "nbf" (Not Before) Claim value to set on the JWT Claims Set. | #### Returns `this` *** ### setSubject() ▸ **setSubject**(`subject`): `this` Set the "sub" (Subject) Claim. #### Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `subject` | `string` | "sub" (Subject) Claim value to set on the JWT Claims Set. | #### Returns `this` ================================================ FILE: docs/types/interfaces/ResolvedKey.md ================================================ # Interface: ResolvedKey ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). When key resolver functions are used this becomes part of successful resolves ## Properties ### key • **key**: [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) \| [`CryptoKey`](https://developer.mozilla.org/docs/Web/API/CryptoKey) Key resolved from the key resolver function. ================================================ FILE: docs/types/interfaces/SignOptions.md ================================================ # Interface: SignOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JWS Signing options. ## Properties ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` ================================================ FILE: docs/types/interfaces/VerifyOptions.md ================================================ # Interface: VerifyOptions ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). JWS Verification options. ## Properties ### algorithms? • `optional` **algorithms?**: `string`[] A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg" (Algorithm) values applicable for the used key/secret are allowed. > [!NOTE]\ > Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API. *** ### crit? • `optional` **crit?**: `object` An object with keys representing recognized "crit" (Critical) Header Parameter names. The value for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity protected, `false` when it's irrelevant. This makes the "Extension Header Parameter "..." is not recognized" error go away. Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically correct when provided and that it is optionally integrity protected. It will not process the Header Parameter in any way or reject the operation if it is missing. You MUST still verify the Header Parameter was present and process it according to the profile's validation steps after the operation succeeds. The JWS extension Header Parameter `b64` is always recognized and processed properly. No other registered Header Parameters that need this kind of default built-in treatment are currently available. #### Index Signature \[`propName`: `string`\]: `boolean` ================================================ FILE: docs/types/type-aliases/CryptoKey.md ================================================ # Type Alias: CryptoKey ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • **CryptoKey** = [`Extract`](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union)\<[`Awaited`](https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype)\<[`ReturnType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype)\<*typeof* [`crypto.subtle.generateKey`](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/generateKey)\>\>, \{ `type`: `string`; \}\> [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) is a representation of a key/secret available in all supported runtimes. In addition to the [Key Import Functions](../../key/import/README.md) you may use the [SubtleCrypto.importKey](https://developer.mozilla.org/docs/Web/API/SubtleCrypto/importKey) API to obtain a [CryptoKey](https://developer.mozilla.org/docs/Web/API/CryptoKey) from your existing key material. ================================================ FILE: docs/util/base64url/README.md ================================================ # util/base64url Base64URL encoding and decoding utilities ## Functions - [decode](functions/decode.md) - [encode](functions/encode.md) ================================================ FILE: docs/util/base64url/functions/decode.md ================================================ # Function: decode() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **decode**(`input`): [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) Decodes a Base64URL encoded input. ## Parameters | Parameter | Type | | ------ | ------ | | `input` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | ## Returns [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) ================================================ FILE: docs/util/base64url/functions/encode.md ================================================ # Function: encode() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **encode**(`input`): `string` Encodes an input using Base64URL with no padding. ## Parameters | Parameter | Type | | ------ | ------ | | `input` | `string` \| [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | ## Returns `string` ================================================ FILE: docs/util/decode_jwt/README.md ================================================ # util/decode\_jwt JSON Web Token (JWT) Claims Set Decoding (no validation, no signature checking) ## Functions - [decodeJwt](functions/decodeJwt.md) ================================================ FILE: docs/util/decode_jwt/functions/decodeJwt.md ================================================ # Function: decodeJwt() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **decodeJwt**\<`PayloadType`\>(`jwt`): `PayloadType` & [`JWTPayload`](../../../types/interfaces/JWTPayload.md) Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set types or values. This does not validate the JWS Signature. For a proper Signed JWT Claims Set validation and JWS signature verification use `jose.jwtVerify()`. For an encrypted JWT Claims Set validation and JWE decryption use `jose.jwtDecrypt()`. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/jwt/decode'`. ## Type Parameters | Type Parameter | Default type | | ------ | ------ | | `PayloadType` | [`JWTPayload`](../../../types/interfaces/JWTPayload.md) | ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `jwt` | `string` | JWT token in compact JWS serialization. | ## Returns `PayloadType` & [`JWTPayload`](../../../types/interfaces/JWTPayload.md) ## Example ```js const claims = jose.decodeJwt(token) console.log(claims) ``` ================================================ FILE: docs/util/decode_protected_header/README.md ================================================ # util/decode\_protected\_header JOSE Protected Header Decoding (JWE, JWS, all serialization syntaxes) ## Type Aliases - [ProtectedHeaderParameters](type-aliases/ProtectedHeaderParameters.md) ## Functions - [decodeProtectedHeader](functions/decodeProtectedHeader.md) ================================================ FILE: docs/util/decode_protected_header/functions/decodeProtectedHeader.md ================================================ # Function: decodeProtectedHeader() ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). ▸ **decodeProtectedHeader**(`token`): [`ProtectedHeaderParameters`](../type-aliases/ProtectedHeaderParameters.md) Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization. This function is exported (as a named export) from the main `'jose'` module entry point as well as from its subpath export `'jose/decode/protected_header'`. ## Parameters | Parameter | Type | Description | | ------ | ------ | ------ | | `token` | `string` \| `object` | JWE/JWS/JWT token in any JOSE serialization. | ## Returns [`ProtectedHeaderParameters`](../type-aliases/ProtectedHeaderParameters.md) ## Example ```js const protectedHeader = jose.decodeProtectedHeader(token) console.log(protectedHeader) ``` ================================================ FILE: docs/util/decode_protected_header/type-aliases/ProtectedHeaderParameters.md ================================================ # Type Alias: ProtectedHeaderParameters ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). • **ProtectedHeaderParameters** = [`JWSHeaderParameters`](../../../types/interfaces/JWSHeaderParameters.md) & [`JWEHeaderParameters`](../../../types/interfaces/JWEHeaderParameters.md) JWE and JWS Header Parameters ================================================ FILE: docs/util/errors/README.md ================================================ # util/errors JOSE module errors and error codes ## Classes - [JOSEAlgNotAllowed](classes/JOSEAlgNotAllowed.md) - [JOSEError](classes/JOSEError.md) - [JOSENotSupported](classes/JOSENotSupported.md) - [JWEDecryptionFailed](classes/JWEDecryptionFailed.md) - [JWEInvalid](classes/JWEInvalid.md) - [JWKInvalid](classes/JWKInvalid.md) - [JWKSInvalid](classes/JWKSInvalid.md) - [JWKSMultipleMatchingKeys](classes/JWKSMultipleMatchingKeys.md) - [JWKSNoMatchingKey](classes/JWKSNoMatchingKey.md) - [JWKSTimeout](classes/JWKSTimeout.md) - [JWSInvalid](classes/JWSInvalid.md) - [JWSSignatureVerificationFailed](classes/JWSSignatureVerificationFailed.md) - [JWTClaimValidationFailed](classes/JWTClaimValidationFailed.md) - [JWTExpired](classes/JWTExpired.md) - [JWTInvalid](classes/JWTInvalid.md) ================================================ FILE: docs/util/errors/classes/JOSEAlgNotAllowed.md ================================================ # Class: JOSEAlgNotAllowed ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JOSE_ALG_NOT_ALLOWED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JOSEAlgNotAllowed) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JOSE_ALG_NOT_ALLOWED'` A unique error code for JOSEAlgNotAllowed. ================================================ FILE: docs/util/errors/classes/JOSEError.md ================================================ # Class: JOSEError ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). A generic Error that all other JOSE specific Error subclasses extend. ## Example Checking thrown error is a JOSE one ```js if (err instanceof jose.errors.JOSEError) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JOSE_GENERIC'` A unique error code for JOSEError. ================================================ FILE: docs/util/errors/classes/JOSENotSupported.md ================================================ # Class: JOSENotSupported ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a particular feature or algorithm is not supported by this implementation or JOSE in general. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JOSE_NOT_SUPPORTED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JOSENotSupported) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JOSE_NOT_SUPPORTED'` A unique error code for JOSENotSupported. ================================================ FILE: docs/util/errors/classes/JWEDecryptionFailed.md ================================================ # Class: JWEDecryptionFailed ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWE ciphertext decryption fails. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWE_DECRYPTION_FAILED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWEDecryptionFailed) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWE_DECRYPTION_FAILED'` A unique error code for JWEDecryptionFailed. ================================================ FILE: docs/util/errors/classes/JWEInvalid.md ================================================ # Class: JWEInvalid ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWE is invalid. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWE_INVALID') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWEInvalid) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWE_INVALID'` A unique error code for JWEInvalid. ================================================ FILE: docs/util/errors/classes/JWKInvalid.md ================================================ # Class: JWKInvalid ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWK is invalid. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWK_INVALID') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWKInvalid) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWK_INVALID'` A unique error code for JWKInvalid. ================================================ FILE: docs/util/errors/classes/JWKSInvalid.md ================================================ # Class: JWKSInvalid ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWKS is invalid. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWKS_INVALID') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWKSInvalid) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWKS_INVALID'` A unique error code for JWKSInvalid. ================================================ FILE: docs/util/errors/classes/JWKSMultipleMatchingKeys.md ================================================ # Class: JWKSMultipleMatchingKeys ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when multiple keys match from a JWKS. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWKSMultipleMatchingKeys) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWKS_MULTIPLE_MATCHING_KEYS'` A unique error code for JWKSMultipleMatchingKeys. ================================================ FILE: docs/util/errors/classes/JWKSNoMatchingKey.md ================================================ # Class: JWKSNoMatchingKey ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when no keys match from a JWKS. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWKS_NO_MATCHING_KEY') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWKSNoMatchingKey) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWKS_NO_MATCHING_KEY'` A unique error code for JWKSNoMatchingKey. ================================================ FILE: docs/util/errors/classes/JWKSTimeout.md ================================================ # Class: JWKSTimeout ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). Timeout was reached when retrieving the JWKS response. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWKS_TIMEOUT') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWKSTimeout) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWKS_TIMEOUT'` A unique error code for JWKSTimeout. ================================================ FILE: docs/util/errors/classes/JWSInvalid.md ================================================ # Class: JWSInvalid ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWS is invalid. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWS_INVALID') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWSInvalid) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWS_INVALID'` A unique error code for JWSInvalid. ================================================ FILE: docs/util/errors/classes/JWSSignatureVerificationFailed.md ================================================ # Class: JWSSignatureVerificationFailed ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when JWS signature verification fails. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWSSignatureVerificationFailed) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWS_SIGNATURE_VERIFICATION_FAILED'` A unique error code for JWSSignatureVerificationFailed. ================================================ FILE: docs/util/errors/classes/JWTClaimValidationFailed.md ================================================ # Class: JWTClaimValidationFailed ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWT Claim Set member validation fails. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWT_CLAIM_VALIDATION_FAILED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWTClaimValidationFailed) { // ... } ``` ## Properties ### claim • **claim**: `string` The Claim for which the validation failed. *** ### code • **code**: `string` = `'ERR_JWT_CLAIM_VALIDATION_FAILED'` A unique error code for JWTClaimValidationFailed. *** ### payload • **payload**: [`JWTPayload`](../../../types/interfaces/JWTPayload.md) The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity has however been verified. Claims Set verification happens after the JWS Signature or JWE Decryption processes. *** ### reason • **reason**: `string` Reason code for the validation failure. ================================================ FILE: docs/util/errors/classes/JWTExpired.md ================================================ # Class: JWTExpired ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWT is expired. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWT_EXPIRED') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWTExpired) { // ... } ``` ## Properties ### claim • **claim**: `string` The Claim for which the validation failed. *** ### code • **code**: `string` = `'ERR_JWT_EXPIRED'` A unique error code for JWTExpired. *** ### payload • **payload**: [`JWTPayload`](../../../types/interfaces/JWTPayload.md) The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity has however been verified. Claims Set verification happens after the JWS Signature or JWE Decryption processes. *** ### reason • **reason**: `string` Reason code for the validation failure. ================================================ FILE: docs/util/errors/classes/JWTInvalid.md ================================================ # Class: JWTInvalid ## [💗 Help the project](https://github.com/sponsors/panva) Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). An error subclass thrown when a JWT is invalid. ## Examples Checking thrown error is this one using a stable error code ```js if (err.code === 'ERR_JWT_INVALID') { // ... } ``` Checking thrown error is this one using `instanceof` ```js if (err instanceof jose.errors.JWTInvalid) { // ... } ``` ## Properties ### code • **code**: `string` = `'ERR_JWT_INVALID'` A unique error code for JWTInvalid. ================================================ FILE: jsr.json ================================================ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@panva/jose", "version": "6.2.2", "exports": { ".": "./src/index.ts", "./jwk/embedded": "./src/jwk/embedded.ts", "./jwk/thumbprint": "./src/jwk/thumbprint.ts", "./key/import": "./src/key/import.ts", "./key/export": "./src/key/export.ts", "./key/generate/keypair": "./src/key/generate_key_pair.ts", "./key/generate/secret": "./src/key/generate_secret.ts", "./jwks/remote": "./src/jwks/remote.ts", "./jwks/local": "./src/jwks/local.ts", "./jwt/sign": "./src/jwt/sign.ts", "./jwt/verify": "./src/jwt/verify.ts", "./jwt/encrypt": "./src/jwt/encrypt.ts", "./jwt/decrypt": "./src/jwt/decrypt.ts", "./jwt/unsecured": "./src/jwt/unsecured.ts", "./jwt/decode": "./src/util/decode_jwt.ts", "./decode/protected_header": "./src/util/decode_protected_header.ts", "./jws/compact/sign": "./src/jws/compact/sign.ts", "./jws/compact/verify": "./src/jws/compact/verify.ts", "./jws/flattened/sign": "./src/jws/flattened/sign.ts", "./jws/flattened/verify": "./src/jws/flattened/verify.ts", "./jws/general/sign": "./src/jws/general/sign.ts", "./jws/general/verify": "./src/jws/general/verify.ts", "./jwe/compact/encrypt": "./src/jwe/compact/encrypt.ts", "./jwe/compact/decrypt": "./src/jwe/compact/decrypt.ts", "./jwe/flattened/encrypt": "./src/jwe/flattened/encrypt.ts", "./jwe/flattened/decrypt": "./src/jwe/flattened/decrypt.ts", "./jwe/general/encrypt": "./src/jwe/general/encrypt.ts", "./jwe/general/decrypt": "./src/jwe/general/decrypt.ts", "./errors": "./src/util/errors.ts", "./base64url": "./src/util/base64url.ts" }, "publish": { "include": ["LICENSE.md", "README.md", "src/**/*.ts"] } } ================================================ FILE: package.json ================================================ { "name": "jose", "version": "6.2.2", "description": "JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes", "keywords": [ "akp", "browser", "bun", "cloudflare", "compact", "decode", "decrypt", "deno", "detached", "ec", "ecdsa", "ed25519", "eddsa", "edge", "electron", "embedded", "encrypt", "flattened", "general", "jose", "json web token", "jsonwebtoken", "jwa", "jwe", "jwk", "jwks", "jws", "jwt-decode", "jwt", "ml-dsa", "netlify", "next", "nextjs", "oct", "okp", "payload", "pem", "pkcs8", "rsa", "sign", "signature", "spki", "validate", "vercel", "verify", "webcrypto", "workerd", "workers", "x509" ], "homepage": "https://github.com/panva/jose", "repository": "panva/jose", "funding": { "url": "https://github.com/sponsors/panva" }, "license": "MIT", "author": "Filip Skokan ", "sideEffects": false, "type": "module", "imports": { "#dist": { "types": "./dist/types/index.d.ts", "default": "./dist/webapi/index.js" }, "#dist/*": { "types": "./dist/types/*.d.ts", "default": "./dist/webapi/*.js" } }, "exports": { ".": { "types": "./dist/types/index.d.ts", "default": "./dist/webapi/index.js" }, "./jwk/embedded": { "types": "./dist/types/jwk/embedded.d.ts", "default": "./dist/webapi/jwk/embedded.js" }, "./jwk/thumbprint": { "types": "./dist/types/jwk/thumbprint.d.ts", "default": "./dist/webapi/jwk/thumbprint.js" }, "./key/import": { "types": "./dist/types/key/import.d.ts", "default": "./dist/webapi/key/import.js" }, "./key/export": { "types": "./dist/types/key/export.d.ts", "default": "./dist/webapi/key/export.js" }, "./key/generate/keypair": { "types": "./dist/types/key/generate_key_pair.d.ts", "default": "./dist/webapi/key/generate_key_pair.js" }, "./key/generate/secret": { "types": "./dist/types/key/generate_secret.d.ts", "default": "./dist/webapi/key/generate_secret.js" }, "./jwks/remote": { "types": "./dist/types/jwks/remote.d.ts", "default": "./dist/webapi/jwks/remote.js" }, "./jwks/local": { "types": "./dist/types/jwks/local.d.ts", "default": "./dist/webapi/jwks/local.js" }, "./jwt/sign": { "types": "./dist/types/jwt/sign.d.ts", "default": "./dist/webapi/jwt/sign.js" }, "./jwt/verify": { "types": "./dist/types/jwt/verify.d.ts", "default": "./dist/webapi/jwt/verify.js" }, "./jwt/encrypt": { "types": "./dist/types/jwt/encrypt.d.ts", "default": "./dist/webapi/jwt/encrypt.js" }, "./jwt/decrypt": { "types": "./dist/types/jwt/decrypt.d.ts", "default": "./dist/webapi/jwt/decrypt.js" }, "./jwt/unsecured": { "types": "./dist/types/jwt/unsecured.d.ts", "default": "./dist/webapi/jwt/unsecured.js" }, "./jwt/decode": { "types": "./dist/types/util/decode_jwt.d.ts", "default": "./dist/webapi/util/decode_jwt.js" }, "./decode/protected_header": { "types": "./dist/types/util/decode_protected_header.d.ts", "default": "./dist/webapi/util/decode_protected_header.js" }, "./jws/compact/sign": { "types": "./dist/types/jws/compact/sign.d.ts", "default": "./dist/webapi/jws/compact/sign.js" }, "./jws/compact/verify": { "types": "./dist/types/jws/compact/verify.d.ts", "default": "./dist/webapi/jws/compact/verify.js" }, "./jws/flattened/sign": { "types": "./dist/types/jws/flattened/sign.d.ts", "default": "./dist/webapi/jws/flattened/sign.js" }, "./jws/flattened/verify": { "types": "./dist/types/jws/flattened/verify.d.ts", "default": "./dist/webapi/jws/flattened/verify.js" }, "./jws/general/sign": { "types": "./dist/types/jws/general/sign.d.ts", "default": "./dist/webapi/jws/general/sign.js" }, "./jws/general/verify": { "types": "./dist/types/jws/general/verify.d.ts", "default": "./dist/webapi/jws/general/verify.js" }, "./jwe/compact/encrypt": { "types": "./dist/types/jwe/compact/encrypt.d.ts", "default": "./dist/webapi/jwe/compact/encrypt.js" }, "./jwe/compact/decrypt": { "types": "./dist/types/jwe/compact/decrypt.d.ts", "default": "./dist/webapi/jwe/compact/decrypt.js" }, "./jwe/flattened/encrypt": { "types": "./dist/types/jwe/flattened/encrypt.d.ts", "default": "./dist/webapi/jwe/flattened/encrypt.js" }, "./jwe/flattened/decrypt": { "types": "./dist/types/jwe/flattened/decrypt.d.ts", "default": "./dist/webapi/jwe/flattened/decrypt.js" }, "./jwe/general/encrypt": { "types": "./dist/types/jwe/general/encrypt.d.ts", "default": "./dist/webapi/jwe/general/encrypt.js" }, "./jwe/general/decrypt": { "types": "./dist/types/jwe/general/decrypt.d.ts", "default": "./dist/webapi/jwe/general/decrypt.js" }, "./errors": { "types": "./dist/types/util/errors.d.ts", "default": "./dist/webapi/util/errors.js" }, "./base64url": { "types": "./dist/types/util/base64url.d.ts", "default": "./dist/webapi/util/base64url.js" }, "./package.json": "./package.json" }, "main": "./dist/webapi/index.js", "types": "./dist/types/index.d.ts", "files": [ "dist/webapi/**/*.js", "dist/types/**/*.d.ts", "!dist/**/*.bundle.js", "!dist/**/*.umd.js", "!dist/**/*.min.js", "!dist/types/runtime/*", "!dist/types/lib/*", "!dist/deno/**/*" ], "scripts": { "build": "tsc", "build-all": "run-s clear build build:*", "build:bundle": "esbuild --bundle dist/webapi/index.js --format=esm --target=es2022 --outfile=dist/webapi/index.bundle.js", "build:bundle-min": "esbuild --minify --bundle dist/webapi/index.js --format=esm --target=es2022 --outfile=dist/webapi/index.bundle.min.js", "build:umd": "rollup dist/webapi/index.bundle.js --format umd --name jose -o dist/webapi/index.umd.js && rollup dist/webapi/index.bundle.min.js --compact --format umd --name jose -o dist/webapi/index.umd.min.js", "build:deno": "mkdir -p dist/deno && cp -R src/. dist/deno && find dist/deno -name '*.ts' -type f -print0 | xargs -0 sed -i.bak -e \"s/\\.js'/.ts'/g\" && npm run sedcleanup", "build:types": "npm run build -- -p ./tsconfig/types.json && cd src && find . -name '*.d.ts' -maxdepth 2 -type f -exec rsync -R \"{}\" ../dist/types \\;", "build:jsr": "npx --yes jsr publish --dry-run --allow-dirty", "clear": "rm -Rf dist", "sedcleanup": "find . -name '*.bak' -type f -print0 | xargs -0 rm -f", "docs:generate": "typedoc", "types:find": "find dist/types -name '*.d.ts' -type f -print0", "test": "bash -c 'source .node_flags.sh && ava'", "format": "prettier --log-level silent --write ./test ./tap ./src ./tools ./cookbook ./tsconfig", "tap:browsers": "./tap/.browsers.sh", "tap:bun": "./tap/.bun.sh", "tap:deno": "./tap/.deno.sh", "tap:electron": "./tap/.electron.sh", "tap:node": "bash -c './tap/.node.sh'", "tap:workerd": "./tap/.workerd.sh" }, "devDependencies": { "@playwright/test": "^1.58.2", "@types/node": "^24.12.0", "@types/qunit": "^2.19.13", "ava": "^7.0.0", "esbuild": "^0.27.4", "glob": "^13.0.6", "npm-run-all2": "^8.0.4", "patch-package": "^8.0.1", "prettier": "^3.8.1", "prettier-plugin-jsdoc": "^1.8.0", "qunit": "^2.25.0", "rollup": "^4.59.0", "tar": "^7.5.11", "timekeeper": "^2.3.1", "tsx": "^4.21.0", "typedoc": "^0.28.17", "typedoc-plugin-markdown": "^4.11.0", "typedoc-plugin-mdn-links": "^5.1.1", "typescript": "^5.9.3", "undici": "^7.24.4" } } ================================================ FILE: patches/typedoc-plugin-markdown+4.11.0.patch ================================================ diff --git a/node_modules/typedoc-plugin-markdown/dist/libs/utils/escape-chars.js b/node_modules/typedoc-plugin-markdown/dist/libs/utils/escape-chars.js index e29ab44..38f70d5 100644 --- a/node_modules/typedoc-plugin-markdown/dist/libs/utils/escape-chars.js +++ b/node_modules/typedoc-plugin-markdown/dist/libs/utils/escape-chars.js @@ -5,7 +5,7 @@ export function escapeChars(str) { .replace(/{/g, '\\{') .replace(/}/g, '\\}') .replace(/_/g, '\\_') - .replace(/`/g, '\\`') + // .replace(/`/g, '\\`') .replace(/\|/g, '\\|') .replace(/\[/g, '\\[') .replace(/\]/g, '\\]') diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/helpers/get-project-name.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/helpers/get-project-name.js index 1c71dae..c031a54 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/helpers/get-project-name.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/helpers/get-project-name.js @@ -4,6 +4,6 @@ export function getProjectName(stringWithPlaceholders, page, includeVersion = tr .replace('{version}', includeVersion && page.project.packageVersion ? `v${page.project.packageVersion}` : '') - .replace(/\s+/g, ' ') + // .replace(/\s+/g, ' ') .trim(); } diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.declarationTitle.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.declarationTitle.js index ee29ee7..9589eae 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.declarationTitle.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.declarationTitle.js @@ -26,7 +26,7 @@ export function declarationTitle(model) { md.push(';'); } const result = md.join(''); - return useCodeBlocks ? codeBlock(result) : `> ${result}`; + return useCodeBlocks ? codeBlock(result) : `• ${result}`; } function getPrefix(context, model, useCodeBlocks) { const keyword = context.helpers.getKeyword(model.kind); diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.hierarchy.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.hierarchy.js index b1d7fd3..b23a8da 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.hierarchy.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.hierarchy.js @@ -1,6 +1,7 @@ import { heading, unorderedList } from '../../../libs/markdown/index.js'; import { i18n } from 'typedoc'; export function hierarchy(model, options) { + return ''; const md = []; const getHierarchy = (hModel) => { const parent = !hModel.isTarget diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.inheritance.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.inheritance.js index 29eaa2b..c0c0f6c 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.inheritance.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.inheritance.js @@ -1,6 +1,7 @@ import { heading } from '../../../libs/markdown/index.js'; import { i18n } from 'typedoc'; export function inheritance(model, options) { + return ''; const md = []; if (model.implementationOf) { if (options.headingLevel !== -1) { diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.memberWithGroups.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.memberWithGroups.js index c2aab7d..fe6daf9 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.memberWithGroups.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.memberWithGroups.js @@ -42,7 +42,7 @@ export function memberWithGroups(model, options) { })); } } - if (model.implementedTypes?.length) { + if (false) { md.push(heading(options.headingLevel, i18n.theme_implements())); md.push(unorderedList(model.implementedTypes.map((implementedType) => this.partials.someType(implementedType)))); } diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.signatureTitle.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.signatureTitle.js index cbc03e8..5fe0813 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.signatureTitle.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/member.signatureTitle.js @@ -38,5 +38,5 @@ export function signatureTitle(model, options) { md.push(';'); } const result = md.join(''); - return useCodeBlocks ? codeBlock(result) : `> ${result}`; + return useCodeBlocks ? codeBlock(result) : `▸ ${result}`; } diff --git a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/page.pageTitle.js b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/page.pageTitle.js index 1df516f..9ce6394 100644 --- a/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/page.pageTitle.js +++ b/node_modules/typedoc-plugin-markdown/dist/theme/context/partials/page.pageTitle.js @@ -117,6 +117,6 @@ function getFromString(textContent, config) { .replace('{codeKeyword}', config.codeKeyword ?? '') .replace('{group}', config.group ?? '') .replace(/``/g, '') - .replace(/\s+/g, ' ') + // .replace(/\s+/g, ' ') .trim(); } ================================================ FILE: patches/typedoc-plugin-mdn-links+5.1.1.patch ================================================ diff --git a/node_modules/typedoc-plugin-mdn-links/data/web-api.json b/node_modules/typedoc-plugin-mdn-links/data/web-api.json index edf887d..8da4c95 100644 --- a/node_modules/typedoc-plugin-mdn-links/data/web-api.json +++ b/node_modules/typedoc-plugin-mdn-links/data/web-api.json @@ -1,4 +1,19 @@ { + "createPublicKey": { + "url": "https://nodejs.org/api/crypto.html#cryptocreatepublickeykey" + }, + "createPrivateKey": { + "url": "https://nodejs.org/api/crypto.html#cryptocreateprivatekeykey" + }, + "createSecretKey": { + "url": "https://nodejs.org/api/crypto.html#cryptocreatesecretkeykey-encoding" + }, + "KeyObject": { + "url": "https://nodejs.org/api/crypto.html#class-keyobject" + }, + "Buffer": { + "url": "https://nodejs.org/api/buffer.html#class-buffer" + }, "ANGLE_instanced_arrays": { "url": "https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays", "inst": { ================================================ FILE: playwright.config.ts ================================================ import { defineConfig, devices } from '@playwright/test' export default defineConfig({ testDir: './tap', testMatch: '.browser.ts', timeout: 120_000, projects: [ { name: 'chromium', use: devices['Desktop Chrome'] }, { name: 'firefox', use: devices['Desktop Firefox'] }, { name: 'safari', use: devices['Desktop Safari'] }, ], }) ================================================ FILE: src/index.ts ================================================ export { compactDecrypt } from './jwe/compact/decrypt.js' export type { CompactDecryptGetKey } from './jwe/compact/decrypt.js' export { flattenedDecrypt } from './jwe/flattened/decrypt.js' export type { FlattenedDecryptGetKey } from './jwe/flattened/decrypt.js' export { generalDecrypt } from './jwe/general/decrypt.js' export type { GeneralDecryptGetKey } from './jwe/general/decrypt.js' export { GeneralEncrypt } from './jwe/general/encrypt.js' export type { Recipient } from './jwe/general/encrypt.js' export { compactVerify } from './jws/compact/verify.js' export type { CompactVerifyGetKey } from './jws/compact/verify.js' export { flattenedVerify } from './jws/flattened/verify.js' export type { FlattenedVerifyGetKey } from './jws/flattened/verify.js' export { generalVerify } from './jws/general/verify.js' export type { GeneralVerifyGetKey } from './jws/general/verify.js' export { jwtVerify } from './jwt/verify.js' export type { JWTVerifyOptions, JWTVerifyGetKey } from './jwt/verify.js' export { jwtDecrypt } from './jwt/decrypt.js' export type { JWTDecryptOptions, JWTDecryptGetKey } from './jwt/decrypt.js' export { CompactEncrypt } from './jwe/compact/encrypt.js' export { FlattenedEncrypt } from './jwe/flattened/encrypt.js' export { CompactSign } from './jws/compact/sign.js' export { FlattenedSign } from './jws/flattened/sign.js' export { GeneralSign } from './jws/general/sign.js' export type { Signature } from './jws/general/sign.js' export { SignJWT } from './jwt/sign.js' export { EncryptJWT } from './jwt/encrypt.js' export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js' export { EmbeddedJWK } from './jwk/embedded.js' export { createLocalJWKSet } from './jwks/local.js' export { createRemoteJWKSet, jwksCache, customFetch } from './jwks/remote.js' export type { RemoteJWKSetOptions, JWKSCacheInput, ExportedJWKSCache, FetchImplementation, } from './jwks/remote.js' export { UnsecuredJWT } from './jwt/unsecured.js' export type { UnsecuredResult } from './jwt/unsecured.js' export { exportPKCS8, exportSPKI, exportJWK } from './key/export.js' export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js' export type { KeyImportOptions } from './key/import.js' export { decodeProtectedHeader } from './util/decode_protected_header.js' export { decodeJwt } from './util/decode_jwt.js' export type { ProtectedHeaderParameters } from './util/decode_protected_header.js' import * as errors from './util/errors.js' export { errors } export { generateKeyPair } from './key/generate_key_pair.js' export type { GenerateKeyPairResult, GenerateKeyPairOptions } from './key/generate_key_pair.js' export { generateSecret } from './key/generate_secret.js' export type { GenerateSecretOptions } from './key/generate_secret.js' import * as base64url from './util/base64url.js' export { base64url } export type { CompactDecryptResult, CompactJWEHeaderParameters, CompactJWSHeaderParameters, CompactVerifyResult, CritOption, CryptoKey, DecryptOptions, EncryptOptions, FlattenedDecryptResult, FlattenedJWE, FlattenedJWS, FlattenedJWSInput, FlattenedVerifyResult, GeneralDecryptResult, GeneralJWE, GeneralJWS, GeneralJWSInput, GeneralVerifyResult, GetKeyFunction, JoseHeaderParameters, JSONWebKeySet, JWEHeaderParameters, JWEKeyManagementHeaderParameters, JWK_EC_Private, JWK_EC_Public, JWK_oct, JWK_OKP_Private, JWK_OKP_Public, JWK_RSA_Private, JWK_RSA_Public, JWK, JWKParameters, JWSHeaderParameters, JWTClaimVerificationOptions, JWTDecryptResult, JWTHeaderParameters, JWTPayload, JWTVerifyResult, KeyObject, ProduceJWT, ResolvedKey, SignOptions, VerifyOptions, } from './types.d.ts' /** * In prior releases this indicated whether a Node.js-specific build was loaded, this is now fixed * to `"WebCryptoAPI"` * * @deprecated */ export const cryptoRuntime = 'WebCryptoAPI' ================================================ FILE: src/jwe/compact/decrypt.ts ================================================ /** * Decrypting JSON Web Encryption (JWE) in Compact Serialization * * @module */ import { flattenedDecrypt } from '../flattened/decrypt.js' import { JWEInvalid } from '../../util/errors.js' import { decoder } from '../../lib/buffer_utils.js' import type * as types from '../../types.d.ts' /** * Interface for Compact JWE Decryption dynamic key resolution. No token components have been * verified at the time of this function call. */ export interface CompactDecryptGetKey extends types.GetKeyFunction< types.CompactJWEHeaderParameters, types.FlattenedJWE > {} /** * Decrypts a Compact JWE. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwe/compact/decrypt'`. * * @example * * ```js * const jwe = * 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.nyQ19eq9ogh9wA7fFtnI2oouzy5_8b5DeLkoRMfi2yijgfTs2zEnayCEofz_qhnL-nwszabd9qUeHv0-IwvhhJJS7GUJOU3ikiIe42qcIAFme1A_Fo9CTxw4XTOy-I5qanl8So91u6hwfyN1VxAqVLsSE7_23EC-gfGEg_5znew9PyXXsOIE-K_HH7IQowRrlZ1X_bM_Liu53RzDpLDvRz59mp3S8L56YqpM8FexFGTGpEaoTcEIst375qncYt3-79IVR7gZN1RWsWgjPatfvVbnh74PglQcATSf3UUhaW0OAKn6q7r3PDx6DIKQ35bgHQg5QopuN00eIfLQL2trGw.W3grIVj5HVuAb76X.6PcuDe5D6ttWFYyv0oqqdDXfI2R8wBg1F2Q80UUA_Gv8eEimNWfxIWdLxrjzgQGSvIhxmFKuLM0.a93_Ug3uZHuczj70Zavx8Q' * * const { plaintext, protectedHeader } = await jose.compactDecrypt(jwe, privateKey) * * console.log(protectedHeader) * console.log(new TextDecoder().decode(plaintext)) * ``` * * @param jwe Compact JWE. * @param key Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export async function compactDecrypt( jwe: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.DecryptOptions, ): Promise /** * @param jwe Compact JWE. * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export async function compactDecrypt( jwe: string | Uint8Array, getKey: CompactDecryptGetKey, options?: types.DecryptOptions, ): Promise export async function compactDecrypt( jwe: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | CompactDecryptGetKey, options?: types.DecryptOptions, ) { if (jwe instanceof Uint8Array) { jwe = decoder.decode(jwe) } if (typeof jwe !== 'string') { throw new JWEInvalid('Compact JWE must be a string or Uint8Array') } const { 0: protectedHeader, 1: encryptedKey, 2: iv, 3: ciphertext, 4: tag, length, } = jwe.split('.') if (length !== 5) { throw new JWEInvalid('Invalid Compact JWE') } const decrypted = await flattenedDecrypt( { ciphertext, iv: iv || undefined, protected: protectedHeader, tag: tag || undefined, encrypted_key: encryptedKey || undefined, }, key as Parameters[1], options, ) const result = { plaintext: decrypted.plaintext, protectedHeader: decrypted.protectedHeader! } if (typeof key === 'function') { return { ...result, key: decrypted.key } } return result } ================================================ FILE: src/jwe/compact/encrypt.ts ================================================ /** * Encrypting JSON Web Encryption (JWE) in Compact Serialization * * @module */ import type * as types from '../../types.d.ts' import { FlattenedEncrypt } from '../flattened/encrypt.js' /** * The CompactEncrypt class is used to build and encrypt Compact JWE strings. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwe/compact/encrypt'`. * * @example * * ```js * const jwe = await new jose.CompactEncrypt( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' }) * .encrypt(publicKey) * * console.log(jwe) * ``` */ export class CompactEncrypt { #flattened: FlattenedEncrypt /** * {@link CompactEncrypt} constructor * * @param plaintext Binary representation of the plaintext to encrypt. */ constructor(plaintext: Uint8Array) { this.#flattened = new FlattenedEncrypt(plaintext) } /** * Sets a content encryption key to use, by default a random suitable one is generated for the JWE * enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param cek JWE Content Encryption Key. */ setContentEncryptionKey(cek: Uint8Array): this { this.#flattened.setContentEncryptionKey(cek) return this } /** * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param iv JWE Initialization Vector. */ setInitializationVector(iv: Uint8Array): this { this.#flattened.setInitializationVector(iv) return this } /** * Sets the JWE Protected Header on the CompactEncrypt object. * * @param protectedHeader JWE Protected Header object. */ setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this { this.#flattened.setProtectedHeader(protectedHeader) return this } /** * Sets the JWE Key Management parameters to be used when encrypting. * * (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement * PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. * * @param parameters JWE Key Management parameters. */ setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this { this.#flattened.setKeyManagementParameters(parameters) return this } /** * Encrypts and resolves the value of the Compact JWE string. * * @param key Public Key or Secret to encrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Encryption options. */ async encrypt( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.EncryptOptions, ): Promise { const jwe = await this.#flattened.encrypt(key, options) return [jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.') } } ================================================ FILE: src/jwe/flattened/decrypt.ts ================================================ /** * Decrypting JSON Web Encryption (JWE) in Flattened JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { decode as b64u } from '../../util/base64url.js' import { decrypt } from '../../lib/content_encryption.js' import { decodeBase64url } from '../../lib/helpers.js' import { JOSEAlgNotAllowed, JOSENotSupported, JWEInvalid } from '../../util/errors.js' import { isDisjoint } from '../../lib/type_checks.js' import { isObject } from '../../lib/type_checks.js' import { decryptKeyManagement } from '../../lib/key_management.js' import { decoder, concat, encode } from '../../lib/buffer_utils.js' import { generateCek } from '../../lib/content_encryption.js' import { validateCrit } from '../../lib/validate_crit.js' import { validateAlgorithms } from '../../lib/validate_algorithms.js' import { normalizeKey } from '../../lib/normalize_key.js' import { checkKeyType } from '../../lib/check_key_type.js' import { decompress } from '../../lib/deflate.js' /** * Interface for Flattened JWE Decryption dynamic key resolution. No token components have been * verified at the time of this function call. */ export interface FlattenedDecryptGetKey extends types.GetKeyFunction< types.JWEHeaderParameters | undefined, types.FlattenedJWE > {} /** * Decrypts a Flattened JWE. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwe/flattened/decrypt'`. * * @example * * ```js * const jwe = { * ciphertext: '9EzjFISUyoG-ifC2mSihfP0DPC80yeyrxhTzKt1C_VJBkxeBG0MI4Te61Pk45RAGubUvBpU9jm4', * iv: '8Fy7A_IuoX5VXG9s', * tag: 'W76IYV6arGRuDSaSyWrQNg', * encrypted_key: * 'Z6eD4UK_yFb5ZoKvKkGAdqywEG_m0e4IYo0x8Vf30LAMJcsc-_zSgIeiF82teZyYi2YYduHKoqImk7MRnoPZOlEs0Q5BNK1OgBmSOhCE8DFyqh9Zh48TCTP6lmBQ52naqoUJFMtHzu-0LwZH26hxos0GP3Dt19O379MJB837TdKKa87skq0zHaVLAquRHOBF77GI54Bc7O49d8aOrSu1VEFGMThlW2caspPRiTSePDMDPq7_WGk50izRhB3Asl9wmP9wEeaTrkJKRnQj5ips1SAZ1hDBsqEQKKukxP1HtdcopHV5_qgwU8Hjm5EwSLMluMQuiE6hwlkXGOujZLVizA', * aad: 'VGhlIEZlbGxvd3NoaXAgb2YgdGhlIFJpbmc', * protected: 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0', * } * * const { plaintext, protectedHeader, additionalAuthenticatedData } = * await jose.flattenedDecrypt(jwe, privateKey) * * console.log(protectedHeader) * const decoder = new TextDecoder() * console.log(decoder.decode(plaintext)) * console.log(decoder.decode(additionalAuthenticatedData)) * ``` * * @param jwe Flattened JWE. * @param key Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export function flattenedDecrypt( jwe: types.FlattenedJWE, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.DecryptOptions, ): Promise /** * @param jwe Flattened JWE. * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export function flattenedDecrypt( jwe: types.FlattenedJWE, getKey: FlattenedDecryptGetKey, options?: types.DecryptOptions, ): Promise export async function flattenedDecrypt( jwe: types.FlattenedJWE, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | FlattenedDecryptGetKey, options?: types.DecryptOptions, ) { if (!isObject(jwe)) { throw new JWEInvalid('Flattened JWE must be an object') } if (jwe.protected === undefined && jwe.header === undefined && jwe.unprotected === undefined) { throw new JWEInvalid('JOSE Header missing') } if (jwe.iv !== undefined && typeof jwe.iv !== 'string') { throw new JWEInvalid('JWE Initialization Vector incorrect type') } if (typeof jwe.ciphertext !== 'string') { throw new JWEInvalid('JWE Ciphertext missing or incorrect type') } if (jwe.tag !== undefined && typeof jwe.tag !== 'string') { throw new JWEInvalid('JWE Authentication Tag incorrect type') } if (jwe.protected !== undefined && typeof jwe.protected !== 'string') { throw new JWEInvalid('JWE Protected Header incorrect type') } if (jwe.encrypted_key !== undefined && typeof jwe.encrypted_key !== 'string') { throw new JWEInvalid('JWE Encrypted Key incorrect type') } if (jwe.aad !== undefined && typeof jwe.aad !== 'string') { throw new JWEInvalid('JWE AAD incorrect type') } if (jwe.header !== undefined && !isObject(jwe.header)) { throw new JWEInvalid('JWE Shared Unprotected Header incorrect type') } if (jwe.unprotected !== undefined && !isObject(jwe.unprotected)) { throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type') } let parsedProt!: types.JWEHeaderParameters | undefined if (jwe.protected) { try { const protectedHeader = b64u(jwe.protected) parsedProt = JSON.parse(decoder.decode(protectedHeader)) } catch { throw new JWEInvalid('JWE Protected Header is invalid') } } if (!isDisjoint(parsedProt, jwe.header, jwe.unprotected)) { throw new JWEInvalid( 'JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint', ) } const joseHeader: types.JWEHeaderParameters = { ...parsedProt, ...jwe.header, ...jwe.unprotected, } validateCrit(JWEInvalid, new Map(), options?.crit, parsedProt, joseHeader) if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') { throw new JOSENotSupported( 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.', ) } if (joseHeader.zip !== undefined && !parsedProt?.zip) { throw new JWEInvalid( 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', ) } const { alg, enc } = joseHeader if (typeof alg !== 'string' || !alg) { throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header') } if (typeof enc !== 'string' || !enc) { throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header') } const keyManagementAlgorithms = options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms) const contentEncryptionAlgorithms = options && validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms) if ( (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) || (!keyManagementAlgorithms && alg.startsWith('PBES2')) ) { throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed') } if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { throw new JOSEAlgNotAllowed('"enc" (Encryption Algorithm) Header Parameter value not allowed') } let encryptedKey!: Uint8Array if (jwe.encrypted_key !== undefined) { encryptedKey = decodeBase64url(jwe.encrypted_key!, 'encrypted_key', JWEInvalid) } let resolvedKey = false if (typeof key === 'function') { key = await key(parsedProt, jwe) resolvedKey = true } checkKeyType(alg === 'dir' ? enc : alg, key, 'decrypt') const k = await normalizeKey(key, alg) let cek: types.CryptoKey | Uint8Array try { cek = await decryptKeyManagement(alg, k, encryptedKey, joseHeader, options) } catch (err) { if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) { throw err } // https://www.rfc-editor.org/rfc/rfc7516#section-11.5 // To mitigate the attacks described in RFC 3218, the // recipient MUST NOT distinguish between format, padding, and length // errors of encrypted keys. It is strongly recommended, in the event // of receiving an improperly formatted key, that the recipient // substitute a randomly generated CEK and proceed to the next step, to // mitigate timing attacks. cek = generateCek(enc) } let iv: Uint8Array | undefined let tag: Uint8Array | undefined if (jwe.iv !== undefined) { iv = decodeBase64url(jwe.iv, 'iv', JWEInvalid) } if (jwe.tag !== undefined) { tag = decodeBase64url(jwe.tag, 'tag', JWEInvalid) } const protectedHeader: Uint8Array = jwe.protected !== undefined ? encode(jwe.protected) : new Uint8Array() let additionalData: Uint8Array if (jwe.aad !== undefined) { additionalData = concat(protectedHeader, encode('.'), encode(jwe.aad)) } else { additionalData = protectedHeader } const ciphertext = decodeBase64url(jwe.ciphertext, 'ciphertext', JWEInvalid) const plaintext = await decrypt(enc, cek, ciphertext, iv, tag, additionalData) const result: types.FlattenedDecryptResult = { plaintext } if (joseHeader.zip === 'DEF') { const maxDecompressedLength = options?.maxDecompressedLength ?? 250_000 if (maxDecompressedLength === 0) { throw new JOSENotSupported( 'JWE "zip" (Compression Algorithm) Header Parameter is not supported.', ) } if ( maxDecompressedLength !== Infinity && (!Number.isSafeInteger(maxDecompressedLength) || maxDecompressedLength < 1) ) { throw new TypeError('maxDecompressedLength must be 0, a positive safe integer, or Infinity') } result.plaintext = await decompress(plaintext, maxDecompressedLength).catch((cause) => { if (cause instanceof JWEInvalid) throw cause throw new JWEInvalid('Failed to decompress plaintext', { cause }) }) } if (jwe.protected !== undefined) { result.protectedHeader = parsedProt } if (jwe.aad !== undefined) { result.additionalAuthenticatedData = decodeBase64url(jwe.aad!, 'aad', JWEInvalid) } if (jwe.unprotected !== undefined) { result.sharedUnprotectedHeader = jwe.unprotected } if (jwe.header !== undefined) { result.unprotectedHeader = jwe.header } if (resolvedKey) { return { ...result, key: k } } return result } ================================================ FILE: src/jwe/flattened/encrypt.ts ================================================ /** * Encrypting JSON Web Encryption (JWE) in Flattened JSON Serialization * * @module */ import { encode as b64u } from '../../util/base64url.js' import { unprotected, assertNotSet } from '../../lib/helpers.js' import { encrypt } from '../../lib/content_encryption.js' import type * as types from '../../types.d.ts' import { encryptKeyManagement } from '../../lib/key_management.js' import { JOSENotSupported, JWEInvalid } from '../../util/errors.js' import { isDisjoint } from '../../lib/type_checks.js' import { concat, encode } from '../../lib/buffer_utils.js' import { validateCrit } from '../../lib/validate_crit.js' import { normalizeKey } from '../../lib/normalize_key.js' import { checkKeyType } from '../../lib/check_key_type.js' import { compress } from '../../lib/deflate.js' /** * The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwe/flattened/encrypt'`. * * @example * * ```js * const jwe = await new jose.FlattenedEncrypt( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' }) * .setAdditionalAuthenticatedData(encoder.encode('The Fellowship of the Ring')) * .encrypt(publicKey) * * console.log(jwe) * ``` */ export class FlattenedEncrypt { #plaintext: Uint8Array #protectedHeader!: types.JWEHeaderParameters | undefined #sharedUnprotectedHeader!: types.JWEHeaderParameters | undefined #unprotectedHeader!: types.JWEHeaderParameters | undefined #aad!: Uint8Array | undefined #cek!: Uint8Array | undefined #iv!: Uint8Array | undefined #keyManagementParameters?: types.JWEKeyManagementHeaderParameters /** * {@link FlattenedEncrypt} constructor * * @param plaintext Binary representation of the plaintext to encrypt. */ constructor(plaintext: Uint8Array) { if (!(plaintext instanceof Uint8Array)) { throw new TypeError('plaintext must be an instance of Uint8Array') } this.#plaintext = plaintext } /** * Sets the JWE Key Management parameters to be used when encrypting. * * (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement * PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. * * @param parameters JWE Key Management parameters. */ setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this { assertNotSet(this.#keyManagementParameters, 'setKeyManagementParameters') this.#keyManagementParameters = parameters return this } /** * Sets the JWE Protected Header on the FlattenedEncrypt object. * * @param protectedHeader JWE Protected Header. */ setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.#protectedHeader, 'setProtectedHeader') this.#protectedHeader = protectedHeader return this } /** * Sets the JWE Shared Unprotected Header on the FlattenedEncrypt object. * * @param sharedUnprotectedHeader JWE Shared Unprotected Header. */ setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.#sharedUnprotectedHeader, 'setSharedUnprotectedHeader') this.#sharedUnprotectedHeader = sharedUnprotectedHeader return this } /** * Sets the JWE Per-Recipient Unprotected Header on the FlattenedEncrypt object. * * @param unprotectedHeader JWE Per-Recipient Unprotected Header. */ setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader') this.#unprotectedHeader = unprotectedHeader return this } /** * Sets the Additional Authenticated Data on the FlattenedEncrypt object. * * @param aad Additional Authenticated Data. */ setAdditionalAuthenticatedData(aad: Uint8Array): this { this.#aad = aad return this } /** * Sets a content encryption key to use, by default a random suitable one is generated for the JWE * enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param cek JWE Content Encryption Key. */ setContentEncryptionKey(cek: Uint8Array): this { assertNotSet(this.#cek, 'setContentEncryptionKey') this.#cek = cek return this } /** * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param iv JWE Initialization Vector. */ setInitializationVector(iv: Uint8Array): this { assertNotSet(this.#iv, 'setInitializationVector') this.#iv = iv return this } /** * Encrypts and resolves the value of the Flattened JWE object. * * @param key Public Key or Secret to encrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Encryption options. */ async encrypt( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.EncryptOptions, ): Promise { if (!this.#protectedHeader && !this.#unprotectedHeader && !this.#sharedUnprotectedHeader) { throw new JWEInvalid( 'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()', ) } if ( !isDisjoint(this.#protectedHeader, this.#unprotectedHeader, this.#sharedUnprotectedHeader) ) { throw new JWEInvalid( 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', ) } const joseHeader: types.JWEHeaderParameters = { ...this.#protectedHeader, ...this.#unprotectedHeader, ...this.#sharedUnprotectedHeader, } validateCrit(JWEInvalid, new Map(), options?.crit, this.#protectedHeader, joseHeader) if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') { throw new JOSENotSupported( 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.', ) } if (joseHeader.zip !== undefined && !this.#protectedHeader?.zip) { throw new JWEInvalid( 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', ) } const { alg, enc } = joseHeader if (typeof alg !== 'string' || !alg) { throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid') } if (typeof enc !== 'string' || !enc) { throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid') } let encryptedKey: Uint8Array | undefined if (this.#cek && (alg === 'dir' || alg === 'ECDH-ES')) { throw new TypeError( `setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`, ) } checkKeyType(alg === 'dir' ? enc : alg, key, 'encrypt') let cek: types.CryptoKey | Uint8Array { let parameters: { [propName: string]: unknown } | undefined const k = await normalizeKey(key, alg) ;({ cek, encryptedKey, parameters } = await encryptKeyManagement( alg, enc, k, this.#cek, this.#keyManagementParameters, )) if (parameters) { if (options && unprotected in options) { if (!this.#unprotectedHeader) { this.setUnprotectedHeader(parameters) } else { this.#unprotectedHeader = { ...this.#unprotectedHeader, ...parameters } } } else if (!this.#protectedHeader) { this.setProtectedHeader(parameters) } else { this.#protectedHeader = { ...this.#protectedHeader, ...parameters } } } } let additionalData: Uint8Array let protectedHeaderS: string let protectedHeaderB: Uint8Array let aadMember: string | undefined if (this.#protectedHeader) { protectedHeaderS = b64u(JSON.stringify(this.#protectedHeader)) protectedHeaderB = encode(protectedHeaderS) } else { protectedHeaderS = '' protectedHeaderB = new Uint8Array() } if (this.#aad) { aadMember = b64u(this.#aad) const aadMemberBytes = encode(aadMember) additionalData = concat(protectedHeaderB, encode('.'), aadMemberBytes) } else { additionalData = protectedHeaderB } let plaintext = this.#plaintext if (joseHeader.zip === 'DEF') { plaintext = await compress(plaintext).catch((cause) => { throw new JWEInvalid('Failed to compress plaintext', { cause }) }) } const { ciphertext, tag, iv } = await encrypt(enc, plaintext, cek, this.#iv, additionalData) const jwe: types.FlattenedJWE = { ciphertext: b64u(ciphertext), } if (iv) { jwe.iv = b64u(iv) } if (tag) { jwe.tag = b64u(tag) } if (encryptedKey) { jwe.encrypted_key = b64u(encryptedKey) } if (aadMember) { jwe.aad = aadMember } if (this.#protectedHeader) { jwe.protected = protectedHeaderS } if (this.#sharedUnprotectedHeader) { jwe.unprotected = this.#sharedUnprotectedHeader } if (this.#unprotectedHeader) { jwe.header = this.#unprotectedHeader } return jwe } } ================================================ FILE: src/jwe/general/decrypt.ts ================================================ /** * Decrypting JSON Web Encryption (JWE) in General JSON Serialization * * @module */ import { flattenedDecrypt } from '../flattened/decrypt.js' import { JWEDecryptionFailed, JWEInvalid } from '../../util/errors.js' import type * as types from '../../types.d.ts' import { isObject } from '../../lib/type_checks.js' /** * Interface for General JWE Decryption dynamic key resolution. No token components have been * verified at the time of this function call. */ export interface GeneralDecryptGetKey extends types.GetKeyFunction< types.JWEHeaderParameters, types.FlattenedJWE > {} /** * Decrypts a General JWE. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwe/general/decrypt'`. * * > [!NOTE]\ * > The function iterates over the `recipients` array in the General JWE and returns the decryption * > result of the first recipient entry that can be successfully decrypted. The result only contains * > the plaintext and headers of that successfully decrypted recipient entry. Other recipient entries * > in the General JWE are not validated, and their headers are not included in the returned result. * > Recipients of a General JWE should only rely on the returned (decrypted) data. * * @example * * ```js * const jwe = { * ciphertext: '9EzjFISUyoG-ifC2mSihfP0DPC80yeyrxhTzKt1C_VJBkxeBG0MI4Te61Pk45RAGubUvBpU9jm4', * iv: '8Fy7A_IuoX5VXG9s', * tag: 'W76IYV6arGRuDSaSyWrQNg', * aad: 'VGhlIEZlbGxvd3NoaXAgb2YgdGhlIFJpbmc', * protected: 'eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0', * recipients: [ * { * encrypted_key: * 'Z6eD4UK_yFb5ZoKvKkGAdqywEG_m0e4IYo0x8Vf30LAMJcsc-_zSgIeiF82teZyYi2YYduHKoqImk7MRnoPZOlEs0Q5BNK1OgBmSOhCE8DFyqh9Zh48TCTP6lmBQ52naqoUJFMtHzu-0LwZH26hxos0GP3Dt19O379MJB837TdKKa87skq0zHaVLAquRHOBF77GI54Bc7O49d8aOrSu1VEFGMThlW2caspPRiTSePDMDPq7_WGk50izRhB3Asl9wmP9wEeaTrkJKRnQj5ips1SAZ1hDBsqEQKKukxP1HtdcopHV5_qgwU8Hjm5EwSLMluMQuiE6hwlkXGOujZLVizA', * }, * ], * } * * const { plaintext, protectedHeader, additionalAuthenticatedData } = * await jose.generalDecrypt(jwe, privateKey) * * console.log(protectedHeader) * const decoder = new TextDecoder() * console.log(decoder.decode(plaintext)) * console.log(decoder.decode(additionalAuthenticatedData)) * ``` * * @param jwe General JWE. * @param key Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export function generalDecrypt( jwe: types.GeneralJWE, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.DecryptOptions, ): Promise /** * @param jwe General JWE. * @param getKey Function resolving Private Key or Secret to decrypt the JWE with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Decryption options. */ export function generalDecrypt( jwe: types.GeneralJWE, getKey: GeneralDecryptGetKey, options?: types.DecryptOptions, ): Promise export async function generalDecrypt( jwe: types.GeneralJWE, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | GeneralDecryptGetKey, options?: types.DecryptOptions, ) { if (!isObject(jwe)) { throw new JWEInvalid('General JWE must be an object') } if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) { throw new JWEInvalid('JWE Recipients missing or incorrect type') } if (!jwe.recipients.length) { throw new JWEInvalid('JWE Recipients has no members') } for (const recipient of jwe.recipients) { try { return await flattenedDecrypt( { aad: jwe.aad, ciphertext: jwe.ciphertext, encrypted_key: recipient.encrypted_key, header: recipient.header, iv: jwe.iv, protected: jwe.protected, tag: jwe.tag, unprotected: jwe.unprotected, }, key as Parameters[1], options, ) } catch { // } } throw new JWEDecryptionFailed() } ================================================ FILE: src/jwe/general/encrypt.ts ================================================ /** * Encrypting JSON Web Encryption (JWE) in General JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { FlattenedEncrypt } from '../flattened/encrypt.js' import { unprotected, assertNotSet } from '../../lib/helpers.js' import { JOSENotSupported, JWEInvalid } from '../../util/errors.js' import { generateCek } from '../../lib/content_encryption.js' import { isDisjoint } from '../../lib/type_checks.js' import { encryptKeyManagement } from '../../lib/key_management.js' import { encode as b64u } from '../../util/base64url.js' import { validateCrit } from '../../lib/validate_crit.js' import { normalizeKey } from '../../lib/normalize_key.js' import { checkKeyType } from '../../lib/check_key_type.js' /** Used to build General JWE object's individual recipients. */ export interface Recipient { /** * Sets the JWE Per-Recipient Unprotected Header on the Recipient object. * * @param unprotectedHeader JWE Per-Recipient Unprotected Header. */ setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): Recipient /** * Sets the JWE Key Management parameters to be used when encrypting. * * (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement * PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. * * @param parameters JWE Key Management parameters. */ setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): Recipient /** A shorthand for calling addRecipient() on the enclosing {@link GeneralEncrypt} instance */ addRecipient(...args: Parameters): Recipient /** A shorthand for calling encrypt() on the enclosing {@link GeneralEncrypt} instance */ encrypt(...args: Parameters): Promise /** Returns the enclosing {@link GeneralEncrypt} instance */ done(): GeneralEncrypt } class IndividualRecipient implements Recipient { #parent: GeneralEncrypt unprotectedHeader?: types.JWEHeaderParameters keyManagementParameters?: types.JWEKeyManagementHeaderParameters key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array options: types.CritOption constructor( enc: GeneralEncrypt, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options: types.CritOption, ) { this.#parent = enc this.key = key this.options = options } setUnprotectedHeader(unprotectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.unprotectedHeader, 'setUnprotectedHeader') this.unprotectedHeader = unprotectedHeader return this } setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this { assertNotSet(this.keyManagementParameters, 'setKeyManagementParameters') this.keyManagementParameters = parameters return this } addRecipient(...args: Parameters) { return this.#parent.addRecipient(...args) } encrypt(...args: Parameters) { return this.#parent.encrypt(...args) } done() { return this.#parent } } /** * The GeneralEncrypt class is used to build and encrypt General JWE objects. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwe/general/encrypt'`. * * @example * * ```js * const jwe = await new jose.GeneralEncrypt( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .setProtectedHeader({ enc: 'A256GCM' }) * .addRecipient(ecPublicKey) * .setUnprotectedHeader({ alg: 'ECDH-ES+A256KW' }) * .addRecipient(rsaPublicKey) * .setUnprotectedHeader({ alg: 'RSA-OAEP-384' }) * .encrypt() * * console.log(jwe) * ``` */ export class GeneralEncrypt { #plaintext: Uint8Array #recipients: IndividualRecipient[] = [] #protectedHeader!: types.JWEHeaderParameters #unprotectedHeader!: types.JWEHeaderParameters #aad!: Uint8Array /** * {@link GeneralEncrypt} constructor * * @param plaintext Binary representation of the plaintext to encrypt. */ constructor(plaintext: Uint8Array) { this.#plaintext = plaintext } /** * Adds an additional recipient for the General JWE object. * * @param key Public Key or Secret to encrypt the Content Encryption Key for the recipient with. * See {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Encryption options. */ addRecipient( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.CritOption, ): Recipient { const recipient = new IndividualRecipient(this, key, { crit: options?.crit }) this.#recipients.push(recipient) return recipient } /** * Sets the JWE Protected Header on the GeneralEncrypt object. * * @param protectedHeader JWE Protected Header object. */ setProtectedHeader(protectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.#protectedHeader, 'setProtectedHeader') this.#protectedHeader = protectedHeader return this } /** * Sets the JWE Shared Unprotected Header on the GeneralEncrypt object. * * @param sharedUnprotectedHeader JWE Shared Unprotected Header object. */ setSharedUnprotectedHeader(sharedUnprotectedHeader: types.JWEHeaderParameters): this { assertNotSet(this.#unprotectedHeader, 'setSharedUnprotectedHeader') this.#unprotectedHeader = sharedUnprotectedHeader return this } /** * Sets the Additional Authenticated Data on the GeneralEncrypt object. * * @param aad Additional Authenticated Data. */ setAdditionalAuthenticatedData(aad: Uint8Array): this { this.#aad = aad return this } /** Encrypts and resolves the value of the General JWE object. */ async encrypt(): Promise { if (!this.#recipients.length) { throw new JWEInvalid('at least one recipient must be added') } if (this.#recipients.length === 1) { const [recipient] = this.#recipients const flattened = await new FlattenedEncrypt(this.#plaintext) .setAdditionalAuthenticatedData(this.#aad) .setProtectedHeader(this.#protectedHeader) .setSharedUnprotectedHeader(this.#unprotectedHeader) .setUnprotectedHeader(recipient.unprotectedHeader!) .encrypt(recipient.key, { ...recipient.options }) const jwe: types.GeneralJWE = { ciphertext: flattened.ciphertext, iv: flattened.iv, recipients: [{}], tag: flattened.tag, } if (flattened.aad) jwe.aad = flattened.aad if (flattened.protected) jwe.protected = flattened.protected if (flattened.unprotected) jwe.unprotected = flattened.unprotected if (flattened.encrypted_key) jwe.recipients![0].encrypted_key = flattened.encrypted_key if (flattened.header) jwe.recipients![0].header = flattened.header return jwe } let enc!: string for (let i = 0; i < this.#recipients.length; i++) { const recipient = this.#recipients[i] if ( !isDisjoint(this.#protectedHeader, this.#unprotectedHeader, recipient.unprotectedHeader) ) { throw new JWEInvalid( 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', ) } const joseHeader = { ...this.#protectedHeader, ...this.#unprotectedHeader, ...recipient.unprotectedHeader, } const { alg } = joseHeader if (typeof alg !== 'string' || !alg) { throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid') } if (alg === 'dir' || alg === 'ECDH-ES') { throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient') } if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) { throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid') } if (!enc) { enc = joseHeader.enc } else if (enc !== joseHeader.enc) { throw new JWEInvalid( 'JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients', ) } validateCrit(JWEInvalid, new Map(), recipient.options.crit, this.#protectedHeader, joseHeader) if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') { throw new JOSENotSupported( 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.', ) } if (joseHeader.zip !== undefined && !this.#protectedHeader?.zip) { throw new JWEInvalid( 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', ) } } const cek = generateCek(enc) const jwe: types.GeneralJWE = { ciphertext: '', recipients: [], } for (let i = 0; i < this.#recipients.length; i++) { const recipient = this.#recipients[i] const target: Record = {} jwe.recipients!.push(target) if (i === 0) { const flattened = await new FlattenedEncrypt(this.#plaintext) .setAdditionalAuthenticatedData(this.#aad) .setContentEncryptionKey(cek) .setProtectedHeader(this.#protectedHeader) .setSharedUnprotectedHeader(this.#unprotectedHeader) .setUnprotectedHeader(recipient.unprotectedHeader!) .setKeyManagementParameters(recipient.keyManagementParameters!) .encrypt(recipient.key, { ...recipient.options, // @ts-expect-error [unprotected]: true, }) jwe.ciphertext = flattened.ciphertext jwe.iv = flattened.iv jwe.tag = flattened.tag if (flattened.aad) jwe.aad = flattened.aad if (flattened.protected) jwe.protected = flattened.protected if (flattened.unprotected) jwe.unprotected = flattened.unprotected target.encrypted_key = flattened.encrypted_key! if (flattened.header) target.header = flattened.header continue } const alg = recipient.unprotectedHeader?.alg! || this.#protectedHeader?.alg! || this.#unprotectedHeader?.alg! checkKeyType(alg === 'dir' ? enc : alg, recipient.key, 'encrypt') const k = await normalizeKey(recipient.key, alg) const { encryptedKey, parameters } = await encryptKeyManagement( alg, enc, k, cek, recipient.keyManagementParameters, ) target.encrypted_key = b64u(encryptedKey!) if (recipient.unprotectedHeader || parameters) target.header = { ...recipient.unprotectedHeader, ...parameters } } return jwe as types.GeneralJWE } } ================================================ FILE: src/jwk/embedded.ts ================================================ /** * Verification using a JWK Embedded in a JWS Header * * @module */ import type * as types from '../types.d.ts' import { importJWK } from '../key/import.js' import { isObject } from '../lib/type_checks.js' import { JWSInvalid } from '../util/errors.js' /** * EmbeddedJWK is an implementation of a GetKeyFunction intended to be used with the JWS/JWT verify * operations whenever you need to opt-in to verify signatures with a public key embedded in the * token's "jwk" (JSON Web Key) Header Parameter. It is recommended to combine this with the verify * function's `algorithms` option to define accepted JWS "alg" (Algorithm) Header Parameter values. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwk/embedded'`. * * @example * * ```js * const jwt = * 'eyJqd2siOnsiY3J2IjoiUC0yNTYiLCJ4IjoiVU05ZzVuS25aWFlvdldBbE03NmNMejl2VG96UmpfX0NIVV9kT2wtZ09vRSIsInkiOiJkczhhZVF3MWwyY0RDQTdiQ2tPTnZ3REtwWEFidFhqdnFDbGVZSDhXc19VIiwia3R5IjoiRUMifSwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSIsImlhdCI6MTYwNDU4MDc5NH0.60boak3_dErnW47ZPty1C0nrjeVq86EN_eK0GOq6K8w2OA0thKoBxFK4j-NuU9yZ_A9UKGxPT_G87DladBaV9g' * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, jose.EmbeddedJWK, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(protectedHeader) * console.log(payload) * ``` */ export async function EmbeddedJWK( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise { const joseHeader = { ...protectedHeader, ...token?.header, } if (!isObject(joseHeader.jwk)) { throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object') } const key = await importJWK({ ...joseHeader.jwk, ext: true }, joseHeader.alg!) if (key instanceof Uint8Array || key.type !== 'public') { throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key') } return key } ================================================ FILE: src/jwk/thumbprint.ts ================================================ /** * JSON Web Key Thumbprint and JSON Web Key Thumbprint URI * * @module */ import { digest } from '../lib/helpers.js' import { encode as b64u } from '../util/base64url.js' import { JOSENotSupported, JWKInvalid } from '../util/errors.js' import { encode } from '../lib/buffer_utils.js' import type * as types from '../types.d.ts' import { isKeyLike } from '../lib/is_key_like.js' import { isJWK } from '../lib/type_checks.js' import { exportJWK } from '../key/export.js' import { invalidKeyInput } from '../lib/invalid_key_input.js' const check = (value: unknown, description: string) => { if (typeof value !== 'string' || !value) { throw new JWKInvalid(`${description} missing or invalid`) } } /** * Calculates a base64url-encoded JSON Web Key (JWK) Thumbprint * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwk/thumbprint'`. * * @example * * ```js * const thumbprint = await jose.calculateJwkThumbprint({ * kty: 'EC', * crv: 'P-256', * x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo', * y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww', * }) * * console.log(thumbprint) * // 'w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8' * ``` * * @param key Key to calculate the thumbprint for. * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is * "sha256". * * @see {@link https://www.rfc-editor.org/rfc/rfc7638 RFC7638} */ export async function calculateJwkThumbprint( key: types.JWK | types.CryptoKey | types.KeyObject, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512', ): Promise { let jwk: types.JWK if (isJWK(key)) { jwk = key } else if (isKeyLike(key)) { jwk = await exportJWK(key) } else { throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key')) } digestAlgorithm ??= 'sha256' if ( digestAlgorithm !== 'sha256' && digestAlgorithm !== 'sha384' && digestAlgorithm !== 'sha512' ) { throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"') } let components: types.JWK switch (jwk.kty) { case 'AKP': check(jwk.alg, '"alg" (Algorithm) Parameter') check(jwk.pub, '"pub" (Public key) Parameter') components = { alg: jwk.alg, kty: jwk.kty, pub: jwk.pub } break case 'EC': check(jwk.crv, '"crv" (Curve) Parameter') check(jwk.x, '"x" (X Coordinate) Parameter') check(jwk.y, '"y" (Y Coordinate) Parameter') components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y } break case 'OKP': check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter') check(jwk.x, '"x" (Public Key) Parameter') components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x } break case 'RSA': check(jwk.e, '"e" (Exponent) Parameter') check(jwk.n, '"n" (Modulus) Parameter') components = { e: jwk.e, kty: jwk.kty, n: jwk.n } break case 'oct': check(jwk.k, '"k" (Key Value) Parameter') components = { k: jwk.k, kty: jwk.kty } break default: throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported') } const data = encode(JSON.stringify(components)) return b64u(await digest(digestAlgorithm, data)) } /** * Calculates a JSON Web Key (JWK) Thumbprint URI * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwk/thumbprint'`. * * @example * * ```js * const thumbprintUri = await jose.calculateJwkThumbprintUri({ * kty: 'EC', * crv: 'P-256', * x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo', * y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww', * }) * * console.log(thumbprintUri) * // 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8' * ``` * * @param key Key to calculate the thumbprint for. * @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint. Default is * "sha256". * * @see {@link https://www.rfc-editor.org/rfc/rfc9278 RFC9278} */ export async function calculateJwkThumbprintUri( key: types.CryptoKey | types.KeyObject | types.JWK, digestAlgorithm?: 'sha256' | 'sha384' | 'sha512', ): Promise { digestAlgorithm ??= 'sha256' const thumbprint = await calculateJwkThumbprint(key, digestAlgorithm) return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}` } ================================================ FILE: src/jwks/local.ts ================================================ /** * Verification using a JSON Web Key Set (JWKS) available locally * * @module */ import type * as types from '../types.d.ts' import { importJWK } from '../key/import.js' import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js' import { isObject } from '../lib/type_checks.js' function getKtyFromAlg(alg: unknown) { switch (typeof alg === 'string' && alg.slice(0, 2)) { case 'RS': case 'PS': return 'RSA' case 'ES': return 'EC' case 'Ed': return 'OKP' case 'ML': return 'AKP' default: throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set') } } interface Cache { [alg: string]: types.CryptoKey } function isJWKSLike(jwks: unknown): jwks is types.JSONWebKeySet { return ( jwks && typeof jwks === 'object' && // @ts-expect-error Array.isArray(jwks.keys) && // @ts-expect-error jwks.keys.every(isJWKLike) ) } function isJWKLike(key: unknown) { return isObject(key) } class LocalJWKSet { #jwks: types.JSONWebKeySet #cached: WeakMap = new WeakMap() constructor(jwks: unknown) { if (!isJWKSLike(jwks)) { throw new JWKSInvalid('JSON Web Key Set malformed') } this.#jwks = structuredClone(jwks) } jwks(): types.JSONWebKeySet { return this.#jwks } async getKey( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise { const { alg, kid } = { ...protectedHeader, ...token?.header } const kty = getKtyFromAlg(alg) const candidates = this.#jwks!.keys.filter((jwk) => { // filter keys based on the mapping of signature algorithms to Key Type let candidate = kty === jwk.kty // filter keys based on the JWK Key ID in the header if (candidate && typeof kid === 'string') { candidate = kid === jwk.kid } // filter keys based on the key's declared Algorithm if (candidate && (typeof jwk.alg === 'string' || kty === 'AKP')) { candidate = alg === jwk.alg } // filter keys based on the key's declared Public Key Use if (candidate && typeof jwk.use === 'string') { candidate = jwk.use === 'sig' } // filter keys based on the key's declared Key Operations if (candidate && Array.isArray(jwk.key_ops)) { candidate = jwk.key_ops.includes('verify') } // filter out non-applicable curves / sub types if (candidate) { switch (alg) { case 'ES256': candidate = jwk.crv === 'P-256' break case 'ES384': candidate = jwk.crv === 'P-384' break case 'ES512': candidate = jwk.crv === 'P-521' break case 'Ed25519': // Fall through case 'EdDSA': candidate = jwk.crv === 'Ed25519' break } } return candidate }) const { 0: jwk, length } = candidates if (length === 0) { throw new JWKSNoMatchingKey() } if (length !== 1) { const error = new JWKSMultipleMatchingKeys() const _cached = this.#cached error[Symbol.asyncIterator] = async function* () { for (const jwk of candidates) { try { yield await importWithAlgCache(_cached, jwk, alg!) } catch {} } } throw error } return importWithAlgCache(this.#cached, jwk, alg!) } } async function importWithAlgCache(cache: WeakMap, jwk: types.JWK, alg: string) { const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk)! if (cached[alg] === undefined) { const key = await importJWK({ ...jwk, ext: true }, alg) if (key instanceof Uint8Array || key.type !== 'public') { throw new JWKSInvalid('JSON Web Key Set members must be public keys') } cached[alg] = key } return cached[alg] } /** * Returns a function that resolves a JWS JOSE Header to a public key object from a locally stored, * or otherwise available, JSON Web Key Set. * * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key * Operations) Parameters (if they are present on the JWK). * * Only a single public key must match the selection process. As shown in the example below when * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt * verification in an iterative manner. * * > [!NOTE]\ * > The function's purpose is to resolve public keys used for verifying signatures and will not work * > for public encryption keys. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwks/local'`. * * @example * * ```js * const JWKS = jose.createLocalJWKSet({ * keys: [ * { * kty: 'RSA', * e: 'AQAB', * n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ', * alg: 'PS256', * }, * { * crv: 'P-256', * kty: 'EC', * x: 'ySK38C1jBdLwDsNWKzzBHqKYEE5Cgv-qjWvorUXk9fw', * y: '_LeQBw07cf5t57Iavn4j-BqJsAD1dpoz8gokd3sBsOo', * alg: 'ES256', * }, * ], * }) * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * console.log(protectedHeader) * console.log(payload) * ``` * * @example * * Opting-in to multiple JWKS matches using `createLocalJWKSet` * * ```js * const options = { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * } * const { payload, protectedHeader } = await jose * .jwtVerify(jwt, JWKS, options) * .catch(async (error) => { * if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { * for await (const publicKey of error) { * try { * return await jose.jwtVerify(jwt, publicKey, options) * } catch (innerError) { * if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { * continue * } * throw innerError * } * } * throw new jose.errors.JWSSignatureVerificationFailed() * } * * throw error * }) * console.log(protectedHeader) * console.log(payload) * ``` * * @param jwks JSON Web Key Set formatted object. */ export function createLocalJWKSet( jwks: types.JSONWebKeySet, ): ( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ) => Promise { const set = new LocalJWKSet(jwks) const localJWKSet = async ( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise => set.getKey(protectedHeader, token) Object.defineProperties(localJWKSet, { jwks: { value: () => structuredClone(set.jwks()), enumerable: false, configurable: false, writable: false, }, }) return localJWKSet } ================================================ FILE: src/jwks/remote.ts ================================================ /** * Verification using a JSON Web Key Set (JWKS) available on an HTTP(S) URL * * @module */ import type * as types from '../types.d.ts' import { JOSEError, JWKSNoMatchingKey, JWKSTimeout } from '../util/errors.js' import { createLocalJWKSet } from './local.js' import { isObject } from '../lib/type_checks.js' function isCloudflareWorkers() { return ( // @ts-ignore typeof WebSocketPair !== 'undefined' || // @ts-ignore (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') || // @ts-ignore (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel') ) } // An explicit user-agent in browser environment is a trigger for CORS preflight requests which // are not needed for our request, so we're omitting setting a default user-agent in browser // environments. let USER_AGENT: string // @ts-ignore if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) { const NAME = 'jose' const VERSION = 'v6.2.2' USER_AGENT = `${NAME}/${VERSION}` } /** * When passed to {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} this allows the resolver * to make use of advanced fetch configurations, HTTP Proxies, retry on network errors, etc. * * > [!NOTE]\ * > Known caveat: Expect Type-related issues when passing the inputs through to fetch-like modules, * > they hardly ever get their typings inline with actual fetch, you should `@ts-expect-error` them. * * @example * * Using [sindresorhus/ky](https://github.com/sindresorhus/ky) for retries and its hooks feature for * logging outgoing requests and their responses. * * ```ts * import ky from 'ky' * * let logRequest!: (request: Request) => void * let logResponse!: (request: Request, response: Response) => void * let logRetry!: (request: Request, error: Error, retryCount: number) => void * * const JWKS = jose.createRemoteJWKSet(url, { * [jose.customFetch]: (...args) => * ky(args[0], { * ...args[1], * hooks: { * beforeRequest: [ * (request) => { * logRequest(request) * }, * ], * beforeRetry: [ * ({ request, error, retryCount }) => { * logRetry(request, error, retryCount) * }, * ], * afterResponse: [ * (request, _, response) => { * logResponse(request, response) * }, * ], * }, * }), * }) * ``` * * @example * * Using [nodejs/undici](https://github.com/nodejs/undici) to detect and use HTTP proxies. * * ```ts * import * as undici from 'undici' * * // see https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent * let envHttpProxyAgent = new undici.EnvHttpProxyAgent() * * // @ts-ignore * const JWKS = jose.createRemoteJWKSet(url, { * [jose.customFetch]: (...args) => { * // @ts-ignore * return undici.fetch(args[0], { ...args[1], dispatcher: envHttpProxyAgent }) // prettier-ignore * }, * }) * ``` * * @example * * Using [nodejs/undici](https://github.com/nodejs/undici) to automatically retry network errors. * * ```ts * import * as undici from 'undici' * * // see https://undici.nodejs.org/#/docs/api/RetryAgent * let retryAgent = new undici.RetryAgent(new undici.Agent(), { * statusCodes: [], * errorCodes: [ * 'ECONNRESET', * 'ECONNREFUSED', * 'ENOTFOUND', * 'ENETDOWN', * 'ENETUNREACH', * 'EHOSTDOWN', * 'UND_ERR_SOCKET', * ], * }) * * // @ts-ignore * const JWKS = jose.createRemoteJWKSet(url, { * [jose.customFetch]: (...args) => { * // @ts-ignore * return undici.fetch(args[0], { ...args[1], dispatcher: retryAgent }) // prettier-ignore * }, * }) * ``` * * @example * * Using [nodejs/undici](https://github.com/nodejs/undici) to mock responses in tests. * * ```ts * import * as undici from 'undici' * * // see https://undici.nodejs.org/#/docs/api/MockAgent * let mockAgent = new undici.MockAgent() * mockAgent.disableNetConnect() * * // @ts-ignore * const JWKS = jose.createRemoteJWKSet(url, { * [jose.customFetch]: (...args) => { * // @ts-ignore * return undici.fetch(args[0], { ...args[1], dispatcher: mockAgent }) // prettier-ignore * }, * }) * ``` */ export const customFetch: unique symbol = Symbol() /** See {@link customFetch}. */ export type FetchImplementation = ( /** URL the request is being made sent to {@link !fetch} as the `resource` argument */ url: string, /** Options otherwise sent to {@link !fetch} as the `options` argument */ options: { /** HTTP Headers */ headers: Headers /** The {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods request method} */ method: 'GET' /** See {@link !Request.redirect} */ redirect: 'manual' signal: AbortSignal }, ) => Promise async function fetchJwks( url: string, headers: Headers, signal: AbortSignal, fetchImpl: FetchImplementation = fetch, ) { const response = await fetchImpl(url, { method: 'GET', signal, redirect: 'manual', headers, }).catch((err) => { if (err.name === 'TimeoutError') { throw new JWKSTimeout() } throw err }) if (response.status !== 200) { throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response') } try { return await response.json() } catch { throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON') } } /** * > [!WARNING]\ * > This option has security implications that must be understood, assessed for applicability, and * > accepted before use. It is critical that the JSON Web Key Set cache only be writable by your own * > code. * * This option is intended for cloud computing runtimes that cannot keep an in memory cache between * their code's invocations. Use in runtimes where an in memory cache between requests is available * is not desirable. * * When passed to {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} this allows the passed in * object to: * * - Serve as an initial value for the JSON Web Key Set that the module would otherwise need to * trigger an HTTP request for * - Have the JSON Web Key Set the function optionally ended up triggering an HTTP request for * assigned to it as properties * * The intended use pattern is: * * - Before verifying with {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} you pull the * previously cached object from a low-latency key-value store offered by the cloud computing * runtime it is executed on; * - Default to an empty object `{}` instead when there's no previously cached value; * - Pass it in as {@link RemoteJWKSetOptions[jwksCache]}; * - Afterwards, update the key-value storage if the {@link ExportedJWKSCache.uat `uat`} property of * the object has changed. * * @example * * ```ts * // Prerequisites * let url!: URL * let jwt!: string * let getPreviouslyCachedJWKS!: () => Promise * let storeNewJWKScache!: (cache: jose.ExportedJWKSCache) => Promise * * // Load JSON Web Key Set cache * const jwksCache: jose.JWKSCacheInput = (await getPreviouslyCachedJWKS()) || {} * const { uat } = jwksCache * * const JWKS = jose.createRemoteJWKSet(url, { * [jose.jwksCache]: jwksCache, * }) * * // Use JSON Web Key Set cache * await jose.jwtVerify(jwt, JWKS) * * if (uat !== jwksCache.uat) { * // Update JSON Web Key Set cache * await storeNewJWKScache(jwksCache) * } * ``` */ export const jwksCache: unique symbol = Symbol() /** Options for the remote JSON Web Key Set. */ export interface RemoteJWKSetOptions { /** * Timeout (in milliseconds) for the HTTP request. When reached the request will be aborted and * the verification will fail. Default is 5000 (5 seconds). */ timeoutDuration?: number /** * Duration (in milliseconds) for which no more HTTP requests will be triggered after a previous * successful fetch. Default is 30000 (30 seconds). */ cooldownDuration?: number /** * Maximum time (in milliseconds) between successful HTTP requests. Default is 600000 (10 * minutes). */ cacheMaxAge?: number | typeof Infinity /** Headers to be sent with the HTTP request. */ headers?: Record /** See {@link jwksCache}. */ [jwksCache]?: JWKSCacheInput /** See {@link customFetch}. */ [customFetch]?: FetchImplementation } /** See {@link jwksCache}. */ export interface ExportedJWKSCache { /** Current cached JSON Web Key Set */ jwks: types.JSONWebKeySet /** Last updated at timestamp (seconds since epoch) */ uat: number } /** See {@link jwksCache}. */ export type JWKSCacheInput = ExportedJWKSCache | Record function isFreshJwksCache(input: unknown, cacheMaxAge: number): input is ExportedJWKSCache { if (typeof input !== 'object' || input === null) { return false } if (!('uat' in input) || typeof input.uat !== 'number' || Date.now() - input.uat >= cacheMaxAge) { return false } if ( !('jwks' in input) || !isObject(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isObject) ) { return false } return true } class RemoteJWKSet { #url: URL #timeoutDuration: number #cooldownDuration: number #cacheMaxAge: number #jwksTimestamp?: number #pendingFetch?: Promise #headers: Headers #customFetch?: FetchImplementation #local!: ReturnType #cache?: JWKSCacheInput constructor(url: unknown, options?: RemoteJWKSetOptions) { if (!(url instanceof URL)) { throw new TypeError('url must be an instance of URL') } this.#url = new URL(url.href) this.#timeoutDuration = typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000 this.#cooldownDuration = typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000 this.#cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000 this.#headers = new Headers(options?.headers) if (USER_AGENT && !this.#headers.has('User-Agent')) { this.#headers.set('User-Agent', USER_AGENT) } if (!this.#headers.has('accept')) { this.#headers.set('accept', 'application/json') this.#headers.append('accept', 'application/jwk-set+json') } this.#customFetch = options?.[customFetch] if (options?.[jwksCache] !== undefined) { this.#cache = options?.[jwksCache] if (isFreshJwksCache(options?.[jwksCache], this.#cacheMaxAge)) { this.#jwksTimestamp = this.#cache.uat this.#local = createLocalJWKSet(this.#cache.jwks) } } } pendingFetch(): boolean { return !!this.#pendingFetch } coolingDown(): boolean { return typeof this.#jwksTimestamp === 'number' ? Date.now() < this.#jwksTimestamp + this.#cooldownDuration : false } fresh(): boolean { return typeof this.#jwksTimestamp === 'number' ? Date.now() < this.#jwksTimestamp + this.#cacheMaxAge : false } jwks(): types.JSONWebKeySet | undefined { // @ts-expect-error return this.#local?.jwks() } async getKey( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise { if (!this.#local || !this.fresh()) { await this.reload() } try { return await this.#local(protectedHeader, token) } catch (err) { if (err instanceof JWKSNoMatchingKey) { if (this.coolingDown() === false) { await this.reload() return this.#local(protectedHeader, token) } } throw err } } async reload() { // Do not assume a fetch created in another request reliably resolves // see https://github.com/panva/jose/issues/355 and https://github.com/panva/jose/issues/509 if (this.#pendingFetch && isCloudflareWorkers()) { this.#pendingFetch = undefined } this.#pendingFetch ||= fetchJwks( this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch, ) .then((json) => { this.#local = createLocalJWKSet(json as unknown as types.JSONWebKeySet) if (this.#cache) { this.#cache.uat = Date.now() this.#cache.jwks = json as unknown as types.JSONWebKeySet } this.#jwksTimestamp = Date.now() this.#pendingFetch = undefined }) .catch((err: Error) => { this.#pendingFetch = undefined throw err }) await this.#pendingFetch } } /** * Returns a function that resolves a JWS JOSE Header to a public key object downloaded from a * remote endpoint returning a JSON Web Key Set, that is, for example, an OAuth 2.0 or OIDC * jwks_uri. The JSON Web Key Set is fetched when no key matches the selection process but only as * frequently as the `cooldownDuration` option allows to prevent abuse. * * It uses the "alg" (JWS Algorithm) Header Parameter to determine the right JWK "kty" (Key Type), * then proceeds to match the JWK "kid" (Key ID) with one found in the JWS Header Parameters (if * there is one) while also respecting the JWK "use" (Public Key Use) and JWK "key_ops" (Key * Operations) Parameters (if they are present on the JWK). * * Only a single public key must match the selection process. As shown in the example below when * multiple keys get matched it is possible to opt-in to iterate over the matched keys and attempt * verification in an iterative manner. * * > [!NOTE]\ * > The function's purpose is to resolve public keys used for verifying signatures and will not work * > for public encryption keys. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwks/remote'`. * * @example * * ```js * const JWKS = jose.createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')) * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * console.log(protectedHeader) * console.log(payload) * ``` * * @example * * Opting-in to multiple JWKS matches using `createRemoteJWKSet` * * ```js * const options = { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * } * const { payload, protectedHeader } = await jose * .jwtVerify(jwt, JWKS, options) * .catch(async (error) => { * if (error?.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { * for await (const publicKey of error) { * try { * return await jose.jwtVerify(jwt, publicKey, options) * } catch (innerError) { * if (innerError?.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { * continue * } * throw innerError * } * } * throw new jose.errors.JWSSignatureVerificationFailed() * } * * throw error * }) * console.log(protectedHeader) * console.log(payload) * ``` * * @param url URL to fetch the JSON Web Key Set from. * @param options Options for the remote JSON Web Key Set. */ export function createRemoteJWKSet( url: URL, options?: RemoteJWKSetOptions, ): { ( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise /** @ignore */ coolingDown: boolean /** @ignore */ fresh: boolean /** @ignore */ reloading: boolean /** @ignore */ reload: () => Promise /** @ignore */ jwks: () => types.JSONWebKeySet | undefined } { const set = new RemoteJWKSet(url, options) const remoteJWKSet = async ( protectedHeader?: types.JWSHeaderParameters, token?: types.FlattenedJWSInput, ): Promise => set.getKey(protectedHeader, token) Object.defineProperties(remoteJWKSet, { coolingDown: { get: () => set.coolingDown(), enumerable: true, configurable: false, }, fresh: { get: () => set.fresh(), enumerable: true, configurable: false, }, reload: { value: () => set.reload(), enumerable: true, configurable: false, writable: false, }, reloading: { get: () => set.pendingFetch(), enumerable: true, configurable: false, }, jwks: { value: () => set.jwks(), enumerable: true, configurable: false, writable: false, }, }) // @ts-expect-error return remoteJWKSet } ================================================ FILE: src/jws/compact/sign.ts ================================================ /** * Signing JSON Web Signature (JWS) in Compact Serialization * * @module */ import type * as types from '../../types.d.ts' import { FlattenedSign } from '../flattened/sign.js' /** * The CompactSign class is used to build and sign Compact JWS strings. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jws/compact/sign'`. * * @example * * ```js * const jws = await new jose.CompactSign( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .setProtectedHeader({ alg: 'ES256' }) * .sign(privateKey) * * console.log(jws) * ``` */ export class CompactSign { #flattened: FlattenedSign /** * {@link CompactSign} constructor * * @param payload Binary representation of the payload to sign. */ constructor(payload: Uint8Array) { this.#flattened = new FlattenedSign(payload) } /** * Sets the JWS Protected Header on the CompactSign object. * * @param protectedHeader JWS Protected Header. */ setProtectedHeader(protectedHeader: types.CompactJWSHeaderParameters): this { this.#flattened.setProtectedHeader(protectedHeader) return this } /** * Signs and resolves the value of the Compact JWS string. * * @param key Private Key or Secret to sign the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Sign options. */ async sign( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.SignOptions, ): Promise { const jws = await this.#flattened.sign(key, options) if (jws.payload === undefined) { throw new TypeError('use the flattened module for creating JWS with b64: false') } return `${jws.protected}.${jws.payload}.${jws.signature}` } } ================================================ FILE: src/jws/compact/verify.ts ================================================ /** * Verifying JSON Web Signature (JWS) in Compact Serialization * * @module */ import type * as types from '../../types.d.ts' import { flattenedVerify } from '../flattened/verify.js' import { JWSInvalid } from '../../util/errors.js' import { decoder } from '../../lib/buffer_utils.js' /** * Interface for Compact JWS Verification dynamic key resolution. No token components have been * verified at the time of this function call. * * @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set. */ export interface CompactVerifyGetKey extends types.GenericGetKeyFunction< types.CompactJWSHeaderParameters, types.FlattenedJWSInput, types.CryptoKey | types.KeyObject | types.JWK | Uint8Array > {} /** * Verifies the signature and format of and afterwards decodes the Compact JWS. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jws/compact/verify'`. * * @example * * ```js * const jws = * 'eyJhbGciOiJFUzI1NiJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.kkAs_gPPxWMI3rHuVlxHaTPfDWDoqdI8jSvuSmqV-8IHIWXg9mcAeC9ggV-45ZHRbiRJ3obUIFo1rHphPA5URg' * * const { payload, protectedHeader } = await jose.compactVerify(jws, publicKey) * * console.log(protectedHeader) * console.log(new TextDecoder().decode(payload)) * ``` * * @param jws Compact JWS. * @param key Key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function compactVerify( jws: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.VerifyOptions, ): Promise /** * @param jws Compact JWS. * @param getKey Function resolving a key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function compactVerify( jws: string | Uint8Array, getKey: CompactVerifyGetKey, options?: types.VerifyOptions, ): Promise export async function compactVerify( jws: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | CompactVerifyGetKey, options?: types.VerifyOptions, ) { if (jws instanceof Uint8Array) { jws = decoder.decode(jws) } if (typeof jws !== 'string') { throw new JWSInvalid('Compact JWS must be a string or Uint8Array') } const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.') if (length !== 3) { throw new JWSInvalid('Invalid Compact JWS') } const verified = await flattenedVerify( { payload, protected: protectedHeader, signature }, key as Parameters[1], options, ) const result = { payload: verified.payload, protectedHeader: verified.protectedHeader! } if (typeof key === 'function') { return { ...result, key: verified.key } } return result } ================================================ FILE: src/jws/flattened/sign.ts ================================================ /** * Signing JSON Web Signature (JWS) in Flattened JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { encode as b64u } from '../../util/base64url.js' import { sign } from '../../lib/signing.js' import { isDisjoint } from '../../lib/type_checks.js' import { JWSInvalid } from '../../util/errors.js' import { concat, encode } from '../../lib/buffer_utils.js' import { checkKeyType } from '../../lib/check_key_type.js' import { validateCrit } from '../../lib/validate_crit.js' import { normalizeKey } from '../../lib/normalize_key.js' import { assertNotSet } from '../../lib/helpers.js' /** * The FlattenedSign class is used to build and sign Flattened JWS objects. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jws/flattened/sign'`. * * @example * * ```js * const jws = await new jose.FlattenedSign( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .setProtectedHeader({ alg: 'ES256' }) * .sign(privateKey) * * console.log(jws) * ``` */ export class FlattenedSign { #payload: Uint8Array #protectedHeader!: types.JWSHeaderParameters #unprotectedHeader!: types.JWSHeaderParameters /** * {@link FlattenedSign} constructor * * @param payload Binary representation of the payload to sign. */ constructor(payload: Uint8Array) { if (!(payload instanceof Uint8Array)) { throw new TypeError('payload must be an instance of Uint8Array') } this.#payload = payload } /** * Sets the JWS Protected Header on the FlattenedSign object. * * @param protectedHeader JWS Protected Header. */ setProtectedHeader(protectedHeader: types.JWSHeaderParameters): this { assertNotSet(this.#protectedHeader, 'setProtectedHeader') this.#protectedHeader = protectedHeader return this } /** * Sets the JWS Unprotected Header on the FlattenedSign object. * * @param unprotectedHeader JWS Unprotected Header. */ setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): this { assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader') this.#unprotectedHeader = unprotectedHeader return this } /** * Signs and resolves the value of the Flattened JWS object. * * @param key Private Key or Secret to sign the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Sign options. */ async sign( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.SignOptions, ): Promise { if (!this.#protectedHeader && !this.#unprotectedHeader) { throw new JWSInvalid( 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()', ) } if (!isDisjoint(this.#protectedHeader, this.#unprotectedHeader)) { throw new JWSInvalid( 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', ) } const joseHeader: types.JWSHeaderParameters = { ...this.#protectedHeader, ...this.#unprotectedHeader, } const extensions = validateCrit( JWSInvalid, new Map([['b64', true]]), options?.crit, this.#protectedHeader, joseHeader, ) let b64 = true if (extensions.has('b64')) { b64 = this.#protectedHeader.b64! if (typeof b64 !== 'boolean') { throw new JWSInvalid( 'The "b64" (base64url-encode payload) Header Parameter must be a boolean', ) } } const { alg } = joseHeader if (typeof alg !== 'string' || !alg) { throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') } checkKeyType(alg, key, 'sign') let payloadS: string let payloadB: Uint8Array if (b64) { payloadS = b64u(this.#payload) payloadB = encode(payloadS) } else { payloadB = this.#payload payloadS = '' } let protectedHeaderString: string let protectedHeaderBytes: Uint8Array if (this.#protectedHeader) { protectedHeaderString = b64u(JSON.stringify(this.#protectedHeader)) protectedHeaderBytes = encode(protectedHeaderString) } else { protectedHeaderString = '' protectedHeaderBytes = new Uint8Array() } const data = concat(protectedHeaderBytes, encode('.'), payloadB) const k = await normalizeKey(key, alg) const signature = await sign(alg, k, data) const jws: types.FlattenedJWS = { signature: b64u(signature), payload: payloadS, } if (this.#unprotectedHeader) { jws.header = this.#unprotectedHeader } if (this.#protectedHeader) { jws.protected = protectedHeaderString } return jws } } ================================================ FILE: src/jws/flattened/verify.ts ================================================ /** * Verifying JSON Web Signature (JWS) in Flattened JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { decode as b64u } from '../../util/base64url.js' import { verify } from '../../lib/signing.js' import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js' import { concat, encoder, decoder, encode } from '../../lib/buffer_utils.js' import { decodeBase64url } from '../../lib/helpers.js' import { isDisjoint } from '../../lib/type_checks.js' import { isObject } from '../../lib/type_checks.js' import { checkKeyType } from '../../lib/check_key_type.js' import { validateCrit } from '../../lib/validate_crit.js' import { validateAlgorithms } from '../../lib/validate_algorithms.js' import { normalizeKey } from '../../lib/normalize_key.js' /** * Interface for Flattened JWS Verification dynamic key resolution. No token components have been * verified at the time of this function call. * * @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set. */ export interface FlattenedVerifyGetKey extends types.GenericGetKeyFunction< types.JWSHeaderParameters | undefined, types.FlattenedJWSInput, types.CryptoKey | types.KeyObject | types.JWK | Uint8Array > {} /** * Verifies the signature and format of and afterwards decodes the Flattened JWS. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jws/flattened/verify'`. * * @example * * ```js * const decoder = new TextDecoder() * const jws = { * signature: * 'FVVOXwj6kD3DqdfD9yYqfT2W9jv-Nop4kOehp_DeDGNB5dQNSPRvntBY6xH3uxlCxE8na9d_kyhYOcanpDJ0EA', * payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', * protected: 'eyJhbGciOiJFUzI1NiJ9', * } * * const { payload, protectedHeader } = await jose.flattenedVerify(jws, publicKey) * * console.log(protectedHeader) * console.log(decoder.decode(payload)) * ``` * * @param jws Flattened JWS. * @param key Key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function flattenedVerify( jws: types.FlattenedJWSInput, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.VerifyOptions, ): Promise /** * @param jws Flattened JWS. * @param getKey Function resolving a key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function flattenedVerify( jws: types.FlattenedJWSInput, getKey: FlattenedVerifyGetKey, options?: types.VerifyOptions, ): Promise export async function flattenedVerify( jws: types.FlattenedJWSInput, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | FlattenedVerifyGetKey, options?: types.VerifyOptions, ) { if (!isObject(jws)) { throw new JWSInvalid('Flattened JWS must be an object') } if (jws.protected === undefined && jws.header === undefined) { throw new JWSInvalid('Flattened JWS must have either of the "protected" or "header" members') } if (jws.protected !== undefined && typeof jws.protected !== 'string') { throw new JWSInvalid('JWS Protected Header incorrect type') } if (jws.payload === undefined) { throw new JWSInvalid('JWS Payload missing') } if (typeof jws.signature !== 'string') { throw new JWSInvalid('JWS Signature missing or incorrect type') } if (jws.header !== undefined && !isObject(jws.header)) { throw new JWSInvalid('JWS Unprotected Header incorrect type') } let parsedProt: types.JWSHeaderParameters = {} if (jws.protected) { try { const protectedHeader = b64u(jws.protected) parsedProt = JSON.parse(decoder.decode(protectedHeader)) } catch { throw new JWSInvalid('JWS Protected Header is invalid') } } if (!isDisjoint(parsedProt, jws.header)) { throw new JWSInvalid( 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', ) } const joseHeader: types.JWSHeaderParameters = { ...parsedProt, ...jws.header, } const extensions = validateCrit( JWSInvalid, new Map([['b64', true]]), options?.crit, parsedProt, joseHeader, ) let b64 = true if (extensions.has('b64')) { b64 = parsedProt.b64! if (typeof b64 !== 'boolean') { throw new JWSInvalid( 'The "b64" (base64url-encode payload) Header Parameter must be a boolean', ) } } const { alg } = joseHeader if (typeof alg !== 'string' || !alg) { throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid') } const algorithms = options && validateAlgorithms('algorithms', options.algorithms) if (algorithms && !algorithms.has(alg)) { throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed') } if (b64) { if (typeof jws.payload !== 'string') { throw new JWSInvalid('JWS Payload must be a string') } } else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) { throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance') } let resolvedKey = false if (typeof key === 'function') { key = await key(parsedProt, jws) resolvedKey = true } checkKeyType(alg, key, 'verify') const data = concat( jws.protected !== undefined ? encode(jws.protected) : new Uint8Array(), encode('.'), typeof jws.payload === 'string' ? b64 ? encode(jws.payload) : encoder.encode(jws.payload) : jws.payload, ) const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid) const k = await normalizeKey(key, alg) const verified = await verify(alg, k, signature, data) if (!verified) { throw new JWSSignatureVerificationFailed() } let payload: Uint8Array if (b64) { payload = decodeBase64url(jws.payload as string, 'payload', JWSInvalid) } else if (typeof jws.payload === 'string') { payload = encoder.encode(jws.payload) } else { payload = jws.payload } const result: types.FlattenedVerifyResult = { payload } if (jws.protected !== undefined) { result.protectedHeader = parsedProt } if (jws.header !== undefined) { result.unprotectedHeader = jws.header } if (resolvedKey) { return { ...result, key: k } } return result } ================================================ FILE: src/jws/general/sign.ts ================================================ /** * Signing JSON Web Signature (JWS) in General JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { FlattenedSign } from '../flattened/sign.js' import { JWSInvalid } from '../../util/errors.js' import { assertNotSet } from '../../lib/helpers.js' /** Used to build General JWS object's individual signatures. */ export interface Signature { /** * Sets the JWS Protected Header on the Signature object. * * @param protectedHeader JWS Protected Header. */ setProtectedHeader(protectedHeader: types.JWSHeaderParameters): Signature /** * Sets the JWS Unprotected Header on the Signature object. * * @param unprotectedHeader JWS Unprotected Header. */ setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters): Signature /** A shorthand for calling addSignature() on the enclosing {@link GeneralSign} instance */ addSignature(...args: Parameters): Signature /** A shorthand for calling encrypt() on the enclosing {@link GeneralSign} instance */ sign(...args: Parameters): Promise /** Returns the enclosing {@link GeneralSign} instance */ done(): GeneralSign } class IndividualSignature implements Signature { #parent: GeneralSign protectedHeader?: types.JWSHeaderParameters unprotectedHeader?: types.JWSHeaderParameters options?: types.SignOptions key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array constructor( sig: GeneralSign, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.SignOptions, ) { this.#parent = sig this.key = key this.options = options } setProtectedHeader(protectedHeader: types.JWSHeaderParameters) { assertNotSet(this.protectedHeader, 'setProtectedHeader') this.protectedHeader = protectedHeader return this } setUnprotectedHeader(unprotectedHeader: types.JWSHeaderParameters) { assertNotSet(this.unprotectedHeader, 'setUnprotectedHeader') this.unprotectedHeader = unprotectedHeader return this } addSignature(...args: Parameters) { return this.#parent.addSignature(...args) } sign(...args: Parameters) { return this.#parent.sign(...args) } done() { return this.#parent } } /** * The GeneralSign class is used to build and sign General JWS objects. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jws/general/sign'`. * * @example * * ```js * const jws = await new jose.GeneralSign( * new TextEncoder().encode('It’s a dangerous business, Frodo, going out your door.'), * ) * .addSignature(ecPrivateKey) * .setProtectedHeader({ alg: 'ES256' }) * .addSignature(rsaPrivateKey) * .setProtectedHeader({ alg: 'PS256' }) * .sign() * * console.log(jws) * ``` */ export class GeneralSign { #payload: Uint8Array #signatures: IndividualSignature[] = [] /** * {@link GeneralSign} constructor * * @param payload Binary representation of the payload to sign. */ constructor(payload: Uint8Array) { this.#payload = payload } /** * Adds an additional signature for the General JWS object. * * @param key Private Key or Secret to sign the individual JWS signature with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Sign options. */ addSignature( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.SignOptions, ): Signature { const signature = new IndividualSignature(this, key, options) this.#signatures.push(signature) return signature } /** Signs and resolves the value of the General JWS object. */ async sign(): Promise { if (!this.#signatures.length) { throw new JWSInvalid('at least one signature must be added') } const jws: types.GeneralJWS = { signatures: [], payload: '', } for (let i = 0; i < this.#signatures.length; i++) { const signature = this.#signatures[i] const flattened = new FlattenedSign(this.#payload) flattened.setProtectedHeader(signature.protectedHeader!) flattened.setUnprotectedHeader(signature.unprotectedHeader!) const { payload, ...rest } = await flattened.sign(signature.key, signature.options) if (i === 0) { jws.payload = payload } else if (jws.payload !== payload) { throw new JWSInvalid('inconsistent use of JWS Unencoded Payload (RFC7797)') } jws.signatures.push(rest) } return jws } } ================================================ FILE: src/jws/general/verify.ts ================================================ /** * Verifying JSON Web Signature (JWS) in General JSON Serialization * * @module */ import type * as types from '../../types.d.ts' import { flattenedVerify } from '../flattened/verify.js' import { JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js' import { isObject } from '../../lib/type_checks.js' /** * Interface for General JWS Verification dynamic key resolution. No token components have been * verified at the time of this function call. * * @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set. */ export interface GeneralVerifyGetKey extends types.GenericGetKeyFunction< types.JWSHeaderParameters, types.FlattenedJWSInput, types.CryptoKey | types.KeyObject | types.JWK | Uint8Array > {} /** * Verifies the signature and format of and afterwards decodes the General JWS. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jws/general/verify'`. * * > [!NOTE]\ * > The function iterates over the `signatures` array in the General JWS and returns the verification * > result of the first signature entry that can be successfully verified. The result only contains * > the payload, protected header, and unprotected header of that successfully verified signature * > entry. Other signature entries in the General JWS are not validated, and their headers are not * > included in the returned result. Recipients of a General JWS should only rely on the returned * > (verified) data. * * @example * * ```js * const jws = { * payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', * signatures: [ * { * signature: * 'FVVOXwj6kD3DqdfD9yYqfT2W9jv-Nop4kOehp_DeDGNB5dQNSPRvntBY6xH3uxlCxE8na9d_kyhYOcanpDJ0EA', * protected: 'eyJhbGciOiJFUzI1NiJ9', * }, * ], * } * * const { payload, protectedHeader } = await jose.generalVerify(jws, publicKey) * * console.log(protectedHeader) * console.log(new TextDecoder().decode(payload)) * ``` * * @param jws General JWS. * @param key Key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function generalVerify( jws: types.GeneralJWSInput, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.VerifyOptions, ): Promise /** * @param jws General JWS. * @param getKey Function resolving a key to verify the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Verify options. */ export function generalVerify( jws: types.GeneralJWSInput, getKey: GeneralVerifyGetKey, options?: types.VerifyOptions, ): Promise export async function generalVerify( jws: types.GeneralJWSInput, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | GeneralVerifyGetKey, options?: types.VerifyOptions, ) { if (!isObject(jws)) { throw new JWSInvalid('General JWS must be an object') } if (!Array.isArray(jws.signatures) || !jws.signatures.every(isObject)) { throw new JWSInvalid('JWS Signatures missing or incorrect type') } for (const signature of jws.signatures) { try { return await flattenedVerify( { header: signature.header, payload: jws.payload, protected: signature.protected, signature: signature.signature, }, key as Parameters[1], options, ) } catch { // } } throw new JWSSignatureVerificationFailed() } ================================================ FILE: src/jwt/decrypt.ts ================================================ /** * JSON Web Token (JWT) Decryption (JWT is in JWE format) * * @module */ import type * as types from '../types.d.ts' import { compactDecrypt } from '../jwe/compact/decrypt.js' import { validateClaimsSet } from '../lib/jwt_claims_set.js' import { JWTClaimValidationFailed } from '../util/errors.js' /** Combination of JWE Decryption options and JWT Claims Set verification options. */ export interface JWTDecryptOptions extends types.DecryptOptions, types.JWTClaimVerificationOptions {} /** * Interface for JWT Decryption dynamic key resolution. No token components have been verified at * the time of this function call. */ export interface JWTDecryptGetKey extends types.GetKeyFunction< types.CompactJWEHeaderParameters, types.FlattenedJWE > {} /** * Verifies the JWT format (to be a JWE Compact format), decrypts the ciphertext, validates the JWT * Claims Set. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwt/decrypt'`. * * @example * * ```js * const secret = jose.base64url.decode('zH4NRP1HMALxxCFnRZABFA7GOJtzU_gIj02alfL1lvI') * const jwt = * 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..MB66qstZBPxAXKdsjet_lA.WHbtJTl4taHp7otOHLq3hBvv0yNPsPEKHYInmCPdDDeyV1kU-f-tGEiU4FxlSqkqAT2hVs8_wMNiQFAzPU1PUgIqWCPsBrPP3TtxYsrtwagpn4SvCsUsx0Mhw9ZhliAO8CLmCBQkqr_T9AcYsz5uZw.7nX9m7BGUu_u1p1qFHzyIg' * * const { payload, protectedHeader } = await jose.jwtDecrypt(jwt, secret, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(protectedHeader) * console.log(payload) * ``` * * @param jwt JSON Web Token value (encoded as JWE). * @param key Private Key or Secret to decrypt and verify the JWT with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ export async function jwtDecrypt( jwt: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: JWTDecryptOptions, ): Promise> /** * @param jwt JSON Web Token value (encoded as JWE). * @param getKey Function resolving Private Key or Secret to decrypt and verify the JWT with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ export async function jwtDecrypt( jwt: string | Uint8Array, getKey: JWTDecryptGetKey, options?: JWTDecryptOptions, ): Promise & types.ResolvedKey> export async function jwtDecrypt( jwt: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | JWTDecryptGetKey, options?: JWTDecryptOptions, ) { const decrypted = await compactDecrypt(jwt, key as Parameters[1], options) const payload = validateClaimsSet(decrypted.protectedHeader, decrypted.plaintext, options) const { protectedHeader } = decrypted if (protectedHeader.iss !== undefined && protectedHeader.iss !== payload.iss) { throw new JWTClaimValidationFailed( 'replicated "iss" claim header parameter mismatch', payload, 'iss', 'mismatch', ) } if (protectedHeader.sub !== undefined && protectedHeader.sub !== payload.sub) { throw new JWTClaimValidationFailed( 'replicated "sub" claim header parameter mismatch', payload, 'sub', 'mismatch', ) } if ( protectedHeader.aud !== undefined && JSON.stringify(protectedHeader.aud) !== JSON.stringify(payload.aud) ) { throw new JWTClaimValidationFailed( 'replicated "aud" claim header parameter mismatch', payload, 'aud', 'mismatch', ) } const result = { payload, protectedHeader } if (typeof key === 'function') { return { ...result, key: decrypted.key } } return result } ================================================ FILE: src/jwt/encrypt.ts ================================================ /** * JSON Web Token (JWT) Encryption (JWT is in JWE format) * * @module */ import type * as types from '../types.d.ts' import { CompactEncrypt } from '../jwe/compact/encrypt.js' import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js' import { assertNotSet } from '../lib/helpers.js' /** * The EncryptJWT class is used to build and encrypt Compact JWE formatted JSON Web Tokens. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwt/encrypt'`. * * @example * * ```js * const secret = jose.base64url.decode('zH4NRP1HMALxxCFnRZABFA7GOJtzU_gIj02alfL1lvI') * const jwt = await new jose.EncryptJWT({ 'urn:example:claim': true }) * .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256' }) * .setIssuedAt() * .setIssuer('urn:example:issuer') * .setAudience('urn:example:audience') * .setExpirationTime('2h') * .encrypt(secret) * * console.log(jwt) * ``` */ export class EncryptJWT implements types.ProduceJWT { #cek!: Uint8Array #iv!: Uint8Array #keyManagementParameters!: types.JWEKeyManagementHeaderParameters #protectedHeader!: types.CompactJWEHeaderParameters #replicateIssuerAsHeader!: boolean #replicateSubjectAsHeader!: boolean #replicateAudienceAsHeader!: boolean #jwt: JWTClaimsBuilder /** * {@link EncryptJWT} constructor * * @param payload The JWT Claims Set object. Defaults to an empty object. */ constructor(payload: types.JWTPayload = {}) { this.#jwt = new JWTClaimsBuilder(payload) } setIssuer(issuer: string): this { this.#jwt.iss = issuer return this } setSubject(subject: string): this { this.#jwt.sub = subject return this } setAudience(audience: string | string[]): this { this.#jwt.aud = audience return this } setJti(jwtId: string): this { this.#jwt.jti = jwtId return this } setNotBefore(input: number | string | Date): this { this.#jwt.nbf = input return this } setExpirationTime(input: number | string | Date): this { this.#jwt.exp = input return this } setIssuedAt(input?: number | string | Date): this { this.#jwt.iat = input return this } /** * Sets the JWE Protected Header on the EncryptJWT object. * * @param protectedHeader JWE Protected Header. Must contain an "alg" (JWE Algorithm) and "enc" * (JWE Encryption Algorithm) properties. */ setProtectedHeader(protectedHeader: types.CompactJWEHeaderParameters): this { assertNotSet(this.#protectedHeader, 'setProtectedHeader') this.#protectedHeader = protectedHeader return this } /** * Sets the JWE Key Management parameters to be used when encrypting. * * (ECDH-ES) Use of this method is needed for ECDH based algorithms to set the "apu" (Agreement * PartyUInfo) or "apv" (Agreement PartyVInfo) parameters. * * @param parameters JWE Key Management parameters. */ setKeyManagementParameters(parameters: types.JWEKeyManagementHeaderParameters): this { assertNotSet(this.#keyManagementParameters, 'setKeyManagementParameters') this.#keyManagementParameters = parameters return this } /** * Sets a content encryption key to use, by default a random suitable one is generated for the JWE * enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param cek JWE Content Encryption Key. */ setContentEncryptionKey(cek: Uint8Array): this { assertNotSet(this.#cek, 'setContentEncryptionKey') this.#cek = cek return this } /** * Sets the JWE Initialization Vector to use for content encryption, by default a random suitable * one is generated for the JWE enc" (Encryption Algorithm) Header Parameter. * * @deprecated You should not use this method. It is only really intended for test and vector * validation purposes. * * @param iv JWE Initialization Vector. */ setInitializationVector(iv: Uint8Array): this { assertNotSet(this.#iv, 'setInitializationVector') this.#iv = iv return this } /** * Replicates the "iss" (Issuer) Claim as a JWE Protected Header Parameter. * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} */ replicateIssuerAsHeader(): this { this.#replicateIssuerAsHeader = true return this } /** * Replicates the "sub" (Subject) Claim as a JWE Protected Header Parameter. * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} */ replicateSubjectAsHeader(): this { this.#replicateSubjectAsHeader = true return this } /** * Replicates the "aud" (Audience) Claim as a JWE Protected Header Parameter. * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-5.3 RFC7519#section-5.3} */ replicateAudienceAsHeader(): this { this.#replicateAudienceAsHeader = true return this } /** * Encrypts and returns the JWT. * * @param key Public Key or Secret to encrypt the JWT with. See * {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}. * @param options JWE Encryption options. */ async encrypt( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.EncryptOptions, ): Promise { const enc = new CompactEncrypt(this.#jwt.data()) if ( this.#protectedHeader && (this.#replicateIssuerAsHeader || this.#replicateSubjectAsHeader || this.#replicateAudienceAsHeader) ) { this.#protectedHeader = { ...this.#protectedHeader, iss: this.#replicateIssuerAsHeader ? this.#jwt.iss : undefined, sub: this.#replicateSubjectAsHeader ? this.#jwt.sub : undefined, aud: this.#replicateAudienceAsHeader ? this.#jwt.aud : undefined, } } enc.setProtectedHeader(this.#protectedHeader) if (this.#iv) { enc.setInitializationVector(this.#iv) } if (this.#cek) { enc.setContentEncryptionKey(this.#cek) } if (this.#keyManagementParameters) { enc.setKeyManagementParameters(this.#keyManagementParameters) } return enc.encrypt(key, options) } } ================================================ FILE: src/jwt/sign.ts ================================================ /** * JSON Web Token (JWT) Signing (JWT is in JWS format) * * @module */ import { CompactSign } from '../jws/compact/sign.js' import { JWTInvalid } from '../util/errors.js' import type * as types from '../types.d.ts' import { JWTClaimsBuilder } from '../lib/jwt_claims_set.js' /** * The SignJWT class is used to build and sign Compact JWS formatted JSON Web Tokens. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwt/sign'`. * * @example * * Usage with a symmetric secret * * ```js * const secret = new TextEncoder().encode( * 'cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2', * ) * const alg = 'HS256' * * const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) * .setProtectedHeader({ alg }) * .setIssuedAt() * .setIssuer('urn:example:issuer') * .setAudience('urn:example:audience') * .setExpirationTime('2h') * .sign(secret) * * console.log(jwt) * ``` * * @example * * Usage with a private PKCS#8 encoded RSA key * * ```js * const alg = 'RS256' * const pkcs8 = `-----BEGIN PRIVATE KEY----- * MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCFg4UrY5xtulv * /NXKmL1J4qI1SopAfTNMo3X7p+kJO7plqUYjzaztcre1qfh0m33Sm1Q8oPbO/GpP * MU1/HgcceytgJ/b4UwufVVMl9BrMDYG8moDBylbVupFQS3Ly1L9i/iFG9Z9A9xzY * Zzf799A45bnvNXL6s2glzvjiRvfQ2NDF0anTcnZLcYtC7ugq1IMM+ihAcPfw8Qw2 * chN/SmP4qAM+PKaQwagmU7doqmmyN9u38AfoYZ1GCFhEs5TBBT6H6h9YdHeVtiIq * 1c+fl03biSIfLrV7dUBD39gBmXBcL/30Ya3D82mCEUC4zg/UkOfQOmkmV3Lc8YUL * QZ8EJkBLAgMBAAECggEAVuVE/KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34ls * VOPK0XDegZkhAybMZHjRhp+gwVxX5ChC+J3cUpOBH5FNxElgW6HizD2Jcq6t6LoL * YgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG/qJV1K00/Ly1G1QKoBffEs * +v4fAMJrCbUdCz1qWto+PU+HLMEo+krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq/ * aAP4a1SXmo+j0cvRU4W5Fj0RVwNesIpetX2ZFz4p/JmB5sWFEj/fC7h5z2lq+6Bm * e2T3BHtXkIxoBW0/pYVnASC8P2puO5FnVxDmWuHDYQKBgQDTuuBd3+0tSFVEX+DU * 5qpFmHm5nyGItZRJTS+71yg5pBxq1KqNCUjAtbxR0q//fwauakh+BwRVCPOrqsUG * jBSb3NYE70Srp6elqxgkE54PwQx4Mr6exJPnseM9U4K+hULllf5yjM9edreJE1nV * NVgFjeyafQhrHKwgr7PERJ/ikwKBgQDqqsT1M+EJLmI1HtCspOG6cu7q3gf/wKRh * E8tu84i3YyBnI8uJkKy92RNVI5fvpBARe3tjSdM25rr2rcrcmF/5g6Q9ImxZPGCt * 86eOgO9ErNtbc4TEgybsP319UE4O41aKeNiBTAZKoYCxv/dMqG0j4avmWzd+foHq * gSNUvR2maQKBgQCYeqOsV2B6VPY7KIVFLd0AA9/dwvEmgAYLiA/RShDI+hwQ/5jX * uxDu37KAhqeC65sHLrmIMUt4Zdr+DRyZK3aIDNEAesPMjw/X6lCXYp1ZISD2yyym * MFGH8X8CIkstI9Faf9vf6PJKSFrC1/HA7wq17VCwrUzLvrljTMW8meM/CwKBgCpo * 2leGHLFQFKeM/iF1WuYbR1pi7gcmhY6VyTowARFDdOOu8GXYI5/bz0afvCGvAMho * DJCREv7lC/zww6zCTPYG+HOj+PjXlJFba3ixjIxYwPvyEJiDK1Ge18sB7Fl8dHNq * C5ayaqCqN1voWYUdGzxU2IA1E/5kVo5O8FesJeOhAoGBAImJbZFf+D5kA32Xxhac * 59lLWBCsocvvbd1cvDMNlRywAAyhsCb1SuX4nEAK9mrSBdfmoF2Nm3eilfsOds0f * K5mX069IKG82CMqh3Mzptd7e7lyb9lsoGO0BAtjho3cWtha/UZ70vfaMzGuZ6JmQ * ak6k+8+UFd93M4z0Qo74OhXB * -----END PRIVATE KEY-----` * const privateKey = await jose.importPKCS8(pkcs8, alg) * * const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) * .setProtectedHeader({ alg }) * .setIssuedAt() * .setIssuer('urn:example:issuer') * .setAudience('urn:example:audience') * .setExpirationTime('2h') * .sign(privateKey) * * console.log(jwt) * ``` * * @example * * Usage with a private JWK encoded RSA key * * ```js * const alg = 'RS256' * const jwk = { * kty: 'RSA', * n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw', * e: 'AQAB', * d: 'VuVE_KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34lsVOPK0XDegZkhAybMZHjRhp-gwVxX5ChC-J3cUpOBH5FNxElgW6HizD2Jcq6t6LoLYgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG_qJV1K00_Ly1G1QKoBffEs-v4fAMJrCbUdCz1qWto-PU-HLMEo-krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq_aAP4a1SXmo-j0cvRU4W5Fj0RVwNesIpetX2ZFz4p_JmB5sWFEj_fC7h5z2lq-6Bme2T3BHtXkIxoBW0_pYVnASC8P2puO5FnVxDmWuHDYQ', * p: '07rgXd_tLUhVRF_g1OaqRZh5uZ8hiLWUSU0vu9coOaQcatSqjQlIwLW8UdKv_38GrmpIfgcEVQjzq6rFBowUm9zWBO9Eq6enpasYJBOeD8EMeDK-nsST57HjPVOCvoVC5ZX-cozPXna3iRNZ1TVYBY3smn0IaxysIK-zxESf4pM', * q: '6qrE9TPhCS5iNR7QrKThunLu6t4H_8CkYRPLbvOIt2MgZyPLiZCsvdkTVSOX76QQEXt7Y0nTNua69q3K3Jhf-YOkPSJsWTxgrfOnjoDvRKzbW3OExIMm7D99fVBODuNWinjYgUwGSqGAsb_3TKhtI-Gr5ls3fn6B6oEjVL0dpmk', * dp: 'mHqjrFdgelT2OyiFRS3dAAPf3cLxJoAGC4gP0UoQyPocEP-Y17sQ7t-ygIanguubBy65iDFLeGXa_g0cmSt2iAzRAHrDzI8P1-pQl2KdWSEg9ssspjBRh_F_AiJLLSPRWn_b3-jySkhawtfxwO8Kte1QsK1My765Y0zFvJnjPws', * dq: 'KmjaV4YcsVAUp4z-IXVa5htHWmLuByaFjpXJOjABEUN0467wZdgjn9vPRp-8Ia8AyGgMkJES_uUL_PDDrMJM9gb4c6P4-NeUkVtreLGMjFjA-_IQmIMrUZ7XywHsWXx0c2oLlrJqoKo3W-hZhR0bPFTYgDUT_mRWjk7wV6wl46E', * qi: 'iYltkV_4PmQDfZfGFpzn2UtYEKyhy-9t3Vy8Mw2VHLAADKGwJvVK5ficQAr2atIF1-agXY2bd6KV-w52zR8rmZfTr0gobzYIyqHczOm13t7uXJv2WygY7QEC2OGjdxa2Fr9RnvS99ozMa5nomZBqTqT7z5QV33czjPRCjvg6FcE', * } * const privateKey = await jose.importJWK(jwk, alg) * * const jwt = await new jose.SignJWT({ 'urn:example:claim': true }) * .setProtectedHeader({ alg }) * .setIssuedAt() * .setIssuer('urn:example:issuer') * .setAudience('urn:example:audience') * .setExpirationTime('2h') * .sign(privateKey) * * console.log(jwt) * ``` */ export class SignJWT implements types.ProduceJWT { #protectedHeader!: types.JWTHeaderParameters #jwt: JWTClaimsBuilder /** * {@link SignJWT} constructor * * @param payload The JWT Claims Set object. Defaults to an empty object. */ constructor(payload: types.JWTPayload = {}) { this.#jwt = new JWTClaimsBuilder(payload) } setIssuer(issuer: string): this { this.#jwt.iss = issuer return this } setSubject(subject: string): this { this.#jwt.sub = subject return this } setAudience(audience: string | string[]): this { this.#jwt.aud = audience return this } setJti(jwtId: string): this { this.#jwt.jti = jwtId return this } setNotBefore(input: number | string | Date): this { this.#jwt.nbf = input return this } setExpirationTime(input: number | string | Date): this { this.#jwt.exp = input return this } setIssuedAt(input?: number | string | Date): this { this.#jwt.iat = input return this } /** * Sets the JWS Protected Header on the SignJWT object. * * @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property. */ setProtectedHeader(protectedHeader: types.JWTHeaderParameters): this { this.#protectedHeader = protectedHeader return this } /** * Signs and returns the JWT. * * @param key Private Key or Secret to sign the JWT with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWT Sign options. */ async sign( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: types.SignOptions, ): Promise { const sig = new CompactSign(this.#jwt.data()) sig.setProtectedHeader(this.#protectedHeader) if ( Array.isArray(this.#protectedHeader?.crit) && this.#protectedHeader.crit.includes('b64') && // @ts-expect-error this.#protectedHeader.b64 === false ) { throw new JWTInvalid('JWTs MUST NOT use unencoded payload') } return sig.sign(key, options) } } ================================================ FILE: src/jwt/unsecured.ts ================================================ /** * Unsecured (unsigned & unencrypted) JSON Web Tokens (JWT) * * @module */ import * as b64u from '../util/base64url.js' import type * as types from '../types.d.ts' import { decoder } from '../lib/buffer_utils.js' import { JWTInvalid } from '../util/errors.js' import { validateClaimsSet, JWTClaimsBuilder } from '../lib/jwt_claims_set.js' /** Result of decoding an Unsecured JWT. */ export interface UnsecuredResult { payload: PayloadType & types.JWTPayload header: types.JWSHeaderParameters } /** * The UnsecuredJWT class is a utility for dealing with `{ "alg": "none" }` Unsecured JWTs. * * This class is exported (as a named export) from the main `'jose'` module entry point as well as * from its subpath export `'jose/jwt/unsecured'`. * * @example * * Encoding * * ```js * const unsecuredJwt = new jose.UnsecuredJWT({ 'urn:example:claim': true }) * .setIssuedAt() * .setIssuer('urn:example:issuer') * .setAudience('urn:example:audience') * .setExpirationTime('2h') * .encode() * * console.log(unsecuredJwt) * ``` * * @example * * Decoding * * ```js * const payload = jose.UnsecuredJWT.decode(unsecuredJwt, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(payload) * ``` */ export class UnsecuredJWT implements types.ProduceJWT { #jwt: JWTClaimsBuilder /** * {@link UnsecuredJWT} constructor * * @param payload The JWT Claims Set object. Defaults to an empty object. */ constructor(payload: types.JWTPayload = {}) { this.#jwt = new JWTClaimsBuilder(payload) } /** Encodes the Unsecured JWT. */ encode(): string { const header = b64u.encode(JSON.stringify({ alg: 'none' })) const payload = b64u.encode(this.#jwt.data()) return `${header}.${payload}.` } setIssuer(issuer: string): this { this.#jwt.iss = issuer return this } setSubject(subject: string): this { this.#jwt.sub = subject return this } setAudience(audience: string | string[]): this { this.#jwt.aud = audience return this } setJti(jwtId: string): this { this.#jwt.jti = jwtId return this } setNotBefore(input: number | string | Date): this { this.#jwt.nbf = input return this } setExpirationTime(input: number | string | Date): this { this.#jwt.exp = input return this } setIssuedAt(input?: number | string | Date): this { this.#jwt.iat = input return this } /** * Decodes an unsecured JWT. * * @param jwt Unsecured JWT to decode the payload of. * @param options JWT Claims Set validation options. */ static decode( jwt: string, options?: types.JWTClaimVerificationOptions, ): UnsecuredResult { if (typeof jwt !== 'string') { throw new JWTInvalid('Unsecured JWT must be a string') } const { 0: encodedHeader, 1: encodedPayload, 2: signature, length } = jwt.split('.') if (length !== 3 || signature !== '') { throw new JWTInvalid('Invalid Unsecured JWT') } let header: types.JWSHeaderParameters try { header = JSON.parse(decoder.decode(b64u.decode(encodedHeader))) if (header.alg !== 'none') throw new Error() } catch { throw new JWTInvalid('Invalid Unsecured JWT') } const payload = validateClaimsSet( header, b64u.decode(encodedPayload), options, ) as UnsecuredResult['payload'] return { payload, header } } } ================================================ FILE: src/jwt/verify.ts ================================================ /** * JSON Web Token (JWT) Verification (JWT is in JWS format) * * @module */ import { compactVerify } from '../jws/compact/verify.js' import type * as types from '../types.d.ts' import { validateClaimsSet } from '../lib/jwt_claims_set.js' import { JWTInvalid } from '../util/errors.js' /** Combination of JWS Verification options and JWT Claims Set verification options. */ export interface JWTVerifyOptions extends types.VerifyOptions, types.JWTClaimVerificationOptions {} /** * Interface for JWT Verification dynamic key resolution. No token components have been verified at * the time of this function call. * * @see {@link jwks/remote.createRemoteJWKSet createRemoteJWKSet} to verify using a remote JSON Web Key Set. */ export interface JWTVerifyGetKey extends types.GenericGetKeyFunction< types.JWTHeaderParameters, types.FlattenedJWSInput, types.CryptoKey | types.KeyObject | types.JWK | Uint8Array > {} /** * Verifies the JWT format (to be a JWS Compact format), verifies the JWS signature, validates the * JWT Claims Set. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwt/verify'`. * * @example * * Usage with a symmetric secret * * ```js * const secret = new TextEncoder().encode( * 'cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2', * ) * const jwt = * 'eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2MjMxLCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.C4iSlLfAUMBq--wnC6VqD9gEOhwpRZpoRarE0m7KEnI' * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, secret, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(protectedHeader) * console.log(payload) * ``` * * @example * * Usage with a public SPKI encoded RSA key * * ```js * const alg = 'RS256' * const spki = `-----BEGIN PUBLIC KEY----- * MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhYOFK2Ocbbpb/zVypi9 * SeKiNUqKQH0zTKN1+6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4H * HHsrYCf2+FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS/Yv4hRvWfQPcc2Gc3+/fQ * OOW57zVy+rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj * +KgDPjymkMGoJlO3aKppsjfbt/AH6GGdRghYRLOUwQU+h+ofWHR3lbYiKtXPn5dN * 24kiHy61e3VAQ9/YAZlwXC/99GGtw/NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZA * SwIDAQAB * -----END PUBLIC KEY-----` * const publicKey = await jose.importSPKI(spki, alg) * const jwt = * 'eyJhbGciOiJSUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2NDg4LCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.gXrPZ3yM_60dMXGE69dusbpzYASNA-XIOwsb5D5xYnSxyj6_D6OR_uR_1vqhUm4AxZxcrH1_-XJAve9HCw8az_QzHcN-nETt-v6stCsYrn6Bv1YOc-mSJRZ8ll57KVqLbCIbjKwerNX5r2_Qg2TwmJzQdRs-AQDhy-s_DlJd8ql6wR4n-kDZpar-pwIvz4fFIN0Fj57SXpAbLrV6Eo4Byzl0xFD8qEYEpBwjrMMfxCZXTlAVhAq6KCoGlDTwWuExps342-0UErEtyIqDnDGcrfNWiUsoo8j-29IpKd-w9-C388u-ChCxoHz--H8WmMSZzx3zTXsZ5lXLZ9IKfanDKg' * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(protectedHeader) * console.log(payload) * ``` * * @example * * Usage with a public JWK encoded RSA key * * ```js * const alg = 'RS256' * const jwk = { * kty: 'RSA', * n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw', * e: 'AQAB', * } * const publicKey = await jose.importJWK(jwk, alg) * const jwt = * 'eyJhbGciOiJSUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2NDg4LCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.gXrPZ3yM_60dMXGE69dusbpzYASNA-XIOwsb5D5xYnSxyj6_D6OR_uR_1vqhUm4AxZxcrH1_-XJAve9HCw8az_QzHcN-nETt-v6stCsYrn6Bv1YOc-mSJRZ8ll57KVqLbCIbjKwerNX5r2_Qg2TwmJzQdRs-AQDhy-s_DlJd8ql6wR4n-kDZpar-pwIvz4fFIN0Fj57SXpAbLrV6Eo4Byzl0xFD8qEYEpBwjrMMfxCZXTlAVhAq6KCoGlDTwWuExps342-0UErEtyIqDnDGcrfNWiUsoo8j-29IpKd-w9-C388u-ChCxoHz--H8WmMSZzx3zTXsZ5lXLZ9IKfanDKg' * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * * console.log(protectedHeader) * console.log(payload) * ``` * * @param jwt JSON Web Token value (encoded as JWS). * @param key Key to verify the JWT with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ export async function jwtVerify( jwt: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, options?: JWTVerifyOptions, ): Promise> /** * @example * * Usage with a public JSON Web Key Set hosted on a remote URL * * ```js * const JWKS = jose.createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')) * * const { payload, protectedHeader } = await jose.jwtVerify(jwt, JWKS, { * issuer: 'urn:example:issuer', * audience: 'urn:example:audience', * }) * console.log(protectedHeader) * console.log(payload) * ``` * * @param jwt JSON Web Token value (encoded as JWS). * @param getKey Function resolving a key to verify the JWT with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWT Decryption and JWT Claims Set validation options. */ export async function jwtVerify( jwt: string | Uint8Array, getKey: JWTVerifyGetKey, options?: JWTVerifyOptions, ): Promise & types.ResolvedKey> export async function jwtVerify( jwt: string | Uint8Array, key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array | JWTVerifyGetKey, options?: JWTVerifyOptions, ) { const verified = await compactVerify(jwt, key as Parameters[1], options) if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) { throw new JWTInvalid('JWTs MUST NOT use unencoded payload') } const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options) const result = { payload, protectedHeader: verified.protectedHeader } if (typeof key === 'function') { return { ...result, key: verified.key } } return result } ================================================ FILE: src/key/export.ts ================================================ /** * Cryptographic key export functions * * @module */ import { toSPKI as exportPublic, toPKCS8 as exportPrivate } from '../lib/asn1.js' import { keyToJWK } from '../lib/key_to_jwk.js' import type * as types from '../types.d.ts' /** * Exports a public {@link !CryptoKey} or {@link !KeyObject} to a PEM-encoded SPKI string format. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/export'`. * * @example * * ```js * const spkiPem = await jose.exportSPKI(publicKey) * * console.log(spkiPem) * ``` * * @param key Key to export to a PEM-encoded SPKI string format. */ export async function exportSPKI(key: types.CryptoKey | types.KeyObject): Promise { return exportPublic(key) } /** * Exports a private {@link !CryptoKey} or {@link !KeyObject} to a PEM-encoded PKCS8 string format. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/export'`. * * @example * * ```js * const pkcs8Pem = await jose.exportPKCS8(privateKey) * * console.log(pkcs8Pem) * ``` * * @param key Key to export to a PEM-encoded PKCS8 string format. */ export async function exportPKCS8(key: types.CryptoKey | types.KeyObject): Promise { return exportPrivate(key) } /** * Exports a {@link !CryptoKey}, {@link !KeyObject}, or {@link !Uint8Array} to a JWK. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/export'`. * * @example * * ```js * const privateJwk = await jose.exportJWK(privateKey) * const publicJwk = await jose.exportJWK(publicKey) * * console.log(privateJwk) * console.log(publicJwk) * ``` * * @param key Key to export as JWK. */ export async function exportJWK( key: types.CryptoKey | types.KeyObject | Uint8Array, ): Promise { return keyToJWK(key) } ================================================ FILE: src/key/generate_key_pair.ts ================================================ /** * Asymmetric key generation * * @module */ import { JOSENotSupported } from '../util/errors.js' import type * as types from '../types.d.ts' /** Asymmetric key pair generation function result. */ export interface GenerateKeyPairResult { /** The generated Private Key. */ privateKey: types.CryptoKey /** Public Key corresponding to the generated Private Key. */ publicKey: types.CryptoKey } /** Asymmetric key pair generation function options. */ export interface GenerateKeyPairOptions { /** * The EC "crv" (Curve) or OKP "crv" (Subtype of Key Pair) value to generate. The curve must be * both supported on the runtime as well as applicable for the given JWA algorithm identifier. */ crv?: string /** * A hint for RSA algorithms to generate an RSA key of a given `modulusLength` (Key size in bits). * JOSE requires 2048 bits or larger. Default is 2048. */ modulusLength?: number /** * The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false. * * @example * * ```js * const { publicKey, privateKey } = await jose.generateKeyPair('PS256', { * extractable: true, * }) * console.log(await jose.exportJWK(privateKey)) * console.log(await jose.exportPKCS8(privateKey)) * ``` */ extractable?: boolean } function getModulusLengthOption(options?: GenerateKeyPairOptions) { const modulusLength = options?.modulusLength ?? 2048 if (typeof modulusLength !== 'number' || modulusLength < 2048) { throw new JOSENotSupported( 'Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used', ) } return modulusLength } /** * Generates a private and a public key for a given JWA algorithm identifier. This can only generate * asymmetric key pairs. For symmetric secrets use the `generateSecret` function. * * > [!NOTE]\ * > The `privateKey` is generated with `extractable` set to `false` by default. See * > {@link GenerateKeyPairOptions.extractable} to generate an extractable `privateKey`. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/generate/keypair'`. * * @example * * ```js * const { publicKey, privateKey } = await jose.generateKeyPair('PS256') * console.log(publicKey) * console.log(privateKey) * ``` * * @param alg JWA Algorithm Identifier to be used with the generated key pair. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. * @param options Additional options passed down to the key pair generation. */ export async function generateKeyPair( alg: string, options?: GenerateKeyPairOptions, ): Promise { let algorithm: RsaHashedKeyGenParams | EcKeyGenParams | KeyAlgorithm let keyUsages: KeyUsage[] switch (alg) { case 'PS256': case 'PS384': case 'PS512': algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}`, publicExponent: Uint8Array.of(0x01, 0x00, 0x01), modulusLength: getModulusLengthOption(options), } keyUsages = ['sign', 'verify'] break case 'RS256': case 'RS384': case 'RS512': algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}`, publicExponent: Uint8Array.of(0x01, 0x00, 0x01), modulusLength: getModulusLengthOption(options), } keyUsages = ['sign', 'verify'] break case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': algorithm = { name: 'RSA-OAEP', hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, publicExponent: Uint8Array.of(0x01, 0x00, 0x01), modulusLength: getModulusLengthOption(options), } keyUsages = ['decrypt', 'unwrapKey', 'encrypt', 'wrapKey'] break case 'ES256': algorithm = { name: 'ECDSA', namedCurve: 'P-256' } keyUsages = ['sign', 'verify'] break case 'ES384': algorithm = { name: 'ECDSA', namedCurve: 'P-384' } keyUsages = ['sign', 'verify'] break case 'ES512': algorithm = { name: 'ECDSA', namedCurve: 'P-521' } keyUsages = ['sign', 'verify'] break case 'Ed25519': // Fall through case 'EdDSA': { keyUsages = ['sign', 'verify'] algorithm = { name: 'Ed25519' } break } case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': { keyUsages = ['sign', 'verify'] algorithm = { name: alg } break } case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': { keyUsages = ['deriveBits'] const crv = options?.crv ?? 'P-256' switch (crv) { case 'P-256': case 'P-384': case 'P-521': { algorithm = { name: 'ECDH', namedCurve: crv } break } case 'X25519': algorithm = { name: 'X25519' } break default: throw new JOSENotSupported( 'Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519', ) } break } default: throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') } return crypto.subtle.generateKey( algorithm, options?.extractable ?? false, keyUsages, ) as Promise } ================================================ FILE: src/key/generate_secret.ts ================================================ /** * Symmetric key generation * * @module */ import { JOSENotSupported } from '../util/errors.js' import type * as types from '../types.d.ts' /** Secret generation function options. */ export interface GenerateSecretOptions { /** * The value to use as {@link !SubtleCrypto.generateKey} `extractable` argument. Default is false. * * > [!NOTE]\ * > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as * > {@link !CryptoKey} this option has no effect for them. */ extractable?: boolean } /** * Generates a symmetric secret key for a given JWA algorithm identifier. * * > [!NOTE]\ * > The secret key is generated with `extractable` set to `false` by default. * * > [!NOTE]\ * > Because A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512 secrets cannot be represented as * > {@link !CryptoKey} this method yields a {@link !Uint8Array} for them instead. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/generate/secret'`. * * @example * * ```js * const secret = await jose.generateSecret('HS256') * console.log(secret) * ``` * * @param alg JWA Algorithm Identifier to be used with the generated secret. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. * @param options Additional options passed down to the secret generation. */ export async function generateSecret( alg: string, options?: GenerateSecretOptions, ): Promise { let length: number let algorithm: AesKeyGenParams | HmacKeyGenParams let keyUsages: KeyUsage[] switch (alg) { case 'HS256': case 'HS384': case 'HS512': length = parseInt(alg.slice(-3), 10) algorithm = { name: 'HMAC', hash: `SHA-${length}`, length } keyUsages = ['sign', 'verify'] break case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': length = parseInt(alg.slice(-3), 10) return crypto.getRandomValues(new Uint8Array(length >> 3)) case 'A128KW': case 'A192KW': case 'A256KW': length = parseInt(alg.slice(1, 4), 10) algorithm = { name: 'AES-KW', length } keyUsages = ['wrapKey', 'unwrapKey'] break case 'A128GCMKW': case 'A192GCMKW': case 'A256GCMKW': case 'A128GCM': case 'A192GCM': case 'A256GCM': length = parseInt(alg.slice(1, 4), 10) algorithm = { name: 'AES-GCM', length } keyUsages = ['encrypt', 'decrypt'] break default: throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value') } return crypto.subtle.generateKey(algorithm, options?.extractable ?? false, keyUsages) } ================================================ FILE: src/key/import.ts ================================================ /** * Cryptographic key import functions * * @module */ import { decode as decodeBase64URL } from '../util/base64url.js' import { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js' import { jwkToKey } from '../lib/jwk_to_key.js' import { JOSENotSupported } from '../util/errors.js' import { isObject } from '../lib/type_checks.js' import type * as types from '../types.d.ts' /** Key Import Function options. */ export interface KeyImportOptions { /** * The value to use as {@link !SubtleCrypto.importKey} `extractable` argument. Default is false for * private keys, true otherwise. */ extractable?: boolean } /** * Imports a PEM-encoded SPKI string as a {@link !CryptoKey}. * * > [!NOTE]\ * > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in * > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption * > (1.2.840.113549.1.1.1) instead for all RSA algorithms. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/import'`. * * @example * * ```js * const algorithm = 'ES256' * const spki = `-----BEGIN PUBLIC KEY----- * MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFlHHWfLk0gLBbsLTcuCrbCqoHqmM * YJepMC+Q+Dd6RBmBiA41evUsNMwLeN+PNFqib+xwi9JkJ8qhZkq8Y/IzGg== * -----END PUBLIC KEY-----` * const ecPublicKey = await jose.importSPKI(spki, algorithm) * ``` * * @param spki PEM-encoded SPKI string * @param alg JSON Web Algorithm identifier to be used with the imported key. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. */ export async function importSPKI( spki: string, alg: string, options?: KeyImportOptions, ): Promise { if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) { throw new TypeError('"spki" must be SPKI formatted string') } return fromSPKI(spki, alg, options) } /** * Imports the SPKI from an X.509 string certificate as a {@link !CryptoKey}. * * > [!NOTE]\ * > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in * > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption * > (1.2.840.113549.1.1.1) instead for all RSA algorithms. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/import'`. * * @example * * ```js * const algorithm = 'ES256' * const x509 = `-----BEGIN CERTIFICATE----- * MIIBXjCCAQSgAwIBAgIGAXvykuMKMAoGCCqGSM49BAMCMDYxNDAyBgNVBAMMK3Np * QXBNOXpBdk1VaXhXVWVGaGtjZXg1NjJRRzFyQUhXaV96UlFQTVpQaG8wHhcNMjEw * OTE3MDcwNTE3WhcNMjIwNzE0MDcwNTE3WjA2MTQwMgYDVQQDDCtzaUFwTTl6QXZN * VWl4V1VlRmhrY2V4NTYyUUcxckFIV2lfelJRUE1aUGhvMFkwEwYHKoZIzj0CAQYI * KoZIzj0DAQcDQgAE8PbPvCv5D5xBFHEZlBp/q5OEUymq7RIgWIi7tkl9aGSpYE35 * UH+kBKDnphJO3odpPZ5gvgKs2nwRWcrDnUjYLDAKBggqhkjOPQQDAgNIADBFAiEA * 1yyMTRe66MhEXID9+uVub7woMkNYd0LhSHwKSPMUUTkCIFQGsfm1ecXOpeGOufAh * v+A1QWZMuTWqYt+uh/YSRNDn * -----END CERTIFICATE-----` * const ecPublicKey = await jose.importX509(x509, algorithm) * ``` * * @param x509 X.509 certificate string * @param alg JSON Web Algorithm identifier to be used with the imported key. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. */ export async function importX509( x509: string, alg: string, options?: KeyImportOptions, ): Promise { if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) { throw new TypeError('"x509" must be X.509 formatted string') } return fromX509(x509, alg, options) } /** * Imports a PEM-encoded PKCS#8 string as a {@link !CryptoKey}. * * > [!NOTE]\ * > The OID id-RSASSA-PSS (1.2.840.113549.1.1.10) is not supported in * > {@link https://w3c.github.io/webcrypto/ Web Cryptography API}, use the OID rsaEncryption * > (1.2.840.113549.1.1.1) instead for all RSA algorithms. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/import'`. * * @example * * ```js * const algorithm = 'ES256' * const pkcs8 = `-----BEGIN PRIVATE KEY----- * MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiyvo0X+VQ0yIrOaN * nlrnUclopnvuuMfoc8HHly3505OhRANCAAQWUcdZ8uTSAsFuwtNy4KtsKqgeqYxg * l6kwL5D4N3pEGYGIDjV69Sw0zAt43480WqJv7HCL0mQnyqFmSrxj8jMa * -----END PRIVATE KEY-----` * const ecPrivateKey = await jose.importPKCS8(pkcs8, algorithm) * ``` * * @param pkcs8 PEM-encoded PKCS#8 string * @param alg JSON Web Algorithm identifier to be used with the imported key. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. */ export async function importPKCS8( pkcs8: string, alg: string, options?: KeyImportOptions, ): Promise { if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) { throw new TypeError('"pkcs8" must be PKCS#8 formatted string') } return fromPKCS8(pkcs8, alg, options) } /** * Imports a JWK to a {@link !CryptoKey}. Either the JWK "alg" (Algorithm) Parameter, or the optional * "alg" argument, must be present for asymmetric JSON Web Key imports. * * > [!NOTE]\ * > The JSON Web Key parameters "use", "key_ops", and "ext" are also used in the {@link !CryptoKey} * > import process. * * > [!NOTE]\ * > Symmetric JSON Web Keys (i.e. `kty: "oct"`) yield back an {@link !Uint8Array} instead of a * > {@link !CryptoKey}. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/key/import'`. * * @example * * ```js * const ecPublicKey = await jose.importJWK( * { * crv: 'P-256', * kty: 'EC', * x: 'ySK38C1jBdLwDsNWKzzBHqKYEE5Cgv-qjWvorUXk9fw', * y: '_LeQBw07cf5t57Iavn4j-BqJsAD1dpoz8gokd3sBsOo', * }, * 'ES256', * ) * * const rsaPublicKey = await jose.importJWK( * { * kty: 'RSA', * e: 'AQAB', * n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ', * }, * 'PS256', * ) * ``` * * @param jwk JSON Web Key. * @param alg JSON Web Algorithm identifier to be used with the imported key. Default is the "alg" * property on the JWK. See * {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements}. */ export async function importJWK( jwk: types.JWK, alg?: string, options?: KeyImportOptions, ): Promise { if (!isObject(jwk)) { throw new TypeError('JWK must be an object') } let ext: boolean | undefined alg ??= jwk.alg ext ??= options?.extractable ?? jwk.ext switch (jwk.kty) { case 'oct': if (typeof jwk.k !== 'string' || !jwk.k) { throw new TypeError('missing "k" (Key Value) Parameter value') } return decodeBase64URL(jwk.k) case 'RSA': if ('oth' in jwk && jwk.oth !== undefined) { throw new JOSENotSupported( 'RSA JWK "oth" (Other Primes Info) Parameter value is not supported', ) } return jwkToKey({ ...jwk, alg, ext }) case 'AKP': { if (typeof jwk.alg !== 'string' || !jwk.alg) { throw new TypeError('missing "alg" (Algorithm) Parameter value') } if (alg !== undefined && alg !== jwk.alg) { throw new TypeError('JWK alg and alg option value mismatch') } return jwkToKey({ ...jwk, ext }) } case 'EC': case 'OKP': return jwkToKey({ ...jwk, alg, ext }) default: throw new JOSENotSupported('Unsupported "kty" (Key Type) Parameter value') } } ================================================ FILE: src/lib/aesgcmkw.ts ================================================ import { encrypt, decrypt } from './content_encryption.js' import { encode as b64u } from '../util/base64url.js' export async function wrap(alg: string, key: unknown, cek: Uint8Array, iv?: Uint8Array) { const jweAlgorithm = alg.slice(0, 7) const wrapped = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array()) return { encryptedKey: wrapped.ciphertext, iv: b64u(wrapped.iv!), tag: b64u(wrapped.tag!), } } export async function unwrap( alg: string, key: unknown, encryptedKey: Uint8Array, iv: Uint8Array, tag: Uint8Array, ) { const jweAlgorithm = alg.slice(0, 7) return decrypt(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array()) } ================================================ FILE: src/lib/aeskw.ts ================================================ import type * as types from '../types.d.ts' import { checkEncCryptoKey } from './crypto_key.js' function checkKeySize(key: types.CryptoKey, alg: string) { if ((key.algorithm as AesKeyAlgorithm).length !== parseInt(alg.slice(1, 4), 10)) { throw new TypeError(`Invalid key size for alg: ${alg}`) } } function getCryptoKey(key: types.CryptoKey | Uint8Array, alg: string, usage: KeyUsage) { if (key instanceof Uint8Array) { return crypto.subtle.importKey('raw', key as Uint8Array, 'AES-KW', true, [usage]) } checkEncCryptoKey(key, alg, usage) return key } export async function wrap(alg: string, key: types.CryptoKey | Uint8Array, cek: Uint8Array) { const cryptoKey = await getCryptoKey(key, alg, 'wrapKey') checkKeySize(cryptoKey, alg) // algorithm used is irrelevant const cryptoKeyCek = await crypto.subtle.importKey( 'raw', cek as Uint8Array, { hash: 'SHA-256', name: 'HMAC' }, true, ['sign'], ) return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW')) } export async function unwrap( alg: string, key: types.CryptoKey | Uint8Array, encryptedKey: Uint8Array, ) { const cryptoKey = await getCryptoKey(key, alg, 'unwrapKey') checkKeySize(cryptoKey, alg) // algorithm used is irrelevant const cryptoKeyCek = await crypto.subtle.unwrapKey( 'raw', encryptedKey as Uint8Array, cryptoKey, 'AES-KW', { hash: 'SHA-256', name: 'HMAC' }, true, ['sign'], ) return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek)) } ================================================ FILE: src/lib/asn1.ts ================================================ import type * as types from '../types.d.ts' import { invalidKeyInput } from './invalid_key_input.js' import { encodeBase64, decodeBase64 } from '../lib/base64.js' import { JOSENotSupported } from '../util/errors.js' import { isCryptoKey, isKeyObject } from './is_key_like.js' import type { KeyImportOptions } from '../key/import.js' /** * Formats a base64 string as a PEM-encoded key with proper line breaks and headers. * * @param b64 - Base64-encoded key data * @param descriptor - Key type descriptor (e.g., "PUBLIC KEY", "PRIVATE KEY") * * @returns PEM-formatted string */ const formatPEM = (b64: string, descriptor: string) => { const newlined = (b64.match(/.{1,64}/g) || []).join('\n') return `-----BEGIN ${descriptor}-----\n${newlined}\n-----END ${descriptor}-----` } interface ExportOptions { format: 'pem' type: 'spki' | 'pkcs8' } interface ExtractableKeyObject extends types.KeyObject { export(arg: ExportOptions): string } const genericExport = async ( keyType: 'private' | 'public', keyFormat: 'spki' | 'pkcs8', key: unknown, ) => { if (isKeyObject(key)) { if (key.type !== keyType) { throw new TypeError(`key is not a ${keyType} key`) } return (key as ExtractableKeyObject).export({ format: 'pem', type: keyFormat }) } if (!isCryptoKey(key)) { throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject')) } if (!key.extractable) { throw new TypeError('CryptoKey is not extractable') } if (key.type !== keyType) { throw new TypeError(`key is not a ${keyType} key`) } return formatPEM( encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`, ) } export const toSPKI = (key: unknown): Promise => genericExport('public', 'spki', key) export const toPKCS8 = (key: unknown): Promise => genericExport('private', 'pkcs8', key) /** Helper function to compare two byte arrays for equality */ const bytesEqual = (a: Uint8Array, b: readonly number[]): boolean => { if (a.byteLength !== b.length) return false for (let i = 0; i < a.byteLength; i++) { if (a[i] !== b[i]) return false } return true } /** ASN.1 DER parsing state */ interface ASN1State { readonly data: Uint8Array pos: number } /** Creates ASN.1 parsing state */ const createASN1State = (data: Uint8Array): ASN1State => ({ data, pos: 0 }) /** Parses ASN.1 length encoding (both short and long form) */ const parseLength = (state: ASN1State): number => { const first = state.data[state.pos++] if (first & 0x80) { // Long form: first byte indicates number of subsequent length bytes const lengthOfLen = first & 0x7f let length = 0 for (let i = 0; i < lengthOfLen; i++) { length = (length << 8) | state.data[state.pos++] } return length } // Short form: length is encoded directly in first byte return first } /** Skips ASN.1 elements (tag + length + content) */ const skipElement = (state: ASN1State, count: number = 1): void => { if (count <= 0) return state.pos++ // Skip tag byte const length = parseLength(state) state.pos += length // Skip content bytes if (count > 1) { skipElement(state, count - 1) // Recursively skip remaining elements } } /** Expects a specific tag and throws if not found */ const expectTag = (state: ASN1State, expectedTag: number, errorMessage: string): void => { if (state.data[state.pos++] !== expectedTag) { throw new Error(errorMessage) } } /** Gets a subarray from current position */ const getSubarray = (state: ASN1State, length: number): Uint8Array => { const result = state.data.subarray(state.pos, state.pos + length) state.pos += length return result } /** Parses algorithm OID and returns the OID bytes */ const parseAlgorithmOID = (state: ASN1State): Uint8Array => { expectTag(state, 0x06, 'Expected algorithm OID') const oidLen = parseLength(state) return getSubarray(state, oidLen) } /** Parses a PKCS#8 private key structure up to the privateKey field */ function parsePKCS8Header(state: ASN1State) { // Parse outer SEQUENCE (PrivateKeyInfo) expectTag(state, 0x30, 'Invalid PKCS#8 structure') parseLength(state) // Skip outer length // Skip version (INTEGER) expectTag(state, 0x02, 'Expected version field') const verLen = parseLength(state) state.pos += verLen // Parse privateKeyAlgorithm (AlgorithmIdentifier SEQUENCE) expectTag(state, 0x30, 'Expected algorithm identifier') const algIdLen = parseLength(state) const algIdStart = state.pos return { algIdStart, algIdLength: algIdLen } } /** Parses an SPKI structure up to the subjectPublicKey field */ function parseSPKIHeader(state: ASN1State) { // Parse outer SEQUENCE (SubjectPublicKeyInfo) expectTag(state, 0x30, 'Invalid SPKI structure') parseLength(state) // Skip outer length // Parse algorithm identifier (AlgorithmIdentifier SEQUENCE) expectTag(state, 0x30, 'Expected algorithm identifier') const algIdLen = parseLength(state) const algIdStart = state.pos return { algIdStart, algIdLength: algIdLen } } /** Parses algorithm identifier and returns curve name for EC/ECDH keys */ const parseECAlgorithmIdentifier = (state: ASN1State): string => { const algOid = parseAlgorithmOID(state) // id-x25519 if (bytesEqual(algOid, [0x2b, 0x65, 0x6e])) { return 'X25519' } // id-ecPublicKey 1.2.840.10045.2.1 if (!bytesEqual(algOid, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01])) { throw new Error('Unsupported key algorithm') } // Parse curve parameters (should be an OID for named curves) expectTag(state, 0x06, 'Expected curve OID') const curveOidLen = parseLength(state) const curveOid = getSubarray(state, curveOidLen) // Compare with known curve OIDs - NIST curves inlined for (const { name, oid } of [ { name: 'P-256', oid: [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07] }, // 1.2.840.10045.3.1.7 { name: 'P-384', oid: [0x2b, 0x81, 0x04, 0x00, 0x22] }, // 1.3.132.0.34 { name: 'P-521', oid: [0x2b, 0x81, 0x04, 0x00, 0x23] }, // 1.3.132.0.35 ] as const) { if (bytesEqual(curveOid, oid)) { return name } } throw new Error('Unsupported named curve') } const genericImport = async ( keyFormat: 'spki' | 'pkcs8', keyData: Uint8Array, alg: string, options?: KeyImportOptions & { getNamedCurve?: (keyData: Uint8Array) => string }, ) => { let algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm let keyUsages: KeyUsage[] const isPublic = keyFormat === 'spki' // Helper functions for determining key usage based on key type const getSigUsages = (): KeyUsage[] => (isPublic ? ['verify'] : ['sign']) const getEncUsages = (): KeyUsage[] => isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'] switch (alg) { case 'PS256': case 'PS384': case 'PS512': algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` } keyUsages = getSigUsages() break case 'RS256': case 'RS384': case 'RS512': algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` } keyUsages = getSigUsages() break case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': algorithm = { name: 'RSA-OAEP', hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`, } keyUsages = getEncUsages() break case 'ES256': case 'ES384': case 'ES512': { const curveMap = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' } as const algorithm = { name: 'ECDSA', namedCurve: curveMap[alg] } keyUsages = getSigUsages() break } case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': { try { const namedCurve = options!.getNamedCurve!(keyData) algorithm = namedCurve === 'X25519' ? { name: 'X25519' } : { name: 'ECDH', namedCurve } } catch (cause) { throw new JOSENotSupported('Invalid or unsupported key format') } keyUsages = isPublic ? [] : ['deriveBits'] break } case 'Ed25519': case 'EdDSA': algorithm = { name: 'Ed25519' } keyUsages = getSigUsages() break case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': algorithm = { name: alg } keyUsages = getSigUsages() break default: throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value') } return crypto.subtle.importKey( keyFormat, keyData as Uint8Array, algorithm, options?.extractable ?? (isPublic ? true : false), keyUsages, ) } type PEMImportFunction = ( pem: string, alg: string, options?: KeyImportOptions, ) => Promise /** Helper function to process PEM-encoded data */ const processPEMData = (pem: string, pattern: RegExp): Uint8Array => { return decodeBase64(pem.replace(pattern, '')) } export const fromPKCS8: PEMImportFunction = (pem, alg, options?) => { const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g) let opts: Parameters[3] = options if (alg?.startsWith?.('ECDH-ES')) { opts ||= {} opts.getNamedCurve = (keyData: Uint8Array) => { const state = createASN1State(keyData) parsePKCS8Header(state) return parseECAlgorithmIdentifier(state) } } return genericImport('pkcs8', keyData, alg, opts) } export const fromSPKI: PEMImportFunction = (pem, alg, options?) => { const keyData = processPEMData(pem, /(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g) let opts: Parameters[3] = options if (alg?.startsWith?.('ECDH-ES')) { opts ||= {} opts.getNamedCurve = (keyData: Uint8Array) => { const state = createASN1State(keyData) parseSPKIHeader(state) return parseECAlgorithmIdentifier(state) } } return genericImport('spki', keyData, alg, opts) } /** * Extracts the Subject Public Key Info (SPKI) from an X.509 certificate. Parses the ASN.1 DER * structure to locate and extract the public key portion. * * @param buf - DER-encoded X.509 certificate bytes * * @returns SPKI structure as bytes */ function spkiFromX509(buf: Uint8Array): Uint8Array { const state = createASN1State(buf) // Parse outer certificate SEQUENCE expectTag(state, 0x30, 'Invalid certificate structure') parseLength(state) // Skip certificate length // Parse tbsCertificate (To Be Signed Certificate) SEQUENCE expectTag(state, 0x30, 'Invalid tbsCertificate structure') parseLength(state) // Skip tbsCertificate length if (buf[state.pos] === 0xa0) { // Optional version field present (context-specific [0]) // Skip: version, serialNumber, signature algorithm, issuer, validity, subject skipElement(state, 6) } else { // No version field (defaults to v1) // Skip: serialNumber, signature algorithm, issuer, validity, subject skipElement(state, 5) } // Extract subjectPublicKeyInfo SEQUENCE const spkiStart = state.pos expectTag(state, 0x30, 'Invalid SPKI structure') const spkiContentLen = parseLength(state) // Return the complete SPKI structure (tag + length + content) return buf.subarray(spkiStart, spkiStart + spkiContentLen + (state.pos - spkiStart)) } /** * Extracts SPKI from a PEM-encoded X.509 certificate string. * * @param x509 - PEM-encoded X.509 certificate * * @returns SPKI structure as bytes */ function extractX509SPKI(x509: string): Uint8Array { const derBytes = processPEMData(x509, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g) return spkiFromX509(derBytes) } export const fromX509: PEMImportFunction = (pem, alg, options?) => { let spki: Uint8Array try { spki = extractX509SPKI(pem) } catch (cause) { throw new TypeError('Failed to parse the X.509 certificate', { cause }) } return fromSPKI(formatPEM(encodeBase64(spki), 'PUBLIC KEY'), alg, options) } ================================================ FILE: src/lib/base64.ts ================================================ export function encodeBase64(input: Uint8Array): string { // @ts-ignore if (Uint8Array.prototype.toBase64) { // @ts-ignore return input.toBase64() } const CHUNK_SIZE = 0x8000 const arr = [] for (let i = 0; i < input.length; i += CHUNK_SIZE) { // @ts-ignore arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE))) } return btoa(arr.join('')) } export function decodeBase64(encoded: string): Uint8Array { // @ts-ignore if (Uint8Array.fromBase64) { // @ts-ignore return Uint8Array.fromBase64(encoded) } const binary = atob(encoded) const bytes = new Uint8Array(binary.length) for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i) } return bytes } ================================================ FILE: src/lib/buffer_utils.ts ================================================ export const encoder = new TextEncoder() export const decoder = new TextDecoder() const MAX_INT32 = 2 ** 32 export function concat(...buffers: Uint8Array[]): Uint8Array { const size = buffers.reduce((acc, { length }) => acc + length, 0) const buf = new Uint8Array(size) let i = 0 for (const buffer of buffers) { buf.set(buffer, i) i += buffer.length } return buf } function writeUInt32BE(buf: Uint8Array, value: number, offset?: number) { if (value < 0 || value >= MAX_INT32) { throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`) } buf.set([value >>> 24, value >>> 16, value >>> 8, value & 0xff], offset) } export function uint64be(value: number) { const high = Math.floor(value / MAX_INT32) const low = value % MAX_INT32 const buf = new Uint8Array(8) writeUInt32BE(buf, high, 0) writeUInt32BE(buf, low, 4) return buf } export function uint32be(value: number) { const buf = new Uint8Array(4) writeUInt32BE(buf, value) return buf } /** Encodes ASCII-only strings as Uint8Array */ export function encode(string: string): Uint8Array { const bytes = new Uint8Array(string.length) for (let i = 0; i < string.length; i++) { const code = string.charCodeAt(i) if (code > 127) { throw new TypeError('non-ASCII string encountered in encode()') } bytes[i] = code } return bytes } ================================================ FILE: src/lib/check_key_type.ts ================================================ import { withAlg as invalidKeyInput } from './invalid_key_input.js' import { isKeyLike } from './is_key_like.js' import * as jwk from './type_checks.js' import type * as types from '../types.d.ts' // @ts-ignore const tag = (key: unknown): string => key?.[Symbol.toStringTag] const jwkMatchesOp = (alg: string, key: types.JWK, usage: Usage) => { if (key.use !== undefined) { let expected: string switch (usage) { case 'sign': case 'verify': expected = 'sig' break case 'encrypt': case 'decrypt': expected = 'enc' break } if (key.use !== expected) { throw new TypeError( `Invalid key for this operation, its "use" must be "${expected}" when present`, ) } } if (key.alg !== undefined && key.alg !== alg) { throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`) } if (Array.isArray(key.key_ops)) { let expectedKeyOp switch (true) { case usage === 'sign' || usage === 'verify': // Fall through case alg === 'dir': // Fall through case alg.includes('CBC-HS'): expectedKeyOp = usage break case alg.startsWith('PBES2'): expectedKeyOp = 'deriveBits' break case /^A\d{3}(?:GCM)?(?:KW)?$/.test(alg): if (!alg.includes('GCM') && alg.endsWith('KW')) { expectedKeyOp = usage === 'encrypt' ? 'wrapKey' : 'unwrapKey' } else { expectedKeyOp = usage } break case usage === 'encrypt' && alg.startsWith('RSA'): expectedKeyOp = 'wrapKey' break case usage === 'decrypt': expectedKeyOp = alg.startsWith('RSA') ? 'unwrapKey' : 'deriveBits' break } if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) { throw new TypeError( `Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`, ) } } return true } const symmetricTypeCheck = (alg: string, key: unknown, usage: Usage) => { if (key instanceof Uint8Array) return if (jwk.isJWK(key)) { if (jwk.isSecretJWK(key) && jwkMatchesOp(alg, key, usage)) return throw new TypeError( `JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`, ) } if (!isKeyLike(key)) { throw new TypeError( invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array'), ) } if (key.type !== 'secret') { throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`) } } const asymmetricTypeCheck = (alg: string, key: unknown, usage: Usage) => { if (jwk.isJWK(key)) { switch (usage) { case 'decrypt': case 'sign': if (jwk.isPrivateJWK(key) && jwkMatchesOp(alg, key, usage)) return throw new TypeError(`JSON Web Key for this operation must be a private JWK`) case 'encrypt': case 'verify': if (jwk.isPublicJWK(key) && jwkMatchesOp(alg, key, usage)) return throw new TypeError(`JSON Web Key for this operation must be a public JWK`) } } if (!isKeyLike(key)) { throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key')) } if (key.type === 'secret') { throw new TypeError( `${tag(key)} instances for asymmetric algorithms must not be of type "secret"`, ) } if (key.type === 'public') { switch (usage) { case 'sign': throw new TypeError( `${tag(key)} instances for asymmetric algorithm signing must be of type "private"`, ) case 'decrypt': throw new TypeError( `${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`, ) } } if (key.type === 'private') { switch (usage) { case 'verify': throw new TypeError( `${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`, ) case 'encrypt': throw new TypeError( `${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`, ) } } } type Usage = 'sign' | 'verify' | 'encrypt' | 'decrypt' export function checkKeyType(alg: string, key: unknown, usage: Usage): void { switch (alg.substring(0, 2)) { case 'A1': // A128.+, A192.+ case 'A2': // A256.+ case 'di': // dir case 'HS': // HS\d{3} case 'PB': // PBES2.+ symmetricTypeCheck(alg, key, usage) break default: asymmetricTypeCheck(alg, key, usage) } } ================================================ FILE: src/lib/content_encryption.ts ================================================ import type * as types from '../types.d.ts' import { concat, uint64be } from './buffer_utils.js' import { checkEncCryptoKey } from './crypto_key.js' import { invalidKeyInput } from './invalid_key_input.js' import { JOSENotSupported, JWEDecryptionFailed, JWEInvalid } from '../util/errors.js' import { isCryptoKey } from './is_key_like.js' // --- CEK --- export function cekLength(alg: string) { switch (alg) { case 'A128GCM': return 128 case 'A192GCM': return 192 case 'A256GCM': case 'A128CBC-HS256': return 256 case 'A192CBC-HS384': return 384 case 'A256CBC-HS512': return 512 default: throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`) } } export const generateCek = (alg: string): Uint8Array => crypto.getRandomValues(new Uint8Array(cekLength(alg) >> 3)) function checkCekLength(cek: Uint8Array, expected: number) { const actual = cek.byteLength << 3 if (actual !== expected) { throw new JWEInvalid( `Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`, ) } } // --- IV --- function ivBitLength(alg: string) { switch (alg) { case 'A128GCM': case 'A128GCMKW': case 'A192GCM': case 'A192GCMKW': case 'A256GCM': case 'A256GCMKW': return 96 case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': return 128 default: throw new JOSENotSupported(`Unsupported JWE Algorithm: ${alg}`) } } export const generateIv = (alg: string): Uint8Array => crypto.getRandomValues(new Uint8Array(ivBitLength(alg) >> 3)) export function checkIvLength(enc: string, iv: Uint8Array) { if (iv.length << 3 !== ivBitLength(enc)) { throw new JWEInvalid('Invalid Initialization Vector length') } } // --- CBC helpers --- async function cbcKeySetup( enc: string, cek: Uint8Array | types.CryptoKey, usage: 'encrypt' | 'decrypt', ): Promise<{ encKey: CryptoKey; macKey: CryptoKey; keySize: number }> { if (!(cek instanceof Uint8Array)) { throw new TypeError(invalidKeyInput(cek, 'Uint8Array')) } const keySize = parseInt(enc.slice(1, 4), 10) const encKey = await crypto.subtle.importKey( 'raw', cek.subarray(keySize >> 3) as Uint8Array, 'AES-CBC', false, [usage], ) const macKey = await crypto.subtle.importKey( 'raw', cek.subarray(0, keySize >> 3) as Uint8Array, { hash: `SHA-${keySize << 1}`, name: 'HMAC', }, false, ['sign'], ) return { encKey, macKey, keySize } } async function cbcHmacTag( macKey: CryptoKey, macData: Uint8Array, keySize: number, ): Promise { return new Uint8Array( (await crypto.subtle.sign('HMAC', macKey, macData as Uint8Array)).slice( 0, keySize >> 3, ), ) } // --- CBC encrypt/decrypt --- async function cbcEncrypt( enc: string, plaintext: Uint8Array, cek: Uint8Array | types.CryptoKey, iv: Uint8Array, aad: Uint8Array, ) { const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'encrypt') const ciphertext = new Uint8Array( await crypto.subtle.encrypt( { iv: iv as Uint8Array, name: 'AES-CBC', }, encKey, plaintext as Uint8Array, ), ) const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)) const tag = await cbcHmacTag(macKey, macData, keySize) return { ciphertext, tag, iv } } async function timingSafeEqual(a: Uint8Array, b: Uint8Array): Promise { if (!(a instanceof Uint8Array)) { throw new TypeError('First argument must be a buffer') } if (!(b instanceof Uint8Array)) { throw new TypeError('Second argument must be a buffer') } const algorithm = { name: 'HMAC', hash: 'SHA-256' } const key = (await crypto.subtle.generateKey(algorithm, false, ['sign'])) as CryptoKey const aHmac = new Uint8Array( await crypto.subtle.sign(algorithm, key, a as Uint8Array), ) const bHmac = new Uint8Array( await crypto.subtle.sign(algorithm, key, b as Uint8Array), ) let out = 0 let i = -1 while (++i < 32) { out |= aHmac[i] ^ bHmac[i] } return out === 0 } async function cbcDecrypt( enc: string, cek: Uint8Array | types.CryptoKey, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad: Uint8Array, ) { const { encKey, macKey, keySize } = await cbcKeySetup(enc, cek, 'decrypt') const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3)) const expectedTag = await cbcHmacTag(macKey, macData, keySize) let macCheckPassed!: boolean try { macCheckPassed = await timingSafeEqual(tag, expectedTag) } catch { // } if (!macCheckPassed) { throw new JWEDecryptionFailed() } let plaintext!: Uint8Array try { plaintext = new Uint8Array( await crypto.subtle.decrypt( { iv: iv as Uint8Array, name: 'AES-CBC' }, encKey, ciphertext as Uint8Array, ), ) } catch { // } if (!plaintext) { throw new JWEDecryptionFailed() } return plaintext } // --- GCM encrypt/decrypt --- async function gcmEncrypt( enc: string, plaintext: Uint8Array, cek: Uint8Array | types.CryptoKey, iv: Uint8Array, aad: Uint8Array, ) { let encKey: types.CryptoKey if (cek instanceof Uint8Array) { encKey = await crypto.subtle.importKey( 'raw', cek as Uint8Array, 'AES-GCM', false, ['encrypt'], ) } else { checkEncCryptoKey(cek, enc, 'encrypt') encKey = cek } const encrypted = new Uint8Array( await crypto.subtle.encrypt( { additionalData: aad as Uint8Array, iv: iv as Uint8Array, name: 'AES-GCM', tagLength: 128, }, encKey, plaintext as Uint8Array, ), ) const tag = encrypted.slice(-16) const ciphertext = encrypted.slice(0, -16) return { ciphertext, tag, iv } } async function gcmDecrypt( enc: string, cek: Uint8Array | types.CryptoKey, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array, aad: Uint8Array, ) { let encKey: types.CryptoKey if (cek instanceof Uint8Array) { encKey = await crypto.subtle.importKey( 'raw', cek as Uint8Array, 'AES-GCM', false, ['decrypt'], ) } else { checkEncCryptoKey(cek, enc, 'decrypt') encKey = cek } try { return new Uint8Array( await crypto.subtle.decrypt( { additionalData: aad as Uint8Array, iv: iv as Uint8Array, name: 'AES-GCM', tagLength: 128, }, encKey, concat(ciphertext, tag) as Uint8Array, ), ) } catch { throw new JWEDecryptionFailed() } } // --- Public API --- const unsupportedEnc = 'Unsupported JWE Content Encryption Algorithm' export async function encrypt( enc: string, plaintext: Uint8Array, cek: unknown, iv: Uint8Array | undefined, aad: Uint8Array, ): Promise<{ ciphertext: Uint8Array tag: Uint8Array | undefined iv: Uint8Array | undefined }> { if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { throw new TypeError( invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'), ) } if (iv) { checkIvLength(enc, iv) } else { iv = generateIv(enc) } switch (enc) { case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': if (cek instanceof Uint8Array) { checkCekLength(cek, parseInt(enc.slice(-3), 10)) } return cbcEncrypt(enc, plaintext, cek, iv, aad) case 'A128GCM': case 'A192GCM': case 'A256GCM': if (cek instanceof Uint8Array) { checkCekLength(cek, parseInt(enc.slice(1, 4), 10)) } return gcmEncrypt(enc, plaintext, cek, iv, aad) default: throw new JOSENotSupported(unsupportedEnc) } } export async function decrypt( enc: string, cek: unknown, ciphertext: Uint8Array, iv: Uint8Array | undefined, tag: Uint8Array | undefined, aad: Uint8Array, ): Promise { if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) { throw new TypeError( invalidKeyInput(cek, 'CryptoKey', 'KeyObject', 'Uint8Array', 'JSON Web Key'), ) } if (!iv) { throw new JWEInvalid('JWE Initialization Vector missing') } if (!tag) { throw new JWEInvalid('JWE Authentication Tag missing') } checkIvLength(enc, iv) switch (enc) { case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(-3), 10)) return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) case 'A128GCM': case 'A192GCM': case 'A256GCM': if (cek instanceof Uint8Array) checkCekLength(cek, parseInt(enc.slice(1, 4), 10)) return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) default: throw new JOSENotSupported(unsupportedEnc) } } ================================================ FILE: src/lib/crypto_key.ts ================================================ import type * as types from '../types.d.ts' const unusable = (name: string | number, prop = 'algorithm.name') => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`) const isAlgorithm = ( algorithm: KeyAlgorithm, name: string, ): algorithm is T => algorithm.name === name function getHashLength(hash: KeyAlgorithm) { return parseInt(hash.name.slice(4), 10) } function checkHashLength(algorithm: { hash: KeyAlgorithm }, expected: number) { const actual = getHashLength(algorithm.hash) if (actual !== expected) throw unusable(`SHA-${expected}`, 'algorithm.hash') } function getNamedCurve(alg: string) { switch (alg) { case 'ES256': return 'P-256' case 'ES384': return 'P-384' case 'ES512': return 'P-521' default: throw new Error('unreachable') } } function checkUsage(key: types.CryptoKey, usage?: KeyUsage) { if (usage && !key.usages.includes(usage)) { throw new TypeError( `CryptoKey does not support this operation, its usages must include ${usage}.`, ) } } export function checkSigCryptoKey(key: types.CryptoKey, alg: string, usage: KeyUsage) { switch (alg) { case 'HS256': case 'HS384': case 'HS512': { if (!isAlgorithm(key.algorithm, 'HMAC')) throw unusable('HMAC') checkHashLength(key.algorithm, parseInt(alg.slice(2), 10)) break } case 'RS256': case 'RS384': case 'RS512': { if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5')) throw unusable('RSASSA-PKCS1-v1_5') checkHashLength(key.algorithm, parseInt(alg.slice(2), 10)) break } case 'PS256': case 'PS384': case 'PS512': { if (!isAlgorithm(key.algorithm, 'RSA-PSS')) throw unusable('RSA-PSS') checkHashLength(key.algorithm, parseInt(alg.slice(2), 10)) break } case 'Ed25519': // Fall through case 'EdDSA': { if (!isAlgorithm(key.algorithm, 'Ed25519')) throw unusable('Ed25519') break } case 'ML-DSA-44': // Fall through case 'ML-DSA-65': // Fall through case 'ML-DSA-87': { if (!isAlgorithm(key.algorithm, alg)) throw unusable(alg) break } case 'ES256': case 'ES384': case 'ES512': { if (!isAlgorithm(key.algorithm, 'ECDSA')) throw unusable('ECDSA') const expected = getNamedCurve(alg) const actual = key.algorithm.namedCurve if (actual !== expected) throw unusable(expected, 'algorithm.namedCurve') break } default: throw new TypeError('CryptoKey does not support this operation') } checkUsage(key, usage) } export function checkEncCryptoKey(key: types.CryptoKey, alg: string, usage?: KeyUsage) { switch (alg) { case 'A128GCM': case 'A192GCM': case 'A256GCM': { if (!isAlgorithm(key.algorithm, 'AES-GCM')) throw unusable('AES-GCM') const expected = parseInt(alg.slice(1, 4), 10) const actual = key.algorithm.length if (actual !== expected) throw unusable(expected, 'algorithm.length') break } case 'A128KW': case 'A192KW': case 'A256KW': { if (!isAlgorithm(key.algorithm, 'AES-KW')) throw unusable('AES-KW') const expected = parseInt(alg.slice(1, 4), 10) const actual = key.algorithm.length if (actual !== expected) throw unusable(expected, 'algorithm.length') break } case 'ECDH': { switch (key.algorithm.name) { case 'ECDH': case 'X25519': break default: throw unusable('ECDH or X25519') } break } case 'PBES2-HS256+A128KW': case 'PBES2-HS384+A192KW': case 'PBES2-HS512+A256KW': if (!isAlgorithm(key.algorithm, 'PBKDF2')) throw unusable('PBKDF2') break case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': { if (!isAlgorithm(key.algorithm, 'RSA-OAEP')) throw unusable('RSA-OAEP') checkHashLength(key.algorithm, parseInt(alg.slice(9), 10) || 1) break } default: throw new TypeError('CryptoKey does not support this operation') } checkUsage(key, usage) } ================================================ FILE: src/lib/deflate.ts ================================================ import { JOSENotSupported, JWEInvalid } from '../util/errors.js' import { concat } from './buffer_utils.js' function supported(name: 'CompressionStream' | 'DecompressionStream') { if (typeof globalThis[name] === 'undefined') { throw new JOSENotSupported( `JWE "zip" (Compression Algorithm) Header Parameter requires the ${name} API.`, ) } } export async function compress(input: Uint8Array): Promise { supported('CompressionStream') const cs = new CompressionStream('deflate-raw') const writer = cs.writable.getWriter() writer.write(input as Uint8Array).catch(() => {}) writer.close().catch(() => {}) const chunks: Uint8Array[] = [] const reader = cs.readable.getReader() for (;;) { const { value, done } = await reader.read() if (done) break chunks.push(value) } return concat(...chunks) } export async function decompress(input: Uint8Array, maxLength: number): Promise { supported('DecompressionStream') const ds = new DecompressionStream('deflate-raw') const writer = ds.writable.getWriter() writer.write(input as Uint8Array).catch(() => {}) writer.close().catch(() => {}) const chunks: Uint8Array[] = [] let length = 0 const reader = ds.readable.getReader() for (;;) { const { value, done } = await reader.read() if (done) break chunks.push(value) length += value.byteLength if (maxLength !== Infinity && length > maxLength) { throw new JWEInvalid('Decompressed plaintext exceeded the configured limit') } } return concat(...chunks) } ================================================ FILE: src/lib/ecdhes.ts ================================================ import type * as types from '../types.d.ts' import { encode, concat, uint32be } from './buffer_utils.js' import { checkEncCryptoKey } from './crypto_key.js' import { digest } from './helpers.js' function lengthAndInput(input: Uint8Array) { return concat(uint32be(input.length), input) } /** * Concat KDF implementation * * @param Z - Shared secret from key-agreement scheme * @param L - Length of derived keying material in bits * @param OtherInfo - Context and application specific data */ async function concatKdf(Z: Uint8Array, L: number, OtherInfo: Uint8Array) { // dkLen = L (in bits), converted to bytes for output length const dkLen = L >> 3 // Hash output length in bytes (SHA-256 produces 32 bytes) const hashLen = 32 // Number of hash function calls needed const reps = Math.ceil(dkLen / hashLen) // Initialize output buffer for concatenated hash results const dk = new Uint8Array(reps * hashLen) // Perform reps iterations of the hash function for (let i = 1; i <= reps; i++) { // Construct Hash_i input: Counter || Z || OtherInfo const hashInput = new Uint8Array(4 + Z.length + OtherInfo.length) hashInput.set(uint32be(i), 0) // 32-bit big-endian counter hashInput.set(Z, 4) // Shared secret Z hashInput.set(OtherInfo, 4 + Z.length) // OtherInfo // Hash_i = Hash(Counter || Z || OtherInfo) const hashResult = await digest('sha256', hashInput) dk.set(hashResult, (i - 1) * hashLen) } // Return leading L bits of dk (truncate to exact length needed) return dk.slice(0, dkLen) } /** * ECDH-ES Key Agreement with Concat KDF * * @param publicKey * @param privateKey * @param algorithm - AlgorithmID: For Direct Key Agreement (ECDH-ES), this is the "enc" value. For * Key Agreement with Key Wrapping, this is the "alg" value * @param keyLength - Keydatalen: Number of bits in the desired output key * @param apu - PartyUInfo: Agreement PartyUInfo value (information about the producer) * @param apv - PartyVInfo: Agreement PartyVInfo value (information about the recipient) */ export async function deriveKey( publicKey: types.CryptoKey, privateKey: types.CryptoKey, algorithm: string, keyLength: number, apu: Uint8Array = new Uint8Array(), apv: Uint8Array = new Uint8Array(), ) { checkEncCryptoKey(publicKey, 'ECDH') checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits') // Construct OtherInfo const algorithmID = lengthAndInput(encode(algorithm)) const partyUInfo = lengthAndInput(apu) const partyVInfo = lengthAndInput(apv) const suppPubInfo = uint32be(keyLength) const suppPrivInfo = new Uint8Array() const otherInfo = concat(algorithmID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo) // Perform ECDH to get the shared secret Z const Z = new Uint8Array( await crypto.subtle.deriveBits( { name: publicKey.algorithm.name, public: publicKey, }, privateKey, getEcdhBitLength(publicKey), ), ) // Apply Concat KDF to derive the final key material return concatKdf(Z, keyLength, otherInfo) } function getEcdhBitLength(publicKey: CryptoKey) { if (publicKey.algorithm.name === 'X25519') { return 256 } return ( Math.ceil(parseInt((publicKey.algorithm as EcKeyAlgorithm).namedCurve.slice(-3), 10) / 8) << 3 ) } export function allowed(key: types.CryptoKey) { switch ((key.algorithm as EcKeyAlgorithm).namedCurve) { case 'P-256': case 'P-384': case 'P-521': return true default: return key.algorithm.name === 'X25519' } } ================================================ FILE: src/lib/helpers.ts ================================================ import { decode } from '../util/base64url.js' export const unprotected = Symbol() export function assertNotSet(value: unknown, name: string): void { if (value) { throw new TypeError(`${name} can only be called once`) } } export function decodeBase64url( value: string, label: string, ErrorClass: new (message: string) => Error, ): Uint8Array { try { return decode(value) } catch { throw new ErrorClass(`Failed to base64url decode the ${label}`) } } export async function digest( algorithm: 'sha256' | 'sha384' | 'sha512', data: Uint8Array, ): Promise { const subtleDigest = `SHA-${algorithm.slice(-3)}` return new Uint8Array(await crypto.subtle.digest(subtleDigest, data as Uint8Array)) } ================================================ FILE: src/lib/invalid_key_input.ts ================================================ function message(msg: string, actual: unknown, ...types: Array) { types = types.filter(Boolean) if (types.length > 2) { const last = types.pop() msg += `one of type ${types.join(', ')}, or ${last}.` } else if (types.length === 2) { msg += `one of type ${types[0]} or ${types[1]}.` } else { msg += `of type ${types[0]}.` } if (actual == null) { msg += ` Received ${actual}` } else if (typeof actual === 'function' && actual.name) { msg += ` Received function ${actual.name}` } else if (typeof actual === 'object' && actual != null) { if (actual.constructor?.name) { msg += ` Received an instance of ${actual.constructor.name}` } } return msg } export const invalidKeyInput = (actual: unknown, ...types: string[]) => message('Key must be ', actual, ...types) export const withAlg = (alg: string, actual: unknown, ...types: Array) => message(`Key for the ${alg} algorithm must be `, actual, ...types) ================================================ FILE: src/lib/is_key_like.ts ================================================ import type * as types from '../types.d.ts' export function assertCryptoKey(key: unknown): asserts key is types.CryptoKey { if (!isCryptoKey(key)) { throw new Error('CryptoKey instance expected') } } export const isCryptoKey = (key: unknown): key is types.CryptoKey => { // @ts-ignore if (key?.[Symbol.toStringTag] === 'CryptoKey') return true try { return key instanceof CryptoKey } catch { return false } } export const isKeyObject = (key: unknown): key is T => // @ts-ignore key?.[Symbol.toStringTag] === 'KeyObject' export const isKeyLike = (key: unknown): key is types.CryptoKey | types.KeyObject => isCryptoKey(key) || isKeyObject(key) ================================================ FILE: src/lib/jwk_to_key.ts ================================================ import { JOSENotSupported } from '../util/errors.js' import type * as types from '../types.d.ts' const unsupportedAlg = 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value' function subtleMapping(jwk: types.JWK): { algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm keyUsages: KeyUsage[] } { let algorithm: RsaHashedImportParams | EcKeyAlgorithm | Algorithm let keyUsages: KeyUsage[] switch (jwk.kty) { case 'AKP': { switch (jwk.alg) { case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': algorithm = { name: jwk.alg } keyUsages = jwk.priv ? ['sign'] : ['verify'] break default: throw new JOSENotSupported(unsupportedAlg) } break } case 'RSA': { switch (jwk.alg) { case 'PS256': case 'PS384': case 'PS512': algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` } keyUsages = jwk.d ? ['sign'] : ['verify'] break case 'RS256': case 'RS384': case 'RS512': algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` } keyUsages = jwk.d ? ['sign'] : ['verify'] break case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': algorithm = { name: 'RSA-OAEP', hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`, } keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'] break default: throw new JOSENotSupported(unsupportedAlg) } break } case 'EC': { switch (jwk.alg) { case 'ES256': case 'ES384': case 'ES512': algorithm = { name: 'ECDSA', namedCurve: { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' }[jwk.alg], } keyUsages = jwk.d ? ['sign'] : ['verify'] break case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': algorithm = { name: 'ECDH', namedCurve: jwk.crv! } keyUsages = jwk.d ? ['deriveBits'] : [] break default: throw new JOSENotSupported(unsupportedAlg) } break } case 'OKP': { switch (jwk.alg) { case 'Ed25519': // Fall through case 'EdDSA': algorithm = { name: 'Ed25519' } keyUsages = jwk.d ? ['sign'] : ['verify'] break case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': algorithm = { name: jwk.crv! } keyUsages = jwk.d ? ['deriveBits'] : [] break default: throw new JOSENotSupported(unsupportedAlg) } break } default: throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value') } return { algorithm, keyUsages } } export async function jwkToKey(jwk: types.JWK): Promise { if (!jwk.alg) { throw new TypeError('"alg" argument is required when "jwk.alg" is not present') } const { algorithm, keyUsages } = subtleMapping(jwk) const keyData: types.JWK = { ...jwk } if (keyData.kty !== 'AKP') { delete keyData.alg } delete keyData.use return crypto.subtle.importKey( 'jwk', keyData, algorithm, jwk.ext ?? (jwk.d || jwk.priv ? false : true), (jwk.key_ops as KeyUsage[]) ?? keyUsages, ) } ================================================ FILE: src/lib/jwt_claims_set.ts ================================================ import type * as types from '../types.d.ts' import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js' import { encoder, decoder } from './buffer_utils.js' import { isObject } from './type_checks.js' const epoch = (date: Date) => Math.floor(date.getTime() / 1000) const minute = 60 const hour = minute * 60 const day = hour * 24 const week = day * 7 const year = day * 365.25 const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i export function secs(str: string): number { const matched = REGEX.exec(str) if (!matched || (matched[4] && matched[1])) { throw new TypeError('Invalid time period format') } const value = parseFloat(matched[2]) const unit = matched[3].toLowerCase() let numericDate: number switch (unit) { case 'sec': case 'secs': case 'second': case 'seconds': case 's': numericDate = Math.round(value) break case 'minute': case 'minutes': case 'min': case 'mins': case 'm': numericDate = Math.round(value * minute) break case 'hour': case 'hours': case 'hr': case 'hrs': case 'h': numericDate = Math.round(value * hour) break case 'day': case 'days': case 'd': numericDate = Math.round(value * day) break case 'week': case 'weeks': case 'w': numericDate = Math.round(value * week) break // years matched default: numericDate = Math.round(value * year) break } if (matched[1] === '-' || matched[4] === 'ago') { return -numericDate } return numericDate } function validateInput(label: string, input: number) { if (!Number.isFinite(input)) { throw new TypeError(`Invalid ${label} input`) } return input } const normalizeTyp = (value: string) => { if (value.includes('/')) { return value.toLowerCase() } return `application/${value.toLowerCase()}` } const checkAudiencePresence = (audPayload: unknown, audOption: unknown[]) => { if (typeof audPayload === 'string') { return audOption.includes(audPayload) } if (Array.isArray(audPayload)) { // Each principal intended to process the JWT MUST // identify itself with a value in the audience claim return audOption.some(Set.prototype.has.bind(new Set(audPayload))) } return false } export function validateClaimsSet( protectedHeader: types.JWEHeaderParameters | types.JWSHeaderParameters, encodedPayload: Uint8Array, options: types.JWTClaimVerificationOptions = {}, ) { let payload!: { [propName: string]: unknown } try { payload = JSON.parse(decoder.decode(encodedPayload)) } catch { // } if (!isObject(payload)) { throw new JWTInvalid('JWT Claims Set must be a top-level JSON object') } const { typ } = options if ( typ && (typeof protectedHeader!.typ !== 'string' || normalizeTyp(protectedHeader!.typ) !== normalizeTyp(typ)) ) { throw new JWTClaimValidationFailed( 'unexpected "typ" JWT header value', payload, 'typ', 'check_failed', ) } const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options const presenceCheck = [...requiredClaims] if (maxTokenAge !== undefined) presenceCheck.push('iat') if (audience !== undefined) presenceCheck.push('aud') if (subject !== undefined) presenceCheck.push('sub') if (issuer !== undefined) presenceCheck.push('iss') for (const claim of new Set(presenceCheck.reverse())) { if (!(claim in payload)) { throw new JWTClaimValidationFailed( `missing required "${claim}" claim`, payload, claim, 'missing', ) } } if ( issuer && !((Array.isArray(issuer) ? issuer : [issuer]) as unknown[]).includes(payload.iss!) ) { throw new JWTClaimValidationFailed( 'unexpected "iss" claim value', payload, 'iss', 'check_failed', ) } if (subject && payload.sub !== subject) { throw new JWTClaimValidationFailed( 'unexpected "sub" claim value', payload, 'sub', 'check_failed', ) } if ( audience && !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience) ) { throw new JWTClaimValidationFailed( 'unexpected "aud" claim value', payload, 'aud', 'check_failed', ) } let tolerance: number switch (typeof options.clockTolerance) { case 'string': tolerance = secs(options.clockTolerance) break case 'number': tolerance = options.clockTolerance break case 'undefined': tolerance = 0 break default: throw new TypeError('Invalid clockTolerance option type') } const { currentDate } = options const now = epoch(currentDate || new Date()) if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') { throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid') } if (payload.nbf !== undefined) { if (typeof payload.nbf !== 'number') { throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid') } if (payload.nbf > now + tolerance) { throw new JWTClaimValidationFailed( '"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed', ) } } if (payload.exp !== undefined) { if (typeof payload.exp !== 'number') { throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid') } if (payload.exp <= now - tolerance) { throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed') } } if (maxTokenAge) { const age = now - (payload.iat as number) const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge) if (age - tolerance > max) { throw new JWTExpired( '"iat" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed', ) } if (age < 0 - tolerance) { throw new JWTClaimValidationFailed( '"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed', ) } } return payload as types.JWTPayload } export class JWTClaimsBuilder { #payload!: types.JWTPayload constructor(payload: types.JWTPayload) { if (!isObject(payload)) { throw new TypeError('JWT Claims Set MUST be an object') } this.#payload = structuredClone(payload) } data(): Uint8Array { return encoder.encode(JSON.stringify(this.#payload)) } get iss(): string | undefined { return this.#payload.iss } set iss(value: string) { this.#payload.iss = value } get sub(): string | undefined { return this.#payload.sub } set sub(value: string) { this.#payload.sub = value } get aud(): string | string[] | undefined { return this.#payload.aud } set aud(value: string | string[]) { this.#payload.aud = value } set jti(value: string) { this.#payload.jti = value } set nbf(value: number | string | Date) { if (typeof value === 'number') { this.#payload.nbf = validateInput('setNotBefore', value) } else if (value instanceof Date) { this.#payload.nbf = validateInput('setNotBefore', epoch(value)) } else { this.#payload.nbf = epoch(new Date()) + secs(value) } } set exp(value: number | string | Date) { if (typeof value === 'number') { this.#payload.exp = validateInput('setExpirationTime', value) } else if (value instanceof Date) { this.#payload.exp = validateInput('setExpirationTime', epoch(value)) } else { this.#payload.exp = epoch(new Date()) + secs(value) } } set iat(value: number | string | Date | undefined) { if (value === undefined) { this.#payload.iat = epoch(new Date()) } else if (value instanceof Date) { this.#payload.iat = validateInput('setIssuedAt', epoch(value)) } else if (typeof value === 'string') { this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value)) } else { this.#payload.iat = validateInput('setIssuedAt', value) } } } ================================================ FILE: src/lib/key_management.ts ================================================ import type * as types from '../types.d.ts' import type { JWEKeyManagementHeaderParameters, JWEHeaderParameters, JWK } from '../types.d.ts' import * as aeskw from './aeskw.js' import * as ecdhes from './ecdhes.js' import * as pbes2kw from './pbes2kw.js' import * as rsaes from './rsaes.js' import { encode as b64u } from '../util/base64url.js' import { normalizeKey } from './normalize_key.js' import { JOSENotSupported, JWEInvalid } from '../util/errors.js' import { decodeBase64url } from './helpers.js' import { generateCek, cekLength } from './content_encryption.js' import { importJWK } from '../key/import.js' import { exportJWK } from '../key/export.js' import { isObject } from './type_checks.js' import { wrap as aesGcmKwWrap, unwrap as aesGcmKwUnwrap } from './aesgcmkw.js' import { assertCryptoKey } from './is_key_like.js' const unsupportedAlgHeader = 'Invalid or unsupported "alg" (JWE Algorithm) header value' function assertEncryptedKey( encryptedKey: Uint8Array | undefined, ): asserts encryptedKey is Uint8Array { if (encryptedKey === undefined) throw new JWEInvalid('JWE Encrypted Key missing') } export async function decryptKeyManagement( alg: string, key: types.CryptoKey | Uint8Array, encryptedKey: Uint8Array | undefined, joseHeader: types.JWEHeaderParameters, options?: types.DecryptOptions, ): Promise { switch (alg) { case 'dir': { // Direct Encryption if (encryptedKey !== undefined) throw new JWEInvalid('Encountered unexpected JWE Encrypted Key') return key } case 'ECDH-ES': // Direct Key Agreement if (encryptedKey !== undefined) throw new JWEInvalid('Encountered unexpected JWE Encrypted Key') case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': { // Direct Key Agreement if (!isObject(joseHeader.epk)) throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`) assertCryptoKey(key) if (!ecdhes.allowed(key)) throw new JOSENotSupported( 'ECDH with the provided key is not allowed or not supported by your javascript runtime', ) const epk = await importJWK(joseHeader.epk, alg) assertCryptoKey(epk) let partyUInfo!: Uint8Array let partyVInfo!: Uint8Array if (joseHeader.apu !== undefined) { if (typeof joseHeader.apu !== 'string') throw new JWEInvalid(`JOSE Header "apu" (Agreement PartyUInfo) invalid`) partyUInfo = decodeBase64url(joseHeader.apu, 'apu', JWEInvalid) } if (joseHeader.apv !== undefined) { if (typeof joseHeader.apv !== 'string') throw new JWEInvalid(`JOSE Header "apv" (Agreement PartyVInfo) invalid`) partyVInfo = decodeBase64url(joseHeader.apv, 'apv', JWEInvalid) } const sharedSecret = await ecdhes.deriveKey( epk, key, alg === 'ECDH-ES' ? joseHeader.enc! : alg, alg === 'ECDH-ES' ? cekLength(joseHeader.enc!) : parseInt(alg.slice(-5, -2), 10), partyUInfo, partyVInfo, ) if (alg === 'ECDH-ES') return sharedSecret // Key Agreement with Key Wrapping assertEncryptedKey(encryptedKey) return aeskw.unwrap(alg.slice(-6), sharedSecret, encryptedKey) } case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': { // Key Encryption (RSA) assertEncryptedKey(encryptedKey) assertCryptoKey(key) return rsaes.decrypt(alg, key, encryptedKey) } case 'PBES2-HS256+A128KW': case 'PBES2-HS384+A192KW': case 'PBES2-HS512+A256KW': { // Key Encryption (PBES2) assertEncryptedKey(encryptedKey) if (typeof joseHeader.p2c !== 'number') throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`) const p2cLimit = options?.maxPBES2Count || 10_000 if (joseHeader.p2c > p2cLimit) throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`) if (typeof joseHeader.p2s !== 'string') throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`) let p2s: Uint8Array p2s = decodeBase64url(joseHeader.p2s, 'p2s', JWEInvalid) return pbes2kw.unwrap(alg, key, encryptedKey, joseHeader.p2c, p2s) } case 'A128KW': case 'A192KW': case 'A256KW': { // Key Wrapping (AES KW) assertEncryptedKey(encryptedKey) return aeskw.unwrap(alg, key, encryptedKey) } case 'A128GCMKW': case 'A192GCMKW': case 'A256GCMKW': { // Key Wrapping (AES GCM KW) assertEncryptedKey(encryptedKey) if (typeof joseHeader.iv !== 'string') throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`) if (typeof joseHeader.tag !== 'string') throw new JWEInvalid(`JOSE Header "tag" (Authentication Tag) missing or invalid`) let iv: Uint8Array iv = decodeBase64url(joseHeader.iv, 'iv', JWEInvalid) let tag: Uint8Array tag = decodeBase64url(joseHeader.tag, 'tag', JWEInvalid) return aesGcmKwUnwrap(alg, key, encryptedKey, iv, tag) } default: { throw new JOSENotSupported(unsupportedAlgHeader) } } } export async function encryptKeyManagement( alg: string, enc: string, key: types.CryptoKey | Uint8Array, providedCek?: Uint8Array, providedParameters: JWEKeyManagementHeaderParameters = {}, ): Promise<{ cek: types.CryptoKey | Uint8Array encryptedKey?: Uint8Array parameters?: JWEHeaderParameters }> { let encryptedKey: Uint8Array | undefined let parameters: (JWEHeaderParameters & { epk?: JWK }) | undefined let cek: types.CryptoKey | Uint8Array switch (alg) { case 'dir': { // Direct Encryption cek = key break } case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': { assertCryptoKey(key) // Direct Key Agreement if (!ecdhes.allowed(key)) { throw new JOSENotSupported( 'ECDH with the provided key is not allowed or not supported by your javascript runtime', ) } const { apu, apv } = providedParameters let ephemeralKey: types.CryptoKey if (providedParameters.epk) { ephemeralKey = (await normalizeKey(providedParameters.epk, alg)) as types.CryptoKey } else { ephemeralKey = ( await crypto.subtle.generateKey(key.algorithm as EcKeyAlgorithm, true, ['deriveBits']) ).privateKey } const { x, y, crv, kty } = await exportJWK(ephemeralKey!) const sharedSecret = await ecdhes.deriveKey( key, ephemeralKey, alg === 'ECDH-ES' ? enc : alg, alg === 'ECDH-ES' ? cekLength(enc) : parseInt(alg.slice(-5, -2), 10), apu, apv, ) parameters = { epk: { x, crv, kty } } if (kty === 'EC') parameters.epk!.y = y if (apu) parameters.apu = b64u(apu) if (apv) parameters.apv = b64u(apv) if (alg === 'ECDH-ES') { cek = sharedSecret break } // Key Agreement with Key Wrapping cek = providedCek || generateCek(enc) const kwAlg = alg.slice(-6) encryptedKey = await aeskw.wrap(kwAlg, sharedSecret, cek) break } case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': { // Key Encryption (RSA) cek = providedCek || generateCek(enc) assertCryptoKey(key) encryptedKey = await rsaes.encrypt(alg, key, cek) break } case 'PBES2-HS256+A128KW': case 'PBES2-HS384+A192KW': case 'PBES2-HS512+A256KW': { // Key Encryption (PBES2) cek = providedCek || generateCek(enc) const { p2c, p2s } = providedParameters ;({ encryptedKey, ...parameters } = await pbes2kw.wrap(alg, key, cek, p2c, p2s)) break } case 'A128KW': case 'A192KW': case 'A256KW': { // Key Wrapping (AES KW) cek = providedCek || generateCek(enc) encryptedKey = await aeskw.wrap(alg, key, cek) break } case 'A128GCMKW': case 'A192GCMKW': case 'A256GCMKW': { // Key Wrapping (AES GCM KW) cek = providedCek || generateCek(enc) const { iv } = providedParameters ;({ encryptedKey, ...parameters } = await aesGcmKwWrap(alg, key, cek, iv)) break } default: { throw new JOSENotSupported(unsupportedAlgHeader) } } return { cek, encryptedKey, parameters } } ================================================ FILE: src/lib/key_to_jwk.ts ================================================ import type * as types from '../types.d.ts' import { invalidKeyInput } from './invalid_key_input.js' import { encode as b64u } from '../util/base64url.js' import { isCryptoKey, isKeyObject } from './is_key_like.js' interface ExportOptions { format: 'jwk' } interface ExtractableKeyObject extends types.KeyObject { export(arg: ExportOptions): types.JWK export(): Uint8Array } export async function keyToJWK(key: unknown): Promise { if (isKeyObject(key)) { if (key.type === 'secret') { key = (key as ExtractableKeyObject).export() } else { return (key as ExtractableKeyObject).export({ format: 'jwk' }) } } if (key instanceof Uint8Array) { return { kty: 'oct', k: b64u(key), } } if (!isCryptoKey(key)) { throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'Uint8Array')) } if (!key.extractable) { throw new TypeError('non-extractable CryptoKey cannot be exported as a JWK') } const { ext, key_ops, alg, use, ...jwk } = await crypto.subtle.exportKey('jwk', key) if (jwk.kty === 'AKP') { ;(jwk as types.JWK).alg = alg } return jwk as types.JWK } ================================================ FILE: src/lib/normalize_key.ts ================================================ import type * as types from '../types.d.ts' import { isJWK } from './type_checks.js' import { decode } from '../util/base64url.js' import { jwkToKey } from './jwk_to_key.js' import { isCryptoKey, isKeyObject } from './is_key_like.js' const unusableForAlg = 'given KeyObject instance cannot be used for this algorithm' let cache: WeakMap> interface ConvertableKeyObject extends types.KeyObject { export(): Uint8Array export(opts: { format: 'jwk' }): types.JWK asymmetricKeyType?: string asymmetricKeyDetails?: { namedCurve?: string } toCryptoKey( alg: | AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm, extractable: boolean, usages: string[], ): types.CryptoKey } const handleJWK = async ( key: types.KeyObject | types.JWK, jwk: types.JWK, alg: string, freeze = false, ) => { cache ||= new WeakMap() let cached = cache.get(key) if (cached?.[alg]) { return cached[alg] } const cryptoKey = await jwkToKey({ ...jwk, alg }) if (freeze) Object.freeze(key) if (!cached) { cache.set(key, { [alg]: cryptoKey }) } else { cached[alg] = cryptoKey } return cryptoKey } const handleKeyObject = (keyObject: ConvertableKeyObject, alg: string) => { cache ||= new WeakMap() let cached = cache.get(keyObject) if (cached?.[alg]) { return cached[alg] } const isPublic = keyObject.type === 'public' const extractable = isPublic ? true : false let cryptoKey: types.CryptoKey | undefined if (keyObject.asymmetricKeyType === 'x25519') { switch (alg) { case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': break default: throw new TypeError(unusableForAlg) } cryptoKey = keyObject.toCryptoKey( keyObject.asymmetricKeyType, extractable, isPublic ? [] : ['deriveBits'], ) } if (keyObject.asymmetricKeyType === 'ed25519') { if (alg !== 'EdDSA' && alg !== 'Ed25519') { throw new TypeError(unusableForAlg) } cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [ isPublic ? 'verify' : 'sign', ]) } switch (keyObject.asymmetricKeyType) { case 'ml-dsa-44': case 'ml-dsa-65': case 'ml-dsa-87': { if (alg !== keyObject.asymmetricKeyType.toUpperCase()) { throw new TypeError(unusableForAlg) } cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [ isPublic ? 'verify' : 'sign', ]) } } if (keyObject.asymmetricKeyType === 'rsa') { let hash: string switch (alg) { case 'RSA-OAEP': hash = 'SHA-1' break case 'RS256': case 'PS256': case 'RSA-OAEP-256': hash = 'SHA-256' break case 'RS384': case 'PS384': case 'RSA-OAEP-384': hash = 'SHA-384' break case 'RS512': case 'PS512': case 'RSA-OAEP-512': hash = 'SHA-512' break default: throw new TypeError(unusableForAlg) } if (alg.startsWith('RSA-OAEP')) { return keyObject.toCryptoKey( { name: 'RSA-OAEP', hash, }, extractable, isPublic ? ['encrypt'] : ['decrypt'], ) } cryptoKey = keyObject.toCryptoKey( { name: alg.startsWith('PS') ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5', hash, }, extractable, [isPublic ? 'verify' : 'sign'], ) } if (keyObject.asymmetricKeyType === 'ec') { const nist = new Map([ ['prime256v1', 'P-256'], ['secp384r1', 'P-384'], ['secp521r1', 'P-521'], ]) const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve) if (!namedCurve) { throw new TypeError(unusableForAlg) } const expectedCurve: Record = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' } if (expectedCurve[alg] && namedCurve === expectedCurve[alg]) { cryptoKey = keyObject.toCryptoKey( { name: 'ECDSA', namedCurve, }, extractable, [isPublic ? 'verify' : 'sign'], ) } if (alg.startsWith('ECDH-ES')) { cryptoKey = keyObject.toCryptoKey( { name: 'ECDH', namedCurve, }, extractable, isPublic ? [] : ['deriveBits'], ) } } if (!cryptoKey) { throw new TypeError(unusableForAlg) } if (!cached) { cache.set(keyObject, { [alg]: cryptoKey }) } else { cached[alg] = cryptoKey } return cryptoKey } export async function normalizeKey( key: types.CryptoKey | types.KeyObject | types.JWK | Uint8Array, alg: string, ): Promise { if (key instanceof Uint8Array) { return key } if (isCryptoKey(key)) { return key } if (isKeyObject(key)) { if (key.type === 'secret') { return (key as ConvertableKeyObject).export() } if ('toCryptoKey' in key && typeof key.toCryptoKey === 'function') { try { return handleKeyObject(key as ConvertableKeyObject, alg) } catch (err) { if (err instanceof TypeError) { throw err } } } let jwk: types.JWK = (key as ConvertableKeyObject).export({ format: 'jwk' }) return handleJWK(key, jwk, alg) } if (isJWK(key)) { if (key.k) { return decode(key.k) } return handleJWK(key, key, alg, true) } throw new Error('unreachable') } ================================================ FILE: src/lib/pbes2kw.ts ================================================ import type * as types from '../types.d.ts' import { encode as b64u } from '../util/base64url.js' import * as aeskw from './aeskw.js' import { checkEncCryptoKey } from './crypto_key.js' import { concat, encode } from './buffer_utils.js' import { JWEInvalid } from '../util/errors.js' function getCryptoKey(key: types.CryptoKey | Uint8Array, alg: string) { if (key instanceof Uint8Array) { return crypto.subtle.importKey('raw', key as Uint8Array, 'PBKDF2', false, [ 'deriveBits', ]) } checkEncCryptoKey(key, alg, 'deriveBits') return key } const concatSalt = (alg: string, p2sInput: Uint8Array) => concat(encode(alg), Uint8Array.of(0x00), p2sInput) async function deriveKey( p2s: Uint8Array, alg: string, p2c: number, key: types.CryptoKey | Uint8Array, ) { if (!(p2s instanceof Uint8Array) || p2s.length < 8) { throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets') } const salt = concatSalt(alg, p2s) const keylen = parseInt(alg.slice(13, 16), 10) const subtleAlg = { hash: `SHA-${alg.slice(8, 11)}`, iterations: p2c, name: 'PBKDF2', salt, } const cryptoKey = await getCryptoKey(key, alg) return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen)) } export async function wrap( alg: string, key: types.CryptoKey | Uint8Array, cek: Uint8Array, p2c = 2048, p2s: Uint8Array = crypto.getRandomValues(new Uint8Array(16)), ) { const derived = await deriveKey(p2s, alg, p2c, key) const encryptedKey = await aeskw.wrap(alg.slice(-6), derived, cek) return { encryptedKey, p2c, p2s: b64u(p2s) } } export async function unwrap( alg: string, key: types.CryptoKey | Uint8Array, encryptedKey: Uint8Array, p2c: number, p2s: Uint8Array, ) { const derived = await deriveKey(p2s, alg, p2c, key) return aeskw.unwrap(alg.slice(-6), derived, encryptedKey) } ================================================ FILE: src/lib/rsaes.ts ================================================ import type * as types from '../types.d.ts' import { checkEncCryptoKey } from './crypto_key.js' import { checkKeyLength } from './signing.js' import { JOSENotSupported } from '../util/errors.js' const subtleAlgorithm = (alg: string) => { switch (alg) { case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': return 'RSA-OAEP' default: throw new JOSENotSupported( `alg ${alg} is not supported either by JOSE or your javascript runtime`, ) } } export async function encrypt(alg: string, key: types.CryptoKey, cek: Uint8Array) { checkEncCryptoKey(key, alg, 'encrypt') checkKeyLength(alg, key) return new Uint8Array( await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek as Uint8Array), ) } export async function decrypt(alg: string, key: types.CryptoKey, encryptedKey: Uint8Array) { checkEncCryptoKey(key, alg, 'decrypt') checkKeyLength(alg, key) return new Uint8Array( await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey as Uint8Array), ) } ================================================ FILE: src/lib/signing.ts ================================================ import type * as types from '../types.d.ts' import { JOSENotSupported } from '../util/errors.js' import { checkSigCryptoKey } from './crypto_key.js' import { invalidKeyInput } from './invalid_key_input.js' export function checkKeyLength(alg: string, key: types.CryptoKey) { if (alg.startsWith('RS') || alg.startsWith('PS')) { const { modulusLength } = key.algorithm as RsaKeyAlgorithm if (typeof modulusLength !== 'number' || modulusLength < 2048) { throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`) } } } function subtleAlgorithm(alg: string, algorithm: KeyAlgorithm | EcKeyAlgorithm) { const hash = `SHA-${alg.slice(-3)}` switch (alg) { case 'HS256': case 'HS384': case 'HS512': return { hash, name: 'HMAC' } case 'PS256': case 'PS384': case 'PS512': return { hash, name: 'RSA-PSS', saltLength: parseInt(alg.slice(-3), 10) >> 3 } case 'RS256': case 'RS384': case 'RS512': return { hash, name: 'RSASSA-PKCS1-v1_5' } case 'ES256': case 'ES384': case 'ES512': return { hash, name: 'ECDSA', namedCurve: (algorithm as EcKeyAlgorithm).namedCurve } case 'Ed25519': // Fall through case 'EdDSA': return { name: 'Ed25519' } case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': return { name: alg } default: throw new JOSENotSupported( `alg ${alg} is not supported either by JOSE or your javascript runtime`, ) } } async function getSigKey(alg: string, key: types.CryptoKey | Uint8Array, usage: KeyUsage) { if (key instanceof Uint8Array) { if (!alg.startsWith('HS')) { throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key')) } return crypto.subtle.importKey( 'raw', key as Uint8Array, { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, false, [usage], ) } checkSigCryptoKey(key, alg, usage) return key } export async function sign(alg: string, key: types.CryptoKey | Uint8Array, data: Uint8Array) { const cryptoKey = await getSigKey(alg, key, 'sign') checkKeyLength(alg, cryptoKey) const signature = await crypto.subtle.sign( subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data as Uint8Array, ) return new Uint8Array(signature) } export async function verify( alg: string, key: types.CryptoKey | Uint8Array, signature: Uint8Array, data: Uint8Array, ) { const cryptoKey = await getSigKey(alg, key, 'verify') checkKeyLength(alg, cryptoKey) const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm) try { return await crypto.subtle.verify( algorithm, cryptoKey, signature as Uint8Array, data as Uint8Array, ) } catch { return false } } ================================================ FILE: src/lib/type_checks.ts ================================================ import type * as types from '../types.d.ts' const isObjectLike = (value: unknown) => typeof value === 'object' && value !== null export function isObject(input: unknown): input is T { if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') { return false } if (Object.getPrototypeOf(input) === null) { return true } let proto = input while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto) } return Object.getPrototypeOf(input) === proto } export function isDisjoint(...headers: Array) { const sources = headers.filter(Boolean) as object[] if (sources.length === 0 || sources.length === 1) { return true } let acc!: Set for (const header of sources) { const parameters = Object.keys(header) if (!acc || acc.size === 0) { acc = new Set(parameters) continue } for (const parameter of parameters) { if (acc.has(parameter)) { return false } acc.add(parameter) } } return true } export const isJWK = (key: unknown): key is types.JWK & { kty: string } => isObject(key) && typeof key.kty === 'string' export const isPrivateJWK = (key: types.JWK & { kty: string }) => key.kty !== 'oct' && ((key.kty === 'AKP' && typeof key.priv === 'string') || typeof key.d === 'string') export const isPublicJWK = (key: types.JWK & { kty: string }) => key.kty !== 'oct' && key.d === undefined && key.priv === undefined export const isSecretJWK = (key: types.JWK & { kty: string }) => key.kty === 'oct' && typeof key.k === 'string' ================================================ FILE: src/lib/validate_algorithms.ts ================================================ export function validateAlgorithms(option: string, algorithms?: string[]) { if ( algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string')) ) { throw new TypeError(`"${option}" option must be an array of strings`) } if (!algorithms) { return undefined } return new Set(algorithms) } ================================================ FILE: src/lib/validate_crit.ts ================================================ import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js' interface CritCheckHeader { b64?: boolean crit?: string[] [propName: string]: unknown } export function validateCrit( Err: typeof JWEInvalid | typeof JWSInvalid, recognizedDefault: Map, recognizedOption: { [propName: string]: boolean } | undefined, protectedHeader: CritCheckHeader | undefined, joseHeader: CritCheckHeader, ) { if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) { throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected') } if (!protectedHeader || protectedHeader.crit === undefined) { return new Set() } if ( !Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input: string) => typeof input !== 'string' || input.length === 0) ) { throw new Err( '"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present', ) } let recognized: Map if (recognizedOption !== undefined) { recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]) } else { recognized = recognizedDefault } for (const parameter of protectedHeader.crit) { if (!recognized.has(parameter)) { throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`) } if (joseHeader[parameter] === undefined) { throw new Err(`Extension Header Parameter "${parameter}" is missing`) } if (recognized.get(parameter) && protectedHeader[parameter] === undefined) { throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`) } } return new Set(protectedHeader.crit) } ================================================ FILE: src/types.d.ts ================================================ /** Generic JSON Web Key Parameters. */ export interface JWKParameters { /** JWK "kty" (Key Type) Parameter */ kty?: string /** * JWK "alg" (Algorithm) Parameter * * @see {@link https://github.com/panva/jose/issues/210 Algorithm Key Requirements} */ alg?: string /** JWK "key_ops" (Key Operations) Parameter */ key_ops?: string[] /** JWK "ext" (Extractable) Parameter */ ext?: boolean /** JWK "use" (Public Key Use) Parameter */ use?: string /** JWK "x5c" (X.509 Certificate Chain) Parameter */ x5c?: string[] /** JWK "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter */ x5t?: string /** JWK "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Parameter */ 'x5t#S256'?: string /** JWK "x5u" (X.509 URL) Parameter */ x5u?: string /** JWK "kid" (Key ID) Parameter */ kid?: string } /** Convenience interface for Public OKP JSON Web Keys */ export interface JWK_OKP_Public extends JWKParameters { /** OKP JWK "crv" (The Subtype of Key Pair) Parameter */ crv: string /** OKP JWK "x" (The public key) Parameter */ x: string } /** Convenience interface for Private OKP JSON Web Keys */ export interface JWK_OKP_Private extends JWK_OKP_Public { /** OKP JWK "d" (The Private Key) Parameter */ d: string } /** Convenience interface for Public AKP JSON Web Keys */ export interface JWK_AKP_Public extends JWKParameters { /** JWK "alg" (Algorithm) Parameter */ alg: string /** AKP JWK "pub" (The Public key) Parameter */ pub: string } /** Convenience interface for Private AKP JSON Web Keys */ export interface JWK_AKP_Private extends JWK_AKP_Public { /** AKP JWK "priv" (The Private Key) Parameter */ priv: string } /** Convenience interface for Public EC JSON Web Keys */ export interface JWK_EC_Public extends JWKParameters { /** EC JWK "crv" (Curve) Parameter */ crv: string /** EC JWK "x" (X Coordinate) Parameter */ x: string /** EC JWK "y" (Y Coordinate) Parameter */ y: string } /** Convenience interface for Private EC JSON Web Keys */ export interface JWK_EC_Private extends JWK_EC_Public { /** EC JWK "d" (ECC Private Key) Parameter */ d: string } /** Convenience interface for Public RSA JSON Web Keys */ export interface JWK_RSA_Public extends JWKParameters { /** RSA JWK "e" (Exponent) Parameter */ e: string /** RSA JWK "n" (Modulus) Parameter */ n: string } /** Convenience interface for Private RSA JSON Web Keys */ export interface JWK_RSA_Private extends JWK_RSA_Public { /** RSA JWK "d" (Private Exponent) Parameter */ d: string /** RSA JWK "dp" (First Factor CRT Exponent) Parameter */ dp: string /** RSA JWK "dq" (Second Factor CRT Exponent) Parameter */ dq: string /** RSA JWK "p" (First Prime Factor) Parameter */ p: string /** RSA JWK "q" (Second Prime Factor) Parameter */ q: string /** RSA JWK "qi" (First CRT Coefficient) Parameter */ qi: string } /** Convenience interface for oct JSON Web Keys */ export interface JWK_oct extends JWKParameters { /** Oct JWK "k" (Key Value) Parameter */ k: string } /** * JSON Web Key ({@link https://www.rfc-editor.org/rfc/rfc7517 JWK}). "RSA", "EC", "OKP", "AKP", and * "oct" key types are supported. * * @see {@link JWK_AKP_Public} * @see {@link JWK_AKP_Private} * @see {@link JWK_OKP_Public} * @see {@link JWK_OKP_Private} * @see {@link JWK_EC_Public} * @see {@link JWK_EC_Private} * @see {@link JWK_RSA_Public} * @see {@link JWK_RSA_Private} * @see {@link JWK_oct} */ export interface JWK extends JWKParameters { /** * - EC JWK "crv" (Curve) Parameter * - OKP JWK "crv" (The Subtype of Key Pair) Parameter */ crv?: string /** * - Private RSA JWK "d" (Private Exponent) Parameter * - Private EC JWK "d" (ECC Private Key) Parameter * - Private OKP JWK "d" (The Private Key) Parameter */ d?: string /** Private RSA JWK "dp" (First Factor CRT Exponent) Parameter */ dp?: string /** Private RSA JWK "dq" (Second Factor CRT Exponent) Parameter */ dq?: string /** RSA JWK "e" (Exponent) Parameter */ e?: string /** Oct JWK "k" (Key Value) Parameter */ k?: string /** RSA JWK "n" (Modulus) Parameter */ n?: string /** Private RSA JWK "p" (First Prime Factor) Parameter */ p?: string /** Private RSA JWK "q" (Second Prime Factor) Parameter */ q?: string /** Private RSA JWK "qi" (First CRT Coefficient) Parameter */ qi?: string /** * - EC JWK "x" (X Coordinate) Parameter * - OKP JWK "x" (The public key) Parameter */ x?: string /** EC JWK "y" (Y Coordinate) Parameter */ y?: string /** AKP JWK "pub" (Public Key) Parameter */ pub?: string /** AKP JWK "priv" (Private key) Parameter */ priv?: string } /** * @private * * @internal */ export interface GenericGetKeyFunction { /** * Dynamic key resolution function. No token components have been verified at the time of this * function call. * * If a suitable key for the token cannot be matched, throw an error instead. * * @param protectedHeader JWE or JWS Protected Header. * @param token The consumed JWE or JWS token. */ (protectedHeader: IProtectedHeader, token: IToken): Promise | ReturnKeyTypes } /** * Generic Interface for consuming operations dynamic key resolution. * * @param IProtectedHeader Type definition of the JWE or JWS Protected Header. * @param IToken Type definition of the consumed JWE or JWS token. */ export interface GetKeyFunction extends GenericGetKeyFunction< IProtectedHeader, IToken, CryptoKey | KeyObject | JWK | Uint8Array > {} /** * Flattened JWS definition for verify function inputs, allows payload as {@link !Uint8Array} for * detached signature validation. */ export interface FlattenedJWSInput { /** * The "header" member MUST be present and contain the value JWS Unprotected Header when the JWS * Unprotected Header value is non- empty; otherwise, it MUST be absent. This value is represented * as an unencoded JSON object, rather than as a string. These Header Parameter values are not * integrity protected. */ header?: JWSHeaderParameters /** * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When RFC7797 * "b64": false is used the value passed may also be a {@link !Uint8Array}. */ payload: string | Uint8Array /** * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWS Protected * Header)) when the JWS Protected Header value is non-empty; otherwise, it MUST be absent. These * Header Parameter values are integrity protected. */ protected?: string /** The "signature" member MUST be present and contain the value BASE64URL(JWS Signature). */ signature: string } /** * General JWS definition for verify function inputs, allows payload as {@link !Uint8Array} for * detached signature validation. */ export interface GeneralJWSInput { /** * The "payload" member MUST be present and contain the value BASE64URL(JWS Payload). When when * JWS Unencoded Payload ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) "b64": false is * used the value passed may also be a {@link !Uint8Array}. */ payload: string | Uint8Array /** * The "signatures" member value MUST be an array of JSON objects. Each object represents a * signature or MAC over the JWS Payload and the JWS Protected Header. */ signatures: Omit[] } /** * Flattened JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS * Unencoded Payload ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. */ export interface FlattenedJWS extends Partial { payload: string signature: string } /** * General JWS JSON Serialization Syntax token. Payload is returned as an empty string when JWS * Unencoded Payload ({@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}) is used. */ export interface GeneralJWS { payload: string signatures: Omit[] } /** Header Parameters common to JWE and JWS */ export interface JoseHeaderParameters { /** "kid" (Key ID) Header Parameter */ kid?: string /** "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter */ x5t?: string /** "x5c" (X.509 Certificate Chain) Header Parameter */ x5c?: string[] /** "x5u" (X.509 URL) Header Parameter */ x5u?: string /** "jku" (JWK Set URL) Header Parameter */ jku?: string /** "jwk" (JSON Web Key) Header Parameter */ jwk?: Pick /** "typ" (Type) Header Parameter */ typ?: string /** "cty" (Content Type) Header Parameter */ cty?: string } /** Recognized JWS Header Parameters, any other Header Members may also be present. */ export interface JWSHeaderParameters extends JoseHeaderParameters { /** * JWS "alg" (Algorithm) Header Parameter * * @see {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements} */ alg?: string /** * This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing * Input computation as per {@link https://www.rfc-editor.org/rfc/rfc7797 RFC7797}. */ b64?: boolean /** JWS "crit" (Critical) Header Parameter */ crit?: string[] /** Any other JWS Header member. */ [propName: string]: unknown } /** Recognized JWE Key Management-related Header Parameters. */ export interface JWEKeyManagementHeaderParameters { /** * ECDH-ES "apu" (Agreement PartyUInfo). This will be used as a JOSE Header Parameter and will be * used in ECDH's ConcatKDF. */ apu?: Uint8Array /** * ECDH-ES "apv" (Agreement PartyVInfo). This will be used as a JOSE Header Parameter and will be * used in ECDH's ConcatKDF. */ apv?: Uint8Array /** * @deprecated You should not use this parameter. It is only intended for testing and vector * validation purposes. */ p2c?: number /** * @deprecated You should not use this parameter. It is only intended for testing and vector * validation purposes. */ p2s?: Uint8Array /** * @deprecated You should not use this parameter. It is only intended for testing and vector * validation purposes. */ iv?: Uint8Array /** * @deprecated You should not use this parameter. It is only intended for testing and vector * validation purposes. */ epk?: CryptoKey | KeyObject } /** Flattened JWE JSON Serialization Syntax token. */ export interface FlattenedJWE { /** * The "aad" member MUST be present and contain the value BASE64URL(JWE AAD)) when the JWE AAD * value is non-empty; otherwise, it MUST be absent. A JWE AAD value can be included to supply a * base64url-encoded value to be integrity protected but not encrypted. */ aad?: string /** The "ciphertext" member MUST be present and contain the value BASE64URL(JWE Ciphertext). */ ciphertext: string /** * The "encrypted_key" member MUST be present and contain the value BASE64URL(JWE Encrypted Key) * when the JWE Encrypted Key value is non-empty; otherwise, it MUST be absent. */ encrypted_key?: string /** * The "header" member MUST be present and contain the value JWE Per- Recipient Unprotected Header * when the JWE Per-Recipient Unprotected Header value is non-empty; otherwise, it MUST be absent. * This value is represented as an unencoded JSON object, rather than as a string. These Header * Parameter values are not integrity protected. */ header?: JWEHeaderParameters /** * The "iv" member MUST be present and contain the value BASE64URL(JWE Initialization Vector) when * the JWE Initialization Vector value is non-empty; otherwise, it MUST be absent. */ iv?: string /** * The "protected" member MUST be present and contain the value BASE64URL(UTF8(JWE Protected * Header)) when the JWE Protected Header value is non-empty; otherwise, it MUST be absent. These * Header Parameter values are integrity protected. */ protected?: string /** * The "tag" member MUST be present and contain the value BASE64URL(JWE Authentication Tag) when * the JWE Authentication Tag value is non-empty; otherwise, it MUST be absent. */ tag?: string /** * The "unprotected" member MUST be present and contain the value JWE Shared Unprotected Header * when the JWE Shared Unprotected Header value is non-empty; otherwise, it MUST be absent. This * value is represented as an unencoded JSON object, rather than as a string. These Header * Parameter values are not integrity protected. */ unprotected?: JWEHeaderParameters } /** General JWE JSON Serialization Syntax token. */ export interface GeneralJWE extends Omit { recipients: Pick[] } /** Recognized JWE Header Parameters, any other Header members may also be present. */ export interface JWEHeaderParameters extends JoseHeaderParameters { /** * JWE "alg" (Algorithm) Header Parameter * * @see {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements} */ alg?: string /** * JWE "enc" (Encryption Algorithm) Header Parameter * * @see {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements} */ enc?: string /** JWE "crit" (Critical) Header Parameter */ crit?: string[] /** * JWE "zip" (Compression Algorithm) Header Parameter. * * The only supported value is `"DEF"` (DEFLATE). Requires the `CompressionStream` / * `DecompressionStream` APIs to be available in the runtime. * * @see {@link https://www.rfc-editor.org/rfc/rfc7516#section-4.1.3 JWE "zip" Header Parameter} */ zip?: string /** Any other JWE Header member. */ [propName: string]: unknown } /** Shared Interface with a "crit" property for all sign, verify, encrypt and decrypt operations. */ export interface CritOption { /** * An object with keys representing recognized "crit" (Critical) Header Parameter names. The value * for those is either `true` or `false`. `true` when the Header Parameter MUST be integrity * protected, `false` when it's irrelevant. * * This makes the "Extension Header Parameter "..." is not recognized" error go away. * * Use this when a given JWS/JWT/JWE profile requires the use of proprietary non-registered "crit" * (Critical) Header Parameters. This will only make sure the Header Parameter is syntactically * correct when provided and that it is optionally integrity protected. It will not process the * Header Parameter in any way or reject the operation if it is missing. You MUST still verify the * Header Parameter was present and process it according to the profile's validation steps after * the operation succeeds. * * The JWS extension Header Parameter `b64` is always recognized and processed properly. No other * registered Header Parameters that need this kind of default built-in treatment are currently * available. */ crit?: { [propName: string]: boolean } } /** JWE Decryption options. */ export interface DecryptOptions extends CritOption { /** * A list of accepted JWE "alg" (Algorithm) Header Parameter values. By default all "alg" * (Algorithm) Header Parameter values applicable for the used key/secret are allowed except for * all PBES2 Key Management Algorithms, these need to be explicitly allowed using this option. */ keyManagementAlgorithms?: string[] /** * A list of accepted JWE "enc" (Encryption Algorithm) Header Parameter values. By default all * "enc" (Encryption Algorithm) values applicable for the used key/secret are allowed. */ contentEncryptionAlgorithms?: string[] /** * (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 Count) Header Parameter * value. The PBKDF2 iteration count defines the algorithm's computational expense. By default * this value is set to 10000. */ maxPBES2Count?: number /** * Maximum allowed size (in bytes) of the decompressed plaintext when the JWE `"zip"` (Compression * Algorithm) Header Parameter is present. By default this value is set to 250000 (250 KB). The * value must be `0`, a positive safe integer, or `Infinity`. * * Set to `0` to reject all compressed JWEs during decryption. * * Set to `Infinity` to disable the decompressed size limit. */ maxDecompressedLength?: number } /** JWE Encryption options. */ export interface EncryptOptions extends CritOption {} /** JWT Claims Set verification options. */ export interface JWTClaimVerificationOptions { /** * Expected JWT "aud" (Audience) Claim value(s). * * This option makes the JWT "aud" (Audience) Claim presence required. */ audience?: string | string[] /** * Clock skew tolerance * * - In seconds when number (e.g. 5) * - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). * * Used when validating the JWT "nbf" (Not Before) and "exp" (Expiration Time) claims, and when * validating the "iat" (Issued At) claim if the {@link maxTokenAge `maxTokenAge` option} is set. */ clockTolerance?: string | number /** * Expected JWT "iss" (Issuer) Claim value(s). * * This option makes the JWT "iss" (Issuer) Claim presence required. */ issuer?: string | string[] /** * Maximum time elapsed (in seconds) from the JWT "iat" (Issued At) Claim value. * * - In seconds when number (e.g. 5) * - Resolved into a number of seconds when a string (e.g. "5 seconds", "10 minutes", "2 hours"). * * This option makes the JWT "iat" (Issued At) Claim presence required. */ maxTokenAge?: string | number /** * Expected JWT "sub" (Subject) Claim value. * * This option makes the JWT "sub" (Subject) Claim presence required. */ subject?: string /** * Expected JWT "typ" (Type) Header Parameter value. * * This option makes the JWT "typ" (Type) Header Parameter presence required. */ typ?: string /** Date to use when comparing NumericDate claims, defaults to `new Date()`. */ currentDate?: Date /** * Array of required Claim Names that must be present in the JWT Claims Set. Default is that: if * the {@link issuer `issuer` option} is set, then JWT "iss" (Issuer) Claim must be present; if the * {@link audience `audience` option} is set, then JWT "aud" (Audience) Claim must be present; if * the {@link subject `subject` option} is set, then JWT "sub" (Subject) Claim must be present; if * the {@link maxTokenAge `maxTokenAge` option} is set, then JWT "iat" (Issued At) Claim must be * present. */ requiredClaims?: string[] } /** JWS Verification options. */ export interface VerifyOptions extends CritOption { /** * A list of accepted JWS "alg" (Algorithm) Header Parameter values. By default all "alg" * (Algorithm) values applicable for the used key/secret are allowed. * * > [!NOTE]\ * > Unsecured JWTs (`{ "alg": "none" }`) are never accepted by this API. */ algorithms?: string[] } /** JWS Signing options. */ export interface SignOptions extends CritOption {} /** Recognized JWT Claims Set members, any other members may also be present. */ export interface JWTPayload { /** * JWT Issuer * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1 RFC7519#section-4.1.1} */ iss?: string /** * JWT Subject * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2 RFC7519#section-4.1.2} */ sub?: string /** * JWT Audience * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3 RFC7519#section-4.1.3} */ aud?: string | string[] /** * JWT ID * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7 RFC7519#section-4.1.7} */ jti?: string /** * JWT Not Before * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5 RFC7519#section-4.1.5} */ nbf?: number /** * JWT Expiration Time * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4 RFC7519#section-4.1.4} */ exp?: number /** * JWT Issued At * * @see {@link https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 RFC7519#section-4.1.6} */ iat?: number /** Any other JWT Claim Set member. */ [propName: string]: unknown } /** Flattened JWE JSON Serialization Syntax decryption result */ export interface FlattenedDecryptResult { /** JWE AAD. */ additionalAuthenticatedData?: Uint8Array /** Plaintext. */ plaintext: Uint8Array /** JWE Protected Header. */ protectedHeader?: JWEHeaderParameters /** JWE Shared Unprotected Header. */ sharedUnprotectedHeader?: JWEHeaderParameters /** JWE Per-Recipient Unprotected Header. */ unprotectedHeader?: JWEHeaderParameters } /** General JWE JSON Serialization Syntax decryption result */ export interface GeneralDecryptResult extends FlattenedDecryptResult {} /** Compact JWE decryption result */ export interface CompactDecryptResult { /** Plaintext. */ plaintext: Uint8Array /** JWE Protected Header. */ protectedHeader: CompactJWEHeaderParameters } /** Flattened JWS JSON Serialization Syntax verification result */ export interface FlattenedVerifyResult { /** JWS Payload. */ payload: Uint8Array /** JWS Protected Header. */ protectedHeader?: JWSHeaderParameters /** JWS Unprotected Header. */ unprotectedHeader?: JWSHeaderParameters } /** General JWS JSON Serialization Syntax verification result */ export interface GeneralVerifyResult extends FlattenedVerifyResult {} /** Compact JWS verification result */ export interface CompactVerifyResult { /** JWS Payload. */ payload: Uint8Array /** JWS Protected Header. */ protectedHeader: CompactJWSHeaderParameters } /** Signed JSON Web Token (JWT) verification result */ export interface JWTVerifyResult { /** JWT Claims Set. */ payload: PayloadType & JWTPayload /** JWS Protected Header. */ protectedHeader: JWTHeaderParameters } /** Encrypted JSON Web Token (JWT) decryption result */ export interface JWTDecryptResult { /** JWT Claims Set. */ payload: PayloadType & JWTPayload /** JWE Protected Header. */ protectedHeader: CompactJWEHeaderParameters } /** When key resolver functions are used this becomes part of successful resolves */ export interface ResolvedKey { /** Key resolved from the key resolver function. */ key: CryptoKey | Uint8Array } /** Recognized Compact JWS Header Parameters, any other Header Members may also be present. */ export interface CompactJWSHeaderParameters extends JWSHeaderParameters { alg: string } /** Recognized Signed JWT Header Parameters, any other Header Members may also be present. */ export interface JWTHeaderParameters extends CompactJWSHeaderParameters { b64?: true } /** Recognized Compact JWE Header Parameters, any other Header Members may also be present. */ export interface CompactJWEHeaderParameters extends JWEHeaderParameters { alg: string enc: string } /** JSON Web Key Set */ export interface JSONWebKeySet { keys: JWK[] } /** * {@link !KeyObject} is a representation of a key/secret available in the Node.js runtime. You may * use the Node.js runtime APIs {@link !createPublicKey}, {@link !createPrivateKey}, and * {@link !createSecretKey} to obtain a {@link !KeyObject} from your existing key material. */ export interface KeyObject { type: string } /** * {@link !CryptoKey} is a representation of a key/secret available in all supported runtimes. In * addition to the {@link key/import Key Import Functions} you may use the * {@link !SubtleCrypto.importKey} API to obtain a {@link !CryptoKey} from your existing key * material. */ export type CryptoKey = Extract< Awaited>, { type: string } > /** Generic interface for JWT producing classes. */ export interface ProduceJWT { /** * Set the "iss" (Issuer) Claim. * * @param issuer "Issuer" Claim value to set on the JWT Claims Set. */ setIssuer(issuer: string): this /** * Set the "sub" (Subject) Claim. * * @param subject "sub" (Subject) Claim value to set on the JWT Claims Set. */ setSubject(subject: string): this /** * Set the "aud" (Audience) Claim. * * @param audience "aud" (Audience) Claim value to set on the JWT Claims Set. */ setAudience(audience: string | string[]): this /** * Set the "jti" (JWT ID) Claim. * * @param jwtId "jti" (JWT ID) Claim value to set on the JWT Claims Set. */ setJti(jwtId: string): this /** * Set the "nbf" (Not Before) Claim. * * - If a `number` is passed as an argument it is used as the claim directly. * - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the * claim. * - If a `string` is passed as an argument it is resolved to a time span, and then added to the * current unix timestamp and used as the claim. * * Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 * day". * * Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", * "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", * "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an * alias for a year. * * If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets * subtracted from the current unix timestamp. A "from now" suffix can also be used for * readability when adding to the current unix timestamp. * * @param input "nbf" (Not Before) Claim value to set on the JWT Claims Set. */ setNotBefore(input: number | string | Date): this /** * Set the "exp" (Expiration Time) Claim. * * - If a `number` is passed as an argument it is used as the claim directly. * - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the * claim. * - If a `string` is passed as an argument it is resolved to a time span, and then added to the * current unix timestamp and used as the claim. * * Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 * day". * * Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", * "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", * "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an * alias for a year. * * If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets * subtracted from the current unix timestamp. A "from now" suffix can also be used for * readability when adding to the current unix timestamp. * * @param input "exp" (Expiration Time) Claim value to set on the JWT Claims Set. */ setExpirationTime(input: number | string | Date): this /** * Set the "iat" (Issued At) Claim. * * - If no argument is used the current unix timestamp is used as the claim. * - If a `number` is passed as an argument it is used as the claim directly. * - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the * claim. * - If a `string` is passed as an argument it is resolved to a time span, and then added to the * current unix timestamp and used as the claim. * * Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 * day". * * Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", * "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", * "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an * alias for a year. * * If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets * subtracted from the current unix timestamp. A "from now" suffix can also be used for * readability when adding to the current unix timestamp. * * @param input "iat" (Expiration Time) Claim value to set on the JWT Claims Set. */ setIssuedAt(input?: number | string | Date): this } ================================================ FILE: src/util/base64url.ts ================================================ /** * Base64URL encoding and decoding utilities * * @module */ import { encoder, decoder } from '../lib/buffer_utils.js' import { encodeBase64, decodeBase64 } from '../lib/base64.js' /** Decodes a Base64URL encoded input. */ export function decode(input: Uint8Array | string): Uint8Array { // @ts-ignore if (Uint8Array.fromBase64) { // @ts-ignore return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), { alphabet: 'base64url', }) } let encoded = input if (encoded instanceof Uint8Array) { encoded = decoder.decode(encoded) } encoded = encoded.replace(/-/g, '+').replace(/_/g, '/') try { return decodeBase64(encoded) } catch { throw new TypeError('The input to be decoded is not correctly encoded.') } } /** Encodes an input using Base64URL with no padding. */ export function encode(input: Uint8Array | string): string { let unencoded = input if (typeof unencoded === 'string') { unencoded = encoder.encode(unencoded) } // @ts-ignore if (Uint8Array.prototype.toBase64) { // @ts-ignore return unencoded.toBase64({ alphabet: 'base64url', omitPadding: true }) } return encodeBase64(unencoded).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_') } ================================================ FILE: src/util/decode_jwt.ts ================================================ /** * JSON Web Token (JWT) Claims Set Decoding (no validation, no signature checking) * * @module */ import { decode as b64u } from './base64url.js' import { decoder } from '../lib/buffer_utils.js' import { isObject } from '../lib/type_checks.js' import type * as types from '../types.d.ts' import { JWTInvalid } from './errors.js' /** * Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set types or * values. This does not validate the JWS Signature. For a proper Signed JWT Claims Set validation * and JWS signature verification use `jose.jwtVerify()`. For an encrypted JWT Claims Set validation * and JWE decryption use `jose.jwtDecrypt()`. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/jwt/decode'`. * * @example * * ```js * const claims = jose.decodeJwt(token) * console.log(claims) * ``` * * @param jwt JWT token in compact JWS serialization. */ export function decodeJwt( jwt: string, ): PayloadType & types.JWTPayload { if (typeof jwt !== 'string') throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string') const { 1: payload, length } = jwt.split('.') if (length === 5) throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded') if (length !== 3) throw new JWTInvalid('Invalid JWT') if (!payload) throw new JWTInvalid('JWTs must contain a payload') let decoded: Uint8Array try { decoded = b64u(payload) } catch { throw new JWTInvalid('Failed to base64url decode the payload') } let result: unknown try { result = JSON.parse(decoder.decode(decoded)) } catch { throw new JWTInvalid('Failed to parse the decoded payload as JSON') } if (!isObject(result)) throw new JWTInvalid('Invalid JWT Claims Set') return result } ================================================ FILE: src/util/decode_protected_header.ts ================================================ /** * JOSE Protected Header Decoding (JWE, JWS, all serialization syntaxes) * * @module */ import { decode as b64u } from './base64url.js' import { decoder } from '../lib/buffer_utils.js' import { isObject } from '../lib/type_checks.js' import type * as types from '../types.d.ts' /** JWE and JWS Header Parameters */ export type ProtectedHeaderParameters = types.JWSHeaderParameters & types.JWEHeaderParameters /** * Decodes the Protected Header of a JWE/JWS/JWT token utilizing any JOSE serialization. * * This function is exported (as a named export) from the main `'jose'` module entry point as well * as from its subpath export `'jose/decode/protected_header'`. * * @example * * ```js * const protectedHeader = jose.decodeProtectedHeader(token) * console.log(protectedHeader) * ``` * * @param token JWE/JWS/JWT token in any JOSE serialization. */ export function decodeProtectedHeader(token: string | object): ProtectedHeaderParameters { let protectedB64u!: unknown if (typeof token === 'string') { const parts = token.split('.') if (parts.length === 3 || parts.length === 5) { ;[protectedB64u] = parts } } else if (typeof token === 'object' && token) { if ('protected' in token) { protectedB64u = token.protected } else { throw new TypeError('Token does not contain a Protected Header') } } try { if (typeof protectedB64u !== 'string' || !protectedB64u) { throw new Error() } const result = JSON.parse(decoder.decode(b64u(protectedB64u!))) if (!isObject(result)) { throw new Error() } return result as ProtectedHeaderParameters } catch { throw new TypeError('Invalid Token or Protected Header formatting') } } ================================================ FILE: src/util/errors.ts ================================================ /** * JOSE module errors and error codes * * @module */ import type * as types from '../types.d.ts' /** * A generic Error that all other JOSE specific Error subclasses extend. * * @example * * Checking thrown error is a JOSE one * * ```js * if (err instanceof jose.errors.JOSEError) { * // ... * } * ``` */ export class JOSEError extends Error { /** * A unique error code for the particular error subclass. * * @ignore */ static code = 'ERR_JOSE_GENERIC' /** A unique error code for {@link JOSEError}. */ code = 'ERR_JOSE_GENERIC' /** @ignore */ constructor(message?: string, options?: { cause?: unknown }) { super(message, options) this.name = this.constructor.name // @ts-ignore Error.captureStackTrace?.(this, this.constructor) } } /** * An error subclass thrown when a JWT Claim Set member validation fails. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWT_CLAIM_VALIDATION_FAILED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWTClaimValidationFailed) { * // ... * } * ``` */ export class JWTClaimValidationFailed extends JOSEError { /** @ignore */ static override code = 'ERR_JWT_CLAIM_VALIDATION_FAILED' /** A unique error code for {@link JWTClaimValidationFailed}. */ override code = 'ERR_JWT_CLAIM_VALIDATION_FAILED' /** The Claim for which the validation failed. */ claim: string /** Reason code for the validation failure. */ reason: string /** * The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at * this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity * has however been verified. Claims Set verification happens after the JWS Signature or JWE * Decryption processes. */ payload: types.JWTPayload /** @ignore */ constructor( message: string, payload: types.JWTPayload, claim = 'unspecified', reason = 'unspecified', ) { super(message, { cause: { claim, reason, payload } }) this.claim = claim this.reason = reason this.payload = payload } } /** * An error subclass thrown when a JWT is expired. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWT_EXPIRED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWTExpired) { * // ... * } * ``` */ export class JWTExpired extends JOSEError implements JWTClaimValidationFailed { /** @ignore */ static override code = 'ERR_JWT_EXPIRED' /** A unique error code for {@link JWTExpired}. */ override code = 'ERR_JWT_EXPIRED' /** The Claim for which the validation failed. */ claim: string /** Reason code for the validation failure. */ reason: string /** * The parsed JWT Claims Set (aka payload). Other JWT claims may or may not have been verified at * this point. The JSON Web Signature (JWS) or a JSON Web Encryption (JWE) structures' integrity * has however been verified. Claims Set verification happens after the JWS Signature or JWE * Decryption processes. */ payload: types.JWTPayload /** @ignore */ constructor( message: string, payload: types.JWTPayload, claim = 'unspecified', reason = 'unspecified', ) { super(message, { cause: { claim, reason, payload } }) this.claim = claim this.reason = reason this.payload = payload } } /** * An error subclass thrown when a JOSE Algorithm is not allowed per developer preference. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JOSE_ALG_NOT_ALLOWED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JOSEAlgNotAllowed) { * // ... * } * ``` */ export class JOSEAlgNotAllowed extends JOSEError { /** @ignore */ static override code = 'ERR_JOSE_ALG_NOT_ALLOWED' /** A unique error code for {@link JOSEAlgNotAllowed}. */ override code = 'ERR_JOSE_ALG_NOT_ALLOWED' } /** * An error subclass thrown when a particular feature or algorithm is not supported by this * implementation or JOSE in general. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JOSE_NOT_SUPPORTED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JOSENotSupported) { * // ... * } * ``` */ export class JOSENotSupported extends JOSEError { /** @ignore */ static override code = 'ERR_JOSE_NOT_SUPPORTED' /** A unique error code for {@link JOSENotSupported}. */ override code = 'ERR_JOSE_NOT_SUPPORTED' } /** * An error subclass thrown when a JWE ciphertext decryption fails. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWE_DECRYPTION_FAILED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWEDecryptionFailed) { * // ... * } * ``` */ export class JWEDecryptionFailed extends JOSEError { /** @ignore */ static override code = 'ERR_JWE_DECRYPTION_FAILED' /** A unique error code for {@link JWEDecryptionFailed}. */ override code = 'ERR_JWE_DECRYPTION_FAILED' /** @ignore */ constructor(message = 'decryption operation failed', options?: { cause?: unknown }) { super(message, options) } } /** * An error subclass thrown when a JWE is invalid. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWE_INVALID') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWEInvalid) { * // ... * } * ``` */ export class JWEInvalid extends JOSEError { /** @ignore */ static override code = 'ERR_JWE_INVALID' /** A unique error code for {@link JWEInvalid}. */ override code = 'ERR_JWE_INVALID' } /** * An error subclass thrown when a JWS is invalid. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWS_INVALID') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWSInvalid) { * // ... * } * ``` */ export class JWSInvalid extends JOSEError { /** @ignore */ static override code = 'ERR_JWS_INVALID' /** A unique error code for {@link JWSInvalid}. */ override code = 'ERR_JWS_INVALID' } /** * An error subclass thrown when a JWT is invalid. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWT_INVALID') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWTInvalid) { * // ... * } * ``` */ export class JWTInvalid extends JOSEError { /** @ignore */ static override code = 'ERR_JWT_INVALID' /** A unique error code for {@link JWTInvalid}. */ override code = 'ERR_JWT_INVALID' } /** * An error subclass thrown when a JWK is invalid. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWK_INVALID') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWKInvalid) { * // ... * } * ``` */ export class JWKInvalid extends JOSEError { /** @ignore */ static override code = 'ERR_JWK_INVALID' /** A unique error code for {@link JWKInvalid}. */ override code = 'ERR_JWK_INVALID' } /** * An error subclass thrown when a JWKS is invalid. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWKS_INVALID') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWKSInvalid) { * // ... * } * ``` */ export class JWKSInvalid extends JOSEError { /** @ignore */ static override code = 'ERR_JWKS_INVALID' /** A unique error code for {@link JWKSInvalid}. */ override code = 'ERR_JWKS_INVALID' } /** * An error subclass thrown when no keys match from a JWKS. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWKS_NO_MATCHING_KEY') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWKSNoMatchingKey) { * // ... * } * ``` */ export class JWKSNoMatchingKey extends JOSEError { /** @ignore */ static override code = 'ERR_JWKS_NO_MATCHING_KEY' /** A unique error code for {@link JWKSNoMatchingKey}. */ override code = 'ERR_JWKS_NO_MATCHING_KEY' /** @ignore */ constructor( message = 'no applicable key found in the JSON Web Key Set', options?: { cause?: unknown }, ) { super(message, options) } } /** * An error subclass thrown when multiple keys match from a JWKS. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWKS_MULTIPLE_MATCHING_KEYS') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWKSMultipleMatchingKeys) { * // ... * } * ``` */ export class JWKSMultipleMatchingKeys extends JOSEError { /** @ignore */ [Symbol.asyncIterator]!: () => AsyncIterableIterator /** @ignore */ static override code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' /** A unique error code for {@link JWKSMultipleMatchingKeys}. */ override code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS' /** @ignore */ constructor( message = 'multiple matching keys found in the JSON Web Key Set', options?: { cause?: unknown }, ) { super(message, options) } } /** * Timeout was reached when retrieving the JWKS response. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWKS_TIMEOUT') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWKSTimeout) { * // ... * } * ``` */ export class JWKSTimeout extends JOSEError { /** @ignore */ static override code = 'ERR_JWKS_TIMEOUT' /** A unique error code for {@link JWKSTimeout}. */ override code = 'ERR_JWKS_TIMEOUT' /** @ignore */ constructor(message = 'request timed out', options?: { cause?: unknown }) { super(message, options) } } /** * An error subclass thrown when JWS signature verification fails. * * @example * * Checking thrown error is this one using a stable error code * * ```js * if (err.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { * // ... * } * ``` * * @example * * Checking thrown error is this one using `instanceof` * * ```js * if (err instanceof jose.errors.JWSSignatureVerificationFailed) { * // ... * } * ``` */ export class JWSSignatureVerificationFailed extends JOSEError { /** @ignore */ static override code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' /** A unique error code for {@link JWSSignatureVerificationFailed}. */ override code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED' /** @ignore */ constructor(message = 'signature verification failed', options?: { cause?: unknown }) { super(message, options) } } ================================================ FILE: tap/.browser.ts ================================================ import { test, expect } from '@playwright/test' import { createServer } from 'node:http' import { readFileSync } from 'node:fs' const script = readFileSync('./tap/run-browser.js', 'utf-8') test('passes tests', async ({ page }) => { const server = createServer((req, res) => { if (req.url === '/run-browser.js') { res.writeHead(200, { 'Content-Type': 'application/javascript' }) res.end(script) } else { res.writeHead(200, { 'Content-Type': 'text/html' }) res.end( '', ) } }) await new Promise((resolve) => server.listen(0, resolve)) const port = (server.address() as import('node:net').AddressInfo).port page.on('console', (msg) => { if (msg.type() === 'log') { console.log(msg.text()) } }) await page.goto(`http://localhost:${port}`) let stats do { await page.waitForTimeout(1000) stats = await page.evaluate(() => (globalThis as any).stats) } while (!stats) server.close() expect(stats.failed).toBe(0) }) ================================================ FILE: tap/.browsers.sh ================================================ #!/bin/bash ./node_modules/.bin/esbuild \ --log-level=warning \ --format=esm \ --bundle \ --minify \ --target=esnext \ --outfile=tap/run-browser.js \ tap/run-browser.ts : "${BROWSER:=chromium}" npx playwright test --project="$BROWSER" ================================================ FILE: tap/.bun.sh ================================================ echo "Using Bun `bun -v`" bun run tap/run-bun.ts ================================================ FILE: tap/.deno.sh ================================================ #!/bin/bash echo "Using $(deno --version | head -1)" deno run --allow-read --allow-net --allow-env --unstable-sloppy-imports tap/run-deno.ts ================================================ FILE: tap/.electron.sh ================================================ #!/bin/bash source .electron_flags.sh electron tap/run-electron.ts ================================================ FILE: tap/.node.sh ================================================ #!/bin/bash source .node_flags.sh node tap/run-node.ts WEB_CRYPTO_API=$? node tap/run-node.ts --keys='KeyObject' WEB_CRYPTO_API_WITH_KEYOBJECT=$? echo "" echo "Node.js with CryptoKey inputs" test $WEB_CRYPTO_API -eq 0 && echo " passed" || echo " failed" echo "" echo "Node.js with KeyObject inputs" test $WEB_CRYPTO_API_WITH_KEYOBJECT -eq 0 && echo " passed" || echo " failed" test $WEB_CRYPTO_API -eq 0 && test $WEB_CRYPTO_API_WITH_KEYOBJECT -eq 0 ================================================ FILE: tap/.workerd.sh ================================================ #!/bin/bash COMPATIBILITY_DATE=$(node -p "const d = require('workerd').compatibilityDate, t = new Date().toISOString().slice(0,10); d > t ? t : d") WORKERD_VERSION=$(npm ls --global --json | jq -r '.dependencies.workerd.version') echo "Using workerd $WORKERD_VERSION, compatibility date $COMPATIBILITY_DATE" ./node_modules/.bin/esbuild \ --log-level=warning \ --format=esm \ --bundle \ --define:WORKERD_VERSION=\"$WORKERD_VERSION\" \ --target=esnext \ --outfile=tap/run-workerd.js \ tap/run-workerd.ts generate_capnp() { local compatibility_flags=$1 cat < $(pwd)/tap/.workerd.capnp using Workerd = import "/workerd/workerd.capnp"; const config :Workerd.Config = ( services = [ (name = "main", worker = .tapWorker), ], ); const tapWorker :Workerd.Worker = ( modules = [ (name = "worker", esModule = embed "run-workerd.js") ], compatibilityDate = "$COMPATIBILITY_DATE", compatibilityFlags = $compatibility_flags ); EOT } run_test() { local compatibility_flags=$1 generate_capnp "$compatibility_flags" workerd test --verbose $(pwd)/tap/.workerd.capnp return $? } run_test "[]" NO_COMPAT=$? run_test '["nodejs_compat"]' COMPAT=$? echo "" echo "Workerd without nodejs_compat" test $NO_COMPAT -eq 0 && echo " passed" || echo " failed" echo "" echo "Workerd with nodejs_compat" test $COMPAT -eq 0 && echo " passed" || echo " failed" test $NO_COMPAT -eq 0 && test $COMPAT -eq 0 ================================================ FILE: tap/aes.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' import * as roundtrip from './encrypt.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('aes.ts') const algorithms = [ 'A128GCM', 'A192GCM', 'A256GCM', 'A128CBC-HS256', 'A192CBC-HS384', 'A256CBC-HS512', ] function title(algorithm: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${algorithm}` return result } function secretsFor(enc: string) { return [ keys.generateSecret(enc, { extractable: true }), crypto.getRandomValues( new Uint8Array(parseInt(enc.endsWith('GCM') ? enc.slice(1, 4) : enc.slice(-3)) >> 3), ), ] } for (const enc of algorithms) { const execute = async (t: typeof QUnit.assert) => { for await (const secret of secretsFor(enc)) { await roundtrip.jwe(t, lib, keys, 'dir', enc, secret) } } const jwt = async (t: typeof QUnit.assert) => { await roundtrip.jwt(t, lib, keys, 'dir', enc, await secretsFor(enc)[0]) } if (env.supported(enc)) { test(title(enc), execute) test(`${title(enc)} JWT`, jwt) } else { test(title(enc, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/aeskw.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' import * as roundtrip from './encrypt.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('aeskw.ts') const algorithms = ['A128KW', 'A192KW', 'A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW'] function title(algorithm: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${algorithm}` return result } function secretsFor(alg: string) { return [ keys.generateSecret(alg, { extractable: true }), crypto.getRandomValues(new Uint8Array(parseInt(alg.slice(1, 4), 10) >> 3)), ] } for (const alg of algorithms) { const execute = async (t: typeof QUnit.assert) => { for await (const secret of secretsFor(alg)) { await roundtrip.jwe(t, lib, keys, alg, 'A128GCM', secret) } } const jwt = async (t: typeof QUnit.assert) => { await roundtrip.jwt(t, lib, keys, alg, 'A128GCM', await secretsFor(alg)[0]) } if (env.supported(alg)) { test(title(alg), execute) test(`${title(alg)} JWT`, jwt) } else { test(title(alg, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/cookbook.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' // @ts-ignore import jwsVectors from '../cookbook/jws.mjs' // @ts-ignore import jweVectors from '../cookbook/jwe.mjs' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) const pubjwk = (jwk: jose.JWK) => { const { d, p, q, dp, dq, qi, priv, ...publicJwk } = jwk return publicJwk } { module('jws cookbook') const flattened = { Sign: lib.FlattenedSign, verify: lib.flattenedVerify, } const compact = { Sign: lib.CompactSign, verify: lib.compactVerify, } function supported(vector: any) { return env.supported(vector.input.alg) } const execute = (vector: any) => async (t: typeof QUnit.assert) => { const privateKey = await keys.importJWK(vector.input.key, vector.input.alg) const publicKey = await keys.importJWK(pubjwk(vector.input.key), vector.input.alg) if (vector.deterministic) { // sign and compare results are the same const runs = [[flattened, vector.output.json_flat]] if (vector.signing.protected?.b64 !== undefined) { runs.push([compact, vector.output.compact]) } for (const [serialization, expectedResult] of runs) { if (!expectedResult) { continue } const sign = new serialization.Sign(encode(vector.input.payload)) if (vector.signing.protected) { sign.setProtectedHeader(vector.signing.protected) } if (vector.signing.unprotected) { sign.setUnprotectedHeader(vector.signing.unprotected) } const result = await sign.sign(privateKey) if (vector.signing.protected?.b64 === false) { await serialization.verify( { ...result, payload: encode(vector.input.payload) }, publicKey, ) } else { await serialization.verify(result, publicKey) } if (typeof result === 'object') { Object.entries(expectedResult).forEach(([prop, expected]) => { if (prop === 'payload' && vector.signing.protected?.b64 === false) { return } t.equal(JSON.stringify(result[prop]), JSON.stringify(expected)) }) } else { t.equal(result, expectedResult) } } } else { const sign = new flattened.Sign(encode(vector.input.payload)) if (vector.signing.protected) { sign.setProtectedHeader(vector.signing.protected) } if (vector.signing.unprotected) { sign.setUnprotectedHeader(vector.signing.unprotected) } const result = await sign.sign(privateKey) await flattened.verify(result, publicKey) } if (vector.output.json_flat) { await flattened.verify(vector.output.json_flat, publicKey) } if (vector.output.compact) { await compact.verify(vector.output.compact, publicKey) } t.ok(1) } for (const vector of jwsVectors) { if (supported(vector)) { test(vector.title, execute(vector)) } else { test(`[not supported] ${vector.title}`, async (t) => { await t.rejects(execute(vector)(t)) }) } } } { module('jwe cookbook') const flattened = { Encrypt: lib.FlattenedEncrypt, decrypt: lib.flattenedDecrypt, } const compact = { Encrypt: lib.CompactEncrypt, decrypt: lib.compactDecrypt, } function supported(vector: any) { if (vector.input.zip && typeof globalThis.CompressionStream === 'undefined') { return false } return env.supported(vector.input.alg) && env.supported(vector.input.enc) } const toJWK = (input: string | jose.JWK) => { if (typeof input === 'string') { return { kty: 'oct', k: lib.base64url.encode(encode(input)), } } return input } const execute = (vector: any) => async (t: typeof QUnit.assert) => { const dir = vector.input.alg === 'dir' if (vector.deterministic) { // encrypt and compare results are the same for (const [serialization, expectedResult] of [ [flattened, vector.output.json_flat], [compact, vector.output.compact], ]) { if (!expectedResult) { continue } const encrypt = new serialization.Encrypt(encode(vector.input.plaintext)) if (vector.encrypting_content.protected) { encrypt.setProtectedHeader(vector.encrypting_content.protected) } if (vector.encrypting_content.unprotected) { encrypt.setSharedUnprotectedHeader(vector.encrypting_content.unprotected) } const { cek, iv } = vector.generated if (cek) { encrypt.setContentEncryptionKey(lib.base64url.decode(cek)) } if (iv) { encrypt.setInitializationVector(lib.base64url.decode(iv)) } if (vector.input.aad) { encrypt.setAdditionalAuthenticatedData(encode(vector.input.aad)) } const keyManagementParameters: jose.JWEKeyManagementHeaderParameters = {} if (vector.encrypting_key && vector.encrypting_key.iv) { keyManagementParameters.iv = lib.base64url.decode(vector.encrypting_key.iv) } if (vector.encrypting_key && vector.encrypting_key.iteration_count) { keyManagementParameters.p2c = vector.encrypting_key.iteration_count } if (vector.encrypting_key && vector.encrypting_key.salt) { keyManagementParameters.p2s = lib.base64url.decode(vector.encrypting_key.salt) } if (vector.encrypting_key && vector.encrypting_key.epk) { keyManagementParameters.epk = (await keys.importJWK( vector.encrypting_key.epk, vector.input.alg, )) as jose.KeyLike } if (Object.keys(keyManagementParameters).length !== 0) { encrypt.setKeyManagementParameters(keyManagementParameters) } const publicKey = await keys.importJWK( pubjwk(toJWK(vector.input.pwd || vector.input.key)), dir ? vector.input.enc : vector.input.alg, ) const result = await encrypt.encrypt(publicKey) if (typeof result === 'object') { Object.entries(expectedResult).forEach(([prop, expected]) => { t.equal(JSON.stringify(result[prop]), JSON.stringify(expected)) }) } else { t.equal(result, expectedResult) } } } else { const encrypt = new flattened.Encrypt(encode(vector.input.plaintext)) if (vector.encrypting_content.protected) { encrypt.setProtectedHeader(vector.encrypting_content.protected) } if (vector.encrypting_content.unprotected) { encrypt.setUnprotectedHeader(vector.encrypting_content.unprotected) } const privateKey = (await keys.importJWK( toJWK(vector.input.pwd || vector.input.key), dir ? vector.input.enc : vector.input.alg, )) as jose.KeyLike let publicKey if (privateKey.type === 'secret') { publicKey = privateKey } else { publicKey = await keys.importJWK( pubjwk(toJWK(vector.input.pwd || vector.input.key)), dir ? vector.input.enc : vector.input.alg, ) } const result = await encrypt.encrypt(publicKey) await flattened.decrypt(result, privateKey, { keyManagementAlgorithms: [vector.input.alg], contentEncryptionAlgorithms: [vector.input.enc], }) } const privateKey = await keys.importJWK( toJWK(vector.input.pwd || vector.input.key), dir ? vector.input.enc : vector.input.alg, ) if (vector.output.json_flat) { await flattened.decrypt(vector.output.json_flat, privateKey, { keyManagementAlgorithms: [vector.input.alg], contentEncryptionAlgorithms: [vector.input.enc], }) } if (vector.output.compact) { await compact.decrypt(vector.output.compact, privateKey, { keyManagementAlgorithms: [vector.input.alg], contentEncryptionAlgorithms: [vector.input.enc], }) } t.ok(1) } for (const vector of jweVectors) { if (supported(vector)) { test(vector.title, execute(vector)) } else { test(`[not supported] ${vector.title}`, async (t) => { await t.rejects(execute(vector)(t)) }) } } } } ================================================ FILE: tap/ecdh.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' import * as roundtrip from './encrypt.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('ecdh.ts') const kps: Record = {} type Vector = [string, jose.GenerateKeyPairOptions] const algorithms: Vector[] = [ ['ECDH-ES', { crv: 'P-256' }], ['ECDH-ES', { crv: 'P-384' }], ['ECDH-ES', { crv: 'P-521' }], ['ECDH-ES', { crv: 'X25519' }], ] function curve(options?: jose.GenerateKeyPairOptions) { return options?.crv || 'P-256' } function title(vector: Vector, supported = true) { const [alg, options] = vector let result = '' const crv = curve(options) if (!supported) { result = '[not supported] ' } result += `${alg} ${crv}` return result } for (const vector of algorithms) { const [alg, options] = vector const k = options?.crv || alg const execute = async (t: typeof QUnit.assert) => { if (!kps[k]) { kps[k] = await keys.generateKeyPair(alg, { ...options, extractable: true }) } await roundtrip.jwe(t, lib, keys, alg, 'A128GCM', kps[k]) } const jwt = async (t: typeof QUnit.assert) => { if (!kps[k]) { kps[k] = await keys.generateKeyPair(alg, { ...options, extractable: true }) } await roundtrip.jwt(t, lib, keys, alg, 'A128GCM', kps[k]) } if (env.supported(alg) && env.supported(curve(options))) { test(title(vector), execute) test(`${title(vector)} JWT`, jwt) } else { test(title(vector, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/encrypt.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' type keyType = Uint8Array | jose.CryptoKey | jose.KeyObject | jose.GenerateKeyPairResult function isKeyPair(input: keyType): input is jose.GenerateKeyPairResult { return 'publicKey' in input && 'privateKey' in input } async function getKeys( secretOrKeyPair: keyType, jwk: false, keys: Pick, ): Promise> async function getKeys( secretOrKeyPair: keyType, jwk: true, keys: Pick, ): Promise> async function getKeys( secretOrKeyPair: keyType, jwk: boolean, keys: Pick, ) { let dKey = isKeyPair(secretOrKeyPair) ? secretOrKeyPair.privateKey : secretOrKeyPair let eKey = isKeyPair(secretOrKeyPair) ? secretOrKeyPair.publicKey : secretOrKeyPair if (jwk) { // @ts-ignore return [await keys.exportJWK(eKey), await keys.exportJWK(dKey)] } return [eKey, dKey] } function jwkWithProps(jwk: jose.JWK, alg: string, enc: string) { jwk = structuredClone(jwk) jwk.alg = alg === 'dir' ? enc : alg jwk.use = 'enc' if (jwk.k) { if (jwk.alg.match(/^A\d{3}KW$/)) { jwk.key_ops = ['wrapKey', 'unwrapKey'] } else { jwk.key_ops = ['encrypt', 'decrypt'] } } else if (jwk.kty === 'RSA') { if (jwk.d) { jwk.key_ops = ['unwrapKey', 'decrypt'] } else { jwk.key_ops = ['wrapKey', 'encrypt'] } } else if (jwk.kty === 'EC') { if (jwk.d) { jwk.key_ops = ['deriveBits'] } else { jwk.key_ops = [] } } else if (jwk.kty === 'OKP') { if (jwk.d) { jwk.key_ops = ['deriveBits'] } else { jwk.key_ops = [] } } return jwk } export async function jwe( t: typeof QUnit.assert, lib: typeof jose, keys: Pick, alg: string, enc: string, secretOrKeyPair: keyType, cleartext = crypto.getRandomValues(new Uint8Array(16)), ) { // Test Uint8Array, CryptoKey, and KeyObject key inputs { const [eKey, dKey] = await getKeys(secretOrKeyPair, false, keys) const aad = crypto.getRandomValues(new Uint8Array(16)) const jwe = await new lib.FlattenedEncrypt(cleartext) .setProtectedHeader({ alg, enc }) .setAdditionalAuthenticatedData(aad) .encrypt(eKey) for (const key of [dKey, async () => dKey]) { // @ts-ignore const decrypted = await lib.flattenedDecrypt(jwe, key, { keyManagementAlgorithms: [alg], contentEncryptionAlgorithms: [enc], }) t.deepEqual([...decrypted.plaintext], [...cleartext]) t.deepEqual([...decrypted.additionalAuthenticatedData!], [...aad]) } } if (secretOrKeyPair instanceof Uint8Array) return // Test JWK key input { const [eKey, dKey] = await getKeys(secretOrKeyPair, true, keys) const aad = crypto.getRandomValues(new Uint8Array(16)) const jwe = await new lib.FlattenedEncrypt(cleartext) .setProtectedHeader({ alg, enc }) .setAdditionalAuthenticatedData(aad) .encrypt(eKey) { await new lib.FlattenedEncrypt(cleartext) .setProtectedHeader({ alg, enc }) .setAdditionalAuthenticatedData(aad) .encrypt(jwkWithProps(eKey, alg, enc)) } for (const key of [ dKey, async () => dKey, jwkWithProps(dKey, alg, enc), async () => jwkWithProps(dKey, alg, enc), ]) { // @ts-ignore const decrypted = await lib.flattenedDecrypt(jwe, key, { keyManagementAlgorithms: [alg], contentEncryptionAlgorithms: [enc], }) t.deepEqual([...decrypted.plaintext], [...cleartext]) t.deepEqual([...decrypted.additionalAuthenticatedData!], [...aad]) } } } export async function jwt( t: typeof QUnit.assert, lib: typeof jose, keys: Pick, alg: string, enc: string, secretOrKeyPair: Uint8Array | jose.KeyLike | jose.GenerateKeyPairResult, ) { const [eKey, dKey] = await getKeys(secretOrKeyPair, false, keys) const jwt = await new lib.EncryptJWT({ foo: 'bar', '🤷‍♂️': '🤷‍♀️' }) .setProtectedHeader({ alg, enc, '🤷‍♂️': '🤷‍♀️' }) .encrypt(eKey) for (const key of [dKey, async () => dKey]) { // @ts-ignore const decrypted = await lib.jwtDecrypt(jwt, key, { keyManagementAlgorithms: [alg], contentEncryptionAlgorithms: [enc], }) if (enc) { t.propContains(decrypted, { payload: { foo: 'bar', '🤷‍♂️': '🤷‍♀️', }, protectedHeader: { alg, enc, '🤷‍♂️': '🤷‍♀️', }, }) } else { t.propContains(decrypted, { payload: { foo: 'bar', }, protectedHeader: { alg, '🤷‍♂️': '🤷‍♀️', }, }) } } } ================================================ FILE: tap/env.ts ================================================ // @ts-ignore export const isBun = typeof Bun !== 'undefined' // @ts-ignore export const isElectron = typeof process !== 'undefined' && process.versions?.electron !== undefined // @ts-ignore export const isDeno = typeof Deno !== 'undefined' export const isBrowser = typeof navigator !== 'undefined' && navigator.userAgent?.startsWith?.('Mozilla/5.0 ') export const isWorkerd = typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers' // @ts-ignore export const isNode = !isBun && !isElectron && !isDeno && !isWorkerd && typeof process !== 'undefined' const BOWSER = 'https://cdn.jsdelivr.net/npm/bowser@2.11.0/src/bowser.js' let parsedUserAgent: any async function parseUserAgent() { const { default: Bowser } = await import(BOWSER) parsedUserAgent || (parsedUserAgent = Bowser.parse(window.navigator.userAgent)) return parsedUserAgent } async function isEngine(engine: string) { const userAgentData = await parseUserAgent() return userAgentData.engine.name === engine } export function isBrowserVersionAtLeast(version: number) { if (!parsedUserAgent) throw new Error() return parseInt(parsedUserAgent.browser.version.split('.')[0], 10) >= version } export const isBlink = isBrowser && (await isEngine('Blink')) export const isWebKit = isBrowser && (await isEngine('WebKit')) export const isGecko = isBrowser && (await isEngine('Gecko')) function isNodeVersionAtLeast(major: number, minor: number) { const parts = process.versions.node.split('.').map((i) => parseInt(i, 10)) return parts[0] > major || (parts[0] === major && parts[1] >= minor) } export function supported(identifier?: string, op?: string) { switch (identifier) { case 'RSA1_5': case 'X448': case 'Ed448': case 'ES256K': case 'secp256k1': return false } switch (identifier) { case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': return isNode && isNodeVersionAtLeast(24, 7) } if (isBlink) { switch (identifier) { case 'A192CBC-HS384': case 'A192GCM': case 'A192GCMKW': case 'A192KW': case 'PBES2-HS384+A192KW': return false } } if (isElectron) { switch (identifier) { case 'A128KW': case 'A192KW': case 'A256KW': case 'PBES2-HS256+A128KW': case 'PBES2-HS384+A192KW': case 'PBES2-HS512+A256KW': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': return false } } if (isBun && identifier === 'X25519') { switch (op) { case 'private jwk import': case 'public jwk import': case 'pem import': return true default: return false } } if (isDeno) { if ( (identifier === 'P-521' || identifier === 'ES512') && op !== 'pem import' && op !== 'public jwk import' && op !== 'private jwk import' ) { return false } } return true } ================================================ FILE: tap/fixtures.ts ================================================ export const KEYS = { Ed25519: { jwk: { crv: 'Ed25519', d: 'lootR5J6UdF-1tvFpnCZzr2N9AmRwgX92MzH_uuaGCQ', kty: 'OKP', x: 'lBZ9GShvbQEtyyaGs-0Nd4aurH7ERq6UOIvXGb5_tXA', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MC4CAQAwBQYDK2VwBCIEIJaKLUeSelHRftbbxaZwmc69jfQJkcIF/djMx/7rmhgk\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MCowBQYDK2VwAyEAlBZ9GShvbQEtyyaGs+0Nd4aurH7ERq6UOIvXGb5/tXA=\n' + '-----END PUBLIC KEY-----\n', x509: '-----BEGIN CERTIFICATE-----\n' + 'MIIBoTCCAVOgAwIBAgIUde5G4y+mtbb0eRISc7vnINRbSXkwBQYDK2VwMEUxCzAJ\n' + 'BgNVBAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l\n' + 'dCBXaWRnaXRzIFB0eSBMdGQwIBcNMjIxMDExMTIyMTUzWhgPMjEyMjA5MTcxMjIx\n' + 'NTNaMEUxCzAJBgNVBAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK\n' + 'DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwKjAFBgMrZXADIQCUFn0ZKG9tAS3L\n' + 'Joaz7Q13hq6sfsRGrpQ4i9cZvn+1cKNTMFEwHQYDVR0OBBYEFAATzoAtBcYTcOdY\n' + 'jkcQqsWXipnSMB8GA1UdIwQYMBaAFAATzoAtBcYTcOdYjkcQqsWXipnSMA8GA1Ud\n' + 'EwEB/wQFMAMBAf8wBQYDK2VwA0EApfw+9jSO0x0IorDfdr5ZVGRBVgrfrd9XhxqQ\n' + 'Krphj6cA4Ls9aMYAHf5w+OW9D/t3a9p6mYm78AKIdBsPEtT1AQ==\n' + '-----END CERTIFICATE-----\n', }, P256: { jwk: { crv: 'P-256', d: 'WBuVA9Z5CghAbxxspv2j1SiHur5BIR-PSDedvQOzun8', kty: 'EC', x: '4CsFZERaJG_bMfC8AZNAtXbMT4hS6UHTQDsFDVFmZWs', y: '-geyNWMPfctokhFckoSvx-tb_zfRoLZG1fqb3sqaNWw', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWBuVA9Z5CghAbxxs\n' + 'pv2j1SiHur5BIR+PSDedvQOzun+hRANCAATgKwVkRFokb9sx8LwBk0C1dsxPiFLp\n' + 'QdNAOwUNUWZla/oHsjVjD33LaJIRXJKEr8frW/830aC2RtX6m97KmjVs\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4CsFZERaJG/bMfC8AZNAtXbMT4hS\n' + '6UHTQDsFDVFmZWv6B7I1Yw99y2iSEVyShK/H61v/N9GgtkbV+pveypo1bA==\n' + '-----END PUBLIC KEY-----\n', x509: '-----BEGIN CERTIFICATE-----\n' + 'MIIBCTCBsAIJAP3LUepKb7jlMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAkNaMB4X\n' + 'DTIyMTAxMTEyMjUwOVoXDTIzMTAwNjEyMjUwOVowDTELMAkGA1UEBhMCQ1owWTAT\n' + 'BgcqhkjOPQIBBggqhkjOPQMBBwNCAATgKwVkRFokb9sx8LwBk0C1dsxPiFLpQdNA\n' + 'OwUNUWZla/oHsjVjD33LaJIRXJKEr8frW/830aC2RtX6m97KmjVsMAoGCCqGSM49\n' + 'BAMCA0gAMEUCIQDUxxIyJy8FQvrou0eGSLzAoNNKrYLIeI/OJzBIu6VkZwIgZvHb\n' + 'W78UlObaQoVHfmO5TbgLpIEiskde4STWKDYIZmI=\n' + '-----END CERTIFICATE-----\n', }, P384: { jwk: { crv: 'P-384', d: '5bt5RH4yyhwdtO2_gDHUgLn8aJqvlOaMHAznINEvkucDqiwrXL4ul42yNpx3biuM', kty: 'EC', x: 'sb8kfa8h1WbOGJC8lOMBEeXBYOM-EQNJjBSl7Ro5uiDI8Bk3cZpz0XPztPSHTXbw', y: '4tyg6L2MAzZ53Sj7l8O7yYYGOYtpNmuoV8vDSjjk_X7KQY-s0G3uT30uoSWvN0vH', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDlu3lEfjLKHB207b+A\n' + 'MdSAufxomq+U5owcDOcg0S+S5wOqLCtcvi6XjbI2nHduK4yhZANiAASxvyR9ryHV\n' + 'Zs4YkLyU4wER5cFg4z4RA0mMFKXtGjm6IMjwGTdxmnPRc/O09IdNdvDi3KDovYwD\n' + 'NnndKPuXw7vJhgY5i2k2a6hXy8NKOOT9fspBj6zQbe5PfS6hJa83S8c=\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEsb8kfa8h1WbOGJC8lOMBEeXBYOM+EQNJ\n' + 'jBSl7Ro5uiDI8Bk3cZpz0XPztPSHTXbw4tyg6L2MAzZ53Sj7l8O7yYYGOYtpNmuo\n' + 'V8vDSjjk/X7KQY+s0G3uT30uoSWvN0vH\n' + '-----END PUBLIC KEY-----\n', x509: '-----BEGIN CERTIFICATE-----\n' + 'MIIBRTCBzQIJAL/fppsFowYgMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAkNaMB4X\n' + 'DTIyMTAxMTEyMjUyMloXDTIzMTAwNjEyMjUyMlowDTELMAkGA1UEBhMCQ1owdjAQ\n' + 'BgcqhkjOPQIBBgUrgQQAIgNiAASxvyR9ryHVZs4YkLyU4wER5cFg4z4RA0mMFKXt\n' + 'Gjm6IMjwGTdxmnPRc/O09IdNdvDi3KDovYwDNnndKPuXw7vJhgY5i2k2a6hXy8NK\n' + 'OOT9fspBj6zQbe5PfS6hJa83S8cwCgYIKoZIzj0EAwIDZwAwZAIwRc/RKmaCus0F\n' + '7/q6D5fPafPTU1iLIcgpdK9pS+7tsCY8UZF2j5fSTFbYG39XaMEYAjB+piZKxCxh\n' + '/9jkdLz6ax866tMp9hQNTYU98lO44IQXhcEqh13zR3Bek4KhEnpkWiE=\n' + '-----END CERTIFICATE-----\n', }, P521: { jwk: { crv: 'P-521', d: 'Ae6uBN9ZtZD-yfB_--VPF_aEXTUlPENfj0mepT0chVvfi4BfWvs9NNCrv3YtWl1Q2FdYPop8Ch5_aiiiAH0QnmxH', kty: 'EC', x: 'AV6RoNRH5egig9TgU5CKHCf2H6XW7Rlqs_LZZKnQKJCZP_1x6RBw2Qgwwy8VCvUd_C_oxv45jU-boutt_ewcx7Wo', y: 'Abou9L-hVPMkzKNpJPGvhWnAhHNL1DKsXTAty-BmNBZPGtEwWsod8Vv2KN8wcIc7ts3dLedTPIn77O63V32t-cc5', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB7q4E31m1kP7J8H/7\n' + '5U8X9oRdNSU8Q1+PSZ6lPRyFW9+LgF9a+z000Ku/di1aXVDYV1g+inwKHn9qKKIA\n' + 'fRCebEehgYkDgYYABAFekaDUR+XoIoPU4FOQihwn9h+l1u0ZarPy2WSp0CiQmT/9\n' + 'cekQcNkIMMMvFQr1Hfwv6Mb+OY1Pm6Lrbf3sHMe1qAG6LvS/oVTzJMyjaSTxr4Vp\n' + 'wIRzS9QyrF0wLcvgZjQWTxrRMFrKHfFb9ijfMHCHO7bN3S3nUzyJ++zut1d9rfnH\n' + 'OQ==\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBXpGg1Efl6CKD1OBTkIocJ/Yfpdbt\n' + 'GWqz8tlkqdAokJk//XHpEHDZCDDDLxUK9R38L+jG/jmNT5ui62397BzHtagBui70\n' + 'v6FU8yTMo2kk8a+FacCEc0vUMqxdMC3L4GY0Fk8a0TBayh3xW/Yo3zBwhzu2zd0t\n' + '51M8ifvs7rdXfa35xzk=\n' + '-----END PUBLIC KEY-----\n', x509: '-----BEGIN CERTIFICATE-----\n' + 'MIIBkDCB8wIJALGXk5Wy5tmGMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAkNaMB4X\n' + 'DTIyMTAxMTEyMjUzNFoXDTIzMTAwNjEyMjUzNFowDTELMAkGA1UEBhMCQ1owgZsw\n' + 'EAYHKoZIzj0CAQYFK4EEACMDgYYABAFekaDUR+XoIoPU4FOQihwn9h+l1u0ZarPy\n' + '2WSp0CiQmT/9cekQcNkIMMMvFQr1Hfwv6Mb+OY1Pm6Lrbf3sHMe1qAG6LvS/oVTz\n' + 'JMyjaSTxr4VpwIRzS9QyrF0wLcvgZjQWTxrRMFrKHfFb9ijfMHCHO7bN3S3nUzyJ\n' + '++zut1d9rfnHOTAKBggqhkjOPQQDAgOBiwAwgYcCQgCfBR/x6atEB5KAaYmNOiKm\n' + 'OHhQISZU62ayPDipxsXf9vh4OK5WDdI4SmC1du07kAlwa2tFVSvz7vkMXGGXVYBr\n' + 'PQJBSjIXjpo07m26F0Jmv0OVX2on98+GN7xP8pRCviAuQj8UWKIQvwnj3esymVWb\n' + 'kmEjhnWo8H38/2wddwksoxHvinU=\n' + '-----END CERTIFICATE-----\n', }, RSA: { jwk: { d: 'B2Uk38NDKaU7ISmT2nqrQSuC5-fvZSJz4XHaWlVm-7lSV4OE1q7Z2fve9c93Q6Bkb8r6gwpTX_AFcWu6NbJmkascpOa5FhhGjffhxFxCKMRkT6ERt6PanHvLGKrk8UJsibjH_ds7UihOH-jd046jtoJjYHv7hmT3Rf0uttzGd6PDMHaZfp8xCjbAYZUFqW91O0hw3x1ZESsWyAS353JIjePzK5JmY7T7jbd-t6_M-u8i8-lv60Q8mvY0Np8iZadA3dFntIl0J4pwHGQGCC45mCAFIs6dgbVvyor7Eg82oDXHH3oOXIwYDi64Aw91EqxF8CdEByIV3_GphBzikEtTDw', dp: 'lRCClYTQvzgsVoVJjdyADVtceVsH7KfwpFxnrot_V40XJkUmiHj2yWGld8kB78q66faOavY-TFpmFlo-BQpEiXCDGzCqNCLJ-lF_ZnWyARfQHBBQPFHH2MqaNiS29yjpt7V4vc4z1DL_v5CaiR1wq9AZTABIUf8l2dAkHFrSTKk', dq: 'DMfbSrlSf71Ng5WuZxtENbQOSTOOP3rIyYibdPzbjJyb5raj09XQW_EJiaM45qbSwJfzFOnrFrQcoOJYmQTlHkVMcnXis2r1WXT0So32_D_kDevNZjHFG4PvqnK4ToV_GCGy1IbG4vHdjyMmHtG00zy9-02j7WV8-OerFP7QIAk', e: 'AQAB', kty: 'RSA', n: 'wINok6pE72n8ry3P6EIR-4B9oJmudAK_Q9mKkbG7eZlnP2Nvn5Fq6IhbsLn-ygT9OO_PzT23UdkxnjF8DUgSt7fVStQ40z4_lHVSPVIQeeDkML9Pipu1zUx6RqweUZdXnoN0JFNM93iUfJEvEukYxrWAeFVrRL2DU2E8DSrlJUyCO-c2NaO3YFsFdowW8pviMAnOs39VZNemiQtT-D4fg48dRX5bkrzjaRymhY7bWGelts0FhrNb6Jy-WMnSPxqpozbQ0ODI4kGQYSrKiUNLDfnSUAR-o5I4Hp2bkOrXt6lLccNu0PpPksVAWzTLyFtixxC9APKyUJSKQ7zNpKhqwQ', p: '4EqeWf4b6BJ_7v0Dwa_prSvDRylomA_IZpSDP_1Cqy24IFP1dBzdqm0gYbLNq3xQJkhptbBbC7xOPt4Zz0n-wJOHOkGf31m5-Gc4BiR9fTOEdFRtEQJyLfPu-7D9UbJH_bz_zsOQw9TDzllZp1H9X87AzkkkMfqjinxC749tJ1s', q: '27qzE-tl4V0HT_8PGI6YG2rphRfe5PArRSFgRmampXve6d_vgu5IpZEoNG7YGcT_J6tcdxF19-M2VEeNyqTPl2X91ba-RjkAByOTcT4n57-M4aK9yJt8un8BPBBNco42GmDnb5ZkC1UwJrrHc8QSmTjDBmvvjJYNUseWeYvCrRM', qi: 'yjY4FSvy_h8BXcbZJq1rLpMiEAqI3yMZ2wK8THJSfD37xbhil5PrDzzOluM_qkaC1aKTTAksFsx8f5AOEePCyjW095OBQe379EizSIC_EnByBdkfms8u5VOOKrZUI6I024QMhFsWCodaDDoNg-xXGD-viRtxpam9ZxVnyLRDOdg', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAg2iTqkTvafyv\n' + 'Lc/oQhH7gH2gma50Ar9D2YqRsbt5mWc/Y2+fkWroiFuwuf7KBP0478/NPbdR2TGe\n' + 'MXwNSBK3t9VK1DjTPj+UdVI9UhB54OQwv0+Km7XNTHpGrB5Rl1eeg3QkU0z3eJR8\n' + 'kS8S6RjGtYB4VWtEvYNTYTwNKuUlTII75zY1o7dgWwV2jBbym+IwCc6zf1Vk16aJ\n' + 'C1P4Ph+Djx1FfluSvONpHKaFjttYZ6W2zQWGs1vonL5YydI/GqmjNtDQ4MjiQZBh\n' + 'KsqJQ0sN+dJQBH6jkjgenZuQ6te3qUtxw27Q+k+SxUBbNMvIW2LHEL0A8rJQlIpD\n' + 'vM2kqGrBAgMBAAECggEAB2Uk38NDKaU7ISmT2nqrQSuC5+fvZSJz4XHaWlVm+7lS\n' + 'V4OE1q7Z2fve9c93Q6Bkb8r6gwpTX/AFcWu6NbJmkascpOa5FhhGjffhxFxCKMRk\n' + 'T6ERt6PanHvLGKrk8UJsibjH/ds7UihOH+jd046jtoJjYHv7hmT3Rf0uttzGd6PD\n' + 'MHaZfp8xCjbAYZUFqW91O0hw3x1ZESsWyAS353JIjePzK5JmY7T7jbd+t6/M+u8i\n' + '8+lv60Q8mvY0Np8iZadA3dFntIl0J4pwHGQGCC45mCAFIs6dgbVvyor7Eg82oDXH\n' + 'H3oOXIwYDi64Aw91EqxF8CdEByIV3/GphBzikEtTDwKBgQDgSp5Z/hvoEn/u/QPB\n' + 'r+mtK8NHKWiYD8hmlIM//UKrLbggU/V0HN2qbSBhss2rfFAmSGm1sFsLvE4+3hnP\n' + 'Sf7Ak4c6QZ/fWbn4ZzgGJH19M4R0VG0RAnIt8+77sP1Rskf9vP/Ow5DD1MPOWVmn\n' + 'Uf1fzsDOSSQx+qOKfELvj20nWwKBgQDburMT62XhXQdP/w8YjpgbaumFF97k8CtF\n' + 'IWBGZqale97p3++C7kilkSg0btgZxP8nq1x3EXX34zZUR43KpM+XZf3Vtr5GOQAH\n' + 'I5NxPifnv4zhor3Im3y6fwE8EE1yjjYaYOdvlmQLVTAmusdzxBKZOMMGa++Mlg1S\n' + 'x5Z5i8KtEwKBgQCVEIKVhNC/OCxWhUmN3IANW1x5Wwfsp/CkXGeui39XjRcmRSaI\n' + 'ePbJYaV3yQHvyrrp9o5q9j5MWmYWWj4FCkSJcIMbMKo0Isn6UX9mdbIBF9AcEFA8\n' + 'UcfYypo2JLb3KOm3tXi9zjPUMv+/kJqJHXCr0BlMAEhR/yXZ0CQcWtJMqQKBgAzH\n' + '20q5Un+9TYOVrmcbRDW0Dkkzjj96yMmIm3T824ycm+a2o9PV0FvxCYmjOOam0sCX\n' + '8xTp6xa0HKDiWJkE5R5FTHJ14rNq9Vl09EqN9vw/5A3rzWYxxRuD76pyuE6Ffxgh\n' + 'stSGxuLx3Y8jJh7RtNM8vftNo+1lfPjnqxT+0CAJAoGBAMo2OBUr8v4fAV3G2Sat\n' + 'ay6TIhAKiN8jGdsCvExyUnw9+8W4YpeT6w88zpbjP6pGgtWik0wJLBbMfH+QDhHj\n' + 'wso1tPeTgUHt+/RIs0iAvxJwcgXZH5rPLuVTjiq2VCOiNNuEDIRbFgqHWgw6DYPs\n' + 'Vxg/r4kbcaWpvWcVZ8i0QznY\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwINok6pE72n8ry3P6EIR\n' + '+4B9oJmudAK/Q9mKkbG7eZlnP2Nvn5Fq6IhbsLn+ygT9OO/PzT23UdkxnjF8DUgS\n' + 't7fVStQ40z4/lHVSPVIQeeDkML9Pipu1zUx6RqweUZdXnoN0JFNM93iUfJEvEukY\n' + 'xrWAeFVrRL2DU2E8DSrlJUyCO+c2NaO3YFsFdowW8pviMAnOs39VZNemiQtT+D4f\n' + 'g48dRX5bkrzjaRymhY7bWGelts0FhrNb6Jy+WMnSPxqpozbQ0ODI4kGQYSrKiUNL\n' + 'DfnSUAR+o5I4Hp2bkOrXt6lLccNu0PpPksVAWzTLyFtixxC9APKyUJSKQ7zNpKhq\n' + 'wQIDAQAB\n' + '-----END PUBLIC KEY-----\n', x509: '-----BEGIN CERTIFICATE-----\n' + 'MIICljCCAX4CCQDz0yP7UQpRIjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJD\n' + 'WjAeFw0yMjEwMTExMjI2NTVaFw0yMzEwMDYxMjI2NTVaMA0xCzAJBgNVBAYTAkNa\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwINok6pE72n8ry3P6EIR\n' + '+4B9oJmudAK/Q9mKkbG7eZlnP2Nvn5Fq6IhbsLn+ygT9OO/PzT23UdkxnjF8DUgS\n' + 't7fVStQ40z4/lHVSPVIQeeDkML9Pipu1zUx6RqweUZdXnoN0JFNM93iUfJEvEukY\n' + 'xrWAeFVrRL2DU2E8DSrlJUyCO+c2NaO3YFsFdowW8pviMAnOs39VZNemiQtT+D4f\n' + 'g48dRX5bkrzjaRymhY7bWGelts0FhrNb6Jy+WMnSPxqpozbQ0ODI4kGQYSrKiUNL\n' + 'DfnSUAR+o5I4Hp2bkOrXt6lLccNu0PpPksVAWzTLyFtixxC9APKyUJSKQ7zNpKhq\n' + 'wQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCgIGx+4WgwLtSGAxquNk7pMoewqyCx\n' + 'PO1v/3Snte5sHcn2+yBHaVeRrbnvniJIUNWnvGs/MuGA5fhsSgRBeXqOsLq0eprX\n' + 'YfPG7PG532NoaZ97Gp9X/YH3tTLCh/gyp0kQ2R0eci6f3J7nDzSPazHskEGTujHS\n' + 'uSczHvQsD8HdAd2CsZLPp02NgKhiG4VwMP2CeaUTG/62eZp/fGEAAqrXn4YoHsnY\n' + 'DPCORtzlkel/kV8r0/ktXt+fWoPIpn3dUaHDwi9V9RMkML9IMyLYFfLnpfxFMqdg\n' + '7KWOOFnaMmoDLlICZk7Ym3SjNt86hzmJ+eWiUuHAdFN4PeKj9wXd0t90\n' + '-----END CERTIFICATE-----\n', }, X25519: { jwk: { crv: 'X25519', d: 'WCN6m3ZhxgzgsmacDlbq_O347na7uQe6cW8T6ZJ19mw', kty: 'OKP', x: 'aq1ZuCRIE3XXXcRpbC_txuESJ4KFH8VwQlz1nVa8Ugk', }, pkcs8: '-----BEGIN PRIVATE KEY-----\n' + 'MC4CAQAwBQYDK2VuBCIEIFgjept2YcYM4LJmnA5W6vzt+O52u7kHunFvE+mSdfZs\n' + '-----END PRIVATE KEY-----\n', spki: '-----BEGIN PUBLIC KEY-----\n' + 'MCowBQYDK2VuAyEAaq1ZuCRIE3XXXcRpbC/txuESJ4KFH8VwQlz1nVa8Ugk=\n' + '-----END PUBLIC KEY-----\n', x509: undefined, }, 'ML-DSA-44': { jwk: { kty: 'AKP', alg: 'ML-DSA-44', pub: 'fYmD1Rx_jkoW9KG7Bs_5zyYEiWEZs15tYBxNdKq9NircZnvZBwwwaGbj0UsxJNc4Dyfp2IFAZZPO3rFCSUdpXHPrGRHwIVMzwiwfu2V7V02xoheW4mrkPThA3JRJSmNdsx6YGu37MaeJkIk6AlUexo46JfGrkRXZp_IyZxiL_L2dPrfwx-32j7WFI5sBadp7cDWfNkJjdQwW4puTe5Rw7h16GHb-DMOAKpfeMHujh7IYHuLCU6lVi90j1m8Ru0dxdmeQ1eY1vDnO7fNQKfzOLhpUNnj7BBZ24GTqFc-SN5HDCSCsSGKScTYYBwiSVTdSGG1GNqIiN2FgE4z1Jj6JFVB_OIUnl4sKbb3m8kB0BwtUPbkC0FVokGRUEGt6ba1Pc_IMpB5Gs3g9PFREI_C9o1yVW3NS2PzH_Vk4Tpf0N1K1kzIK_3IqekLfyqXmVDNsOovsS7Sw9TdmdWUNGRmhXFKRkex5VjpMIx7OwBGsYJCc4FhauWdrVtbkvHGggSpsla73ZcA4Vzh7aq47LMv0KS2YLp-DMn7SEohPHGg74118eLLn88yptxwtwt1dBFj8BKUfPrytuN1EIRQy34hwbkBLN9wDqhgn3Z3fvksRvmgN_4ZQ8YjeD-H3OFh5WJ_Rd66wHSl-YFat-_JF4UPcdlkNUbxPvDi5VL909Pe3VlwEZhT5otdtXQX4U3dUfqWKEh2kN0Q2lo8wbf3OMmBOFTfyX0eYa_5088ZnJvvliefn-TCDyc6WlcZrNqwBOF8N8-IN3b_8RPq-RuV8-mK-M83Hi4ElQB7Z44eZMmfUwFrozEG4Wq2K6MwQ_edG4dWeUVMCloTpGDFOtlLQlDoAN4m_sS2Lbwm_3ra29noUcK8_j10yy-hENE2Yluh1pIL-GoWZj3uYO-rEKVbszaagdE0DJ_uQcHUdNnBHKn64-cQ6xihXzxaeHx9OxkWWMKbzLtKpuYDK_X7EVvm8YTjl_oTsr2SWT2usjNJko32DhRV-OXLKKHo5FJpCy2bGFLXGG26CglUvgZQ2dyXiWeGVNKffOv1cQ5R_RlU2MpLiZ1bigy9hh4lu_XAHLfjQfhf71jeMuF4nEBWV-YOAjDTaDB2hcGqv_XcGXcmLWHqOWgc5Mb6lkb2zYs_oyOskmyFx6C0P7UrV8kCiN4zbuTqZNdNjlWL_QJUmU3vk6CpNa0XN1M3sLjZpOEsaqgRVPLcIDH-juVhyWiymuxe-8yNCOFSKxhscew08EQ9DEckP_iIA8qU2gcreHtvAS5VA5Emz1K2ypYe6oS3ogP-CX4nOAEfvjsb1HHJoclgiwjL1BtCLFgOE-0vn1M-nVOE6WbHGHoNKMJMHP2a3HQC7DmDfSOw5P6Cj5X7QVqhCY6tAGZWEPu3hUssp7K5UJePEdBn_LrErt4ucyXW6y1PAA2Fn8EuHaRyf2ggibDGnzq8E15m_R4LMvZAuGR0bN9jBTlm_x4ZQMqFwKkIdllkN1QTErazOyNsgU6fhA_20h5EIYT6-LqXr_Otj3Kp8MkJB9c3XNGoo5sbHTQCt0VNOHoxCFP_swiAJLtm743eOsI1M6naWLIqPagSCioosAvJYowypJQGvM-N3hBu8KUr0f911KRN7WqTAXTOHZ_vvTqcWKet0dFdh1EHuP3TrU8hSMciaphGvuK93T3gaWuJ6lcCkQndWvEo9S6FQB7eLU_ALKOQ3ROybUUkXgfyTkWDPxbHdeJCgMRv6Ig1PShPyxYb4ig', priv: '273AhMPiZWLlSQCY41yi1fMj6xavGH0btB23zMhI1uY', }, pkcs8: `-----BEGIN PRIVATE KEY----- MDQCAQAwCwYJYIZIAWUDBAMRBCKAINu9wITD4mVi5UkAmONcotXzI+sWrxh9G7Qd t8zISNbm -----END PRIVATE KEY-----`, spki: `-----BEGIN PUBLIC KEY----- MIIFMjALBglghkgBZQMEAxEDggUhAH2Jg9Ucf45KFvShuwbP+c8mBIlhGbNebWAc TXSqvTYq3GZ72QcMMGhm49FLMSTXOA8n6diBQGWTzt6xQklHaVxz6xkR8CFTM8Is H7tle1dNsaIXluJq5D04QNyUSUpjXbMemBrt+zGniZCJOgJVHsaOOiXxq5EV2afy MmcYi/y9nT638Mft9o+1hSObAWnae3A1nzZCY3UMFuKbk3uUcO4dehh2/gzDgCqX 3jB7o4eyGB7iwlOpVYvdI9ZvEbtHcXZnkNXmNbw5zu3zUCn8zi4aVDZ4+wQWduBk 6hXPkjeRwwkgrEhiknE2GAcIklU3UhhtRjaiIjdhYBOM9SY+iRVQfziFJ5eLCm29 5vJAdAcLVD25AtBVaJBkVBBrem2tT3PyDKQeRrN4PTxURCPwvaNclVtzUtj8x/1Z OE6X9DdStZMyCv9yKnpC38ql5lQzbDqL7Eu0sPU3ZnVlDRkZoVxSkZHseVY6TCMe zsARrGCQnOBYWrlna1bW5LxxoIEqbJWu92XAOFc4e2quOyzL9CktmC6fgzJ+0hKI TxxoO+NdfHiy5/PMqbccLcLdXQRY/ASlHz68rbjdRCEUMt+IcG5ASzfcA6oYJ92d 375LEb5oDf+GUPGI3g/h9zhYeVif0XeusB0pfmBWrfvyReFD3HZZDVG8T7w4uVS/ dPT3t1ZcBGYU+aLXbV0F+FN3VH6lihIdpDdENpaPMG39zjJgThU38l9HmGv+dPPG Zyb75Ynn5/kwg8nOlpXGazasAThfDfPiDd2//ET6vkblfPpivjPNx4uBJUAe2eOH mTJn1MBa6MxBuFqtiujMEP3nRuHVnlFTApaE6RgxTrZS0JQ6ADeJv7Eti28Jv962 tvZ6FHCvP49dMsvoRDRNmJbodaSC/hqFmY97mDvqxClW7M2moHRNAyf7kHB1HTZw Ryp+uPnEOsYoV88Wnh8fTsZFljCm8y7SqbmAyv1+xFb5vGE45f6E7K9klk9rrIzS ZKN9g4UVfjlyyih6ORSaQstmxhS1xhtugoJVL4GUNncl4lnhlTSn3zr9XEOUf0ZV NjKS4mdW4oMvYYeJbv1wBy340H4X+9Y3jLheJxAVlfmDgIw02gwdoXBqr/13Bl3J i1h6jloHOTG+pZG9s2LP6MjrJJshcegtD+1K1fJAojeM27k6mTXTY5Vi/0CVJlN7 5OgqTWtFzdTN7C42aThLGqoEVTy3CAx/o7lYclosprsXvvMjQjhUisYbHHsNPBEP QxHJD/4iAPKlNoHK3h7bwEuVQORJs9StsqWHuqEt6ID/gl+JzgBH747G9RxyaHJY IsIy9QbQixYDhPtL59TPp1ThOlmxxh6DSjCTBz9mtx0Auw5g30jsOT+go+V+0Fao QmOrQBmVhD7t4VLLKeyuVCXjxHQZ/y6xK7eLnMl1ustTwANhZ/BLh2kcn9oIImwx p86vBNeZv0eCzL2QLhkdGzfYwU5Zv8eGUDKhcCpCHZZZDdUExK2szsjbIFOn4QP9 tIeRCGE+vi6l6/zrY9yqfDJCQfXN1zRqKObGx00ArdFTTh6MQhT/7MIgCS7Zu+N3 jrCNTOp2liyKj2oEgoqKLALyWKMMqSUBrzPjd4QbvClK9H/ddSkTe1qkwF0zh2f7 706nFinrdHRXYdRB7j9061PIUjHImqYRr7ivd094GlriepXApEJ3VrxKPUuhUAe3 i1PwCyjkN0Tsm1FJF4H8k5Fgz8Wx3XiQoDEb+iINT0oT8sWG+Io= -----END PUBLIC KEY----- `, }, 'ML-DSA-65': { jwk: { kty: 'AKP', alg: 'ML-DSA-65', pub: 'hxPP5LvG83t2fJyfA1TUssJK_ydrzryrCHGZuKFxmnl5Y3sxHRCPW_JpHEoiIgR6kgELnwibZnueax1zFerTOTA7o0NwXHFiaEB-8AmqJI93DkvtbUOSTCixa3admQBKW_PtgMCVtaEVuuvCuOEFhOyuZkyfvnpBwUKOkz3t-O1wpgrSmf-rdPXOEv8YcsSn-xfLYPSLzPCnt7gnIX_fwtkgnXjref-QqjFKlKZE2e7MkmHeViJ4iGy78r3UzVhBHsmFGC0ZNc8-iT3muH5Sn0SXmNq-F2EoerWLIAsPxL2KE6UrqPAwTbHn1B5sAGWvhsVVLlFPI1s1JLVLBNRJ5vhif525xNIpMAMuAZrteD827pve3zQo9_GHjWgykj9VzM9PEcVmVqxZ5u41kUXsM4PWZF29Oh2sYsmJ2LdiJ9RcA91vRLG2DqEYm-V5JwIz8uxL17DUsEC7zYthvtqGASq05CbfPTBev33rQUv4H0Etz99U89WooTk0FisHDz1uEUilU_VY1tN5byIDitXNf0jnz3SIHDUUZARn7ll0YwO0jtksT68sQW3Liy6Exhlp1td0so2qZUrbVZasjyCOVuibwbwvrdpP3QRsoG5UqkAqk8Rm2iCpdQSg87pswOscgA8AC8TczGHNfXc9PqzAmbsEPKvmZuE60HLGzqpRqFULf3nyYUQUqbdmKJsKQ29LXeDVbyy3-fkTUDuYqNC2tBY7PkzHJSA9Z4hDC_BHEFxcelibScSNyf7y4lDVWnuJMXpQ0WRh3UkUPa007IerhixwxvBvFXQR-ytYinixvjirlcEF1wQI1DzE8KjOXYYuFPS4Yl8HeZHQ-64Q0RuxlKIRP3YvjZWh4IDVvEVs5ZLzZPbE3Twe0N5a7iCu0BzZWTeHNbcMoViFyJTpec2w3vVHeI4PJB-5HeI8xuh-9y8ytTau8QtMe4thoROoajizDQLrkw3e6ryJJ3R84i0oni4vmZWyLDilwcLqPOkQJCIDMjq7exdmVX5t3DtAW4F6Coz0z3sf7tGlSMVxA7izCoVbG0y2_l1P2h7fWBuEPT7PWlMdPqu9Pj5jqXY6jJ0nkaR_pp7dDhO1HKae5edcBYunHZqVQQjRZ_DvKzbPrDk5t6Xq9fdSkiAeP3B4qn5uU-nx7OaX7DRoVEnbbiEDynIRPSEY-Ts3alPJtBv8zuzaGNyX05Z9MyZ0w-VlC-WxOBdVEsIAp_4uJ3kQ3UsfE9DLJH8WPuDI4t4i2VnNNyFlI0XSUocc_0rWgqp2I1UzSzkVbklwkuFywPI645u4G2XAlfdd_wpjFGC-IUPXgpeSfspPwW15sBP-ITS-gwtvfzQVLpRS0euzN97xo_GMhNPZ4bW-YyZt8z_R8bsQ8ktfoP-5RUV-yzYDt0tA01QJsZdBLf5J_H7qP8l4c8V4hPe_CFL032obbxmAnVPAP69u2SaMBlL8azjk4wGVFQpQp1JqMJCao2W8ZImCVegkPZxhGbx0nkgVfyFx4ihMeDNM288JbGC4CGON8C02Q84rQzhwzZE83Y9rSe1Bb0fUMHMu6ihD5jLdeltuBL4ZdJlKgL24KZK5o5pq4_l9SyzGAjB1KAQnClNOB1SxV89CtILu-65wb17s0z3qw2-NF0B6UVlGQFebjbSyLQv2ARaETh_8cBiPugVMgBIV3K1KBwNyWejyI1ZDCssvIZHJCF2SRW3HmJerTiB23eGHFYKSLdxW7LEzoHIc2xZEc3pwR43gavjeoL0pNc-HNFV_c19wiH7Tnw3IHld_FfTqAIPnqKMNIY7D_D0DmFNTOdnzcipqKxUB0Avc-wr8Fz0gjeRpLH2iDSCJWtvWjoeYvHTktGsblDAM5j9xznwEvZfQvj8fTUnFxl3clkD6e9V1jrDQDkXfOtl-bDIv9PtMwamfJFu-z2ubF-gKytUewPNo10uhwr2TDNdUayCZDR2T3HoRLN8goIw2bFoPJ98LoPcSukEvKABjH0DiHNeqFELNZPx_uCx5N-YFkUZxHWA1QUoGhqQ3REtcT3c-SZf_TDFOPvws6bmwt4lcWpLmubOAJLFt6J8m8HCkVUshRdFzvHQm_0JEvA3JtyXZzvsPUv5njdk0nxTZktvsnqX054RQk5x8U-lBY-bK3uMOoFnHju45LMoHCUgGJi22eUm7nLGZEh84ZAbNlPLXpfavXvPJh21OW5EOAeuQ-yWNHY2xbmAiHNnb-J2VpZc1Vy82sxn8umFtKduuQuIQMOsf4qHqj5MzDY_1NjrM4Wm7XAiLC4MpQ22w9PWNQXSZWvo2fj8WUnfEibpgyRkoD2P25GRQqsRJ3-Ykl5bm_2Vfe6i3oXHOwQwZwKGXfAqXyo4iU1UI7e-qC4sj5U64oB_A_NSBaJJrZoQ2fVeGTnFxA4QMMoWCT0VlwBXK0B3jht8Xal3WcI-i9ctQB1-GrmwwgG2ttePHt1IKy69bSZE3FLkFicaHg6VxypG6ef8rVsmMrfpTATOnF5_iEaLNY9428HHGW0iz4vXwaE-MkYy7NK2KMPFiCB0ec9OjIROwayK4LREv4qknWHnVQRSm25Rr9DcVFXKj16Au7X1hv7TuVH7h25U', priv: '1X9VEr_iXMRwBvnSytEmHrtA-DpD6FWAUqMrDNlJVBg', }, pkcs8: `-----BEGIN PRIVATE KEY----- MDQCAQAwCwYJYIZIAWUDBAMSBCKAINV/VRK/4lzEcAb50srRJh67QPg6Q+hVgFKj KwzZSVQY -----END PRIVATE KEY-----`, spki: `-----BEGIN PUBLIC KEY----- MIIHsjALBglghkgBZQMEAxIDggehAIcTz+S7xvN7dnycnwNU1LLCSv8na868qwhx mbihcZp5eWN7MR0Qj1vyaRxKIiIEepIBC58Im2Z7nmsdcxXq0zkwO6NDcFxxYmhA fvAJqiSPdw5L7W1DkkwosWt2nZkASlvz7YDAlbWhFbrrwrjhBYTsrmZMn756QcFC jpM97fjtcKYK0pn/q3T1zhL/GHLEp/sXy2D0i8zwp7e4JyF/38LZIJ1463n/kKox SpSmRNnuzJJh3lYieIhsu/K91M1YQR7JhRgtGTXPPok95rh+Up9El5javhdhKHq1 iyALD8S9ihOlK6jwME2x59QebABlr4bFVS5RTyNbNSS1SwTUSeb4Yn+ducTSKTAD LgGa7Xg/Nu6b3t80KPfxh41oMpI/VczPTxHFZlasWebuNZFF7DOD1mRdvTodrGLJ idi3YifUXAPdb0Sxtg6hGJvleScCM/LsS9ew1LBAu82LYb7ahgEqtOQm3z0wXr99 60FL+B9BLc/fVPPVqKE5NBYrBw89bhFIpVP1WNbTeW8iA4rVzX9I5890iBw1FGQE Z+5ZdGMDtI7ZLE+vLEFty4suhMYZadbXdLKNqmVK21WWrI8gjlbom8G8L63aT90E bKBuVKpAKpPEZtogqXUEoPO6bMDrHIAPAAvE3MxhzX13PT6swJm7BDyr5mbhOtBy xs6qUahVC3958mFEFKm3ZiibCkNvS13g1W8st/n5E1A7mKjQtrQWOz5MxyUgPWeI QwvwRxBcXHpYm0nEjcn+8uJQ1Vp7iTF6UNFkYd1JFD2tNOyHq4YscMbwbxV0Efsr WIp4sb44q5XBBdcECNQ8xPCozl2GLhT0uGJfB3mR0PuuENEbsZSiET92L42VoeCA 1bxFbOWS82T2xN08HtDeWu4grtAc2Vk3hzW3DKFYhciU6XnNsN71R3iODyQfuR3i PMbofvcvMrU2rvELTHuLYaETqGo4sw0C65MN3uq8iSd0fOItKJ4uL5mVsiw4pcHC 6jzpECQiAzI6u3sXZlV+bdw7QFuBegqM9M97H+7RpUjFcQO4swqFWxtMtv5dT9oe 31gbhD0+z1pTHT6rvT4+Y6l2OoydJ5Gkf6ae3Q4TtRymnuXnXAWLpx2alUEI0Wfw 7ys2z6w5Obel6vX3UpIgHj9weKp+blPp8ezml+w0aFRJ224hA8pyET0hGPk7N2pT ybQb/M7s2hjcl9OWfTMmdMPlZQvlsTgXVRLCAKf+Lid5EN1LHxPQyyR/Fj7gyOLe ItlZzTchZSNF0lKHHP9K1oKqdiNVM0s5FW5JcJLhcsDyOuObuBtlwJX3Xf8KYxRg viFD14KXkn7KT8FtebAT/iE0voMLb380FS6UUtHrszfe8aPxjITT2eG1vmMmbfM/ 0fG7EPJLX6D/uUVFfss2A7dLQNNUCbGXQS3+Sfx+6j/JeHPFeIT3vwhS9N9qG28Z gJ1TwD+vbtkmjAZS/Gs45OMBlRUKUKdSajCQmqNlvGSJglXoJD2cYRm8dJ5IFX8h ceIoTHgzTNvPCWxguAhjjfAtNkPOK0M4cM2RPN2Pa0ntQW9H1DBzLuooQ+Yy3Xpb bgS+GXSZSoC9uCmSuaOaauP5fUssxgIwdSgEJwpTTgdUsVfPQrSC7vuucG9e7NM9 6sNvjRdAelFZRkBXm420si0L9gEWhE4f/HAYj7oFTIASFdytSgcDclno8iNWQwrL LyGRyQhdkkVtx5iXq04gdt3hhxWCki3cVuyxM6ByHNsWRHN6cEeN4Gr43qC9KTXP hzRVf3NfcIh+058NyB5XfxX06gCD56ijDSGOw/w9A5hTUznZ83IqaisVAdAL3PsK /Bc9II3kaSx9og0giVrb1o6HmLx05LRrG5QwDOY/cc58BL2X0L4/H01JxcZd3JZA +nvVdY6w0A5F3zrZfmwyL/T7TMGpnyRbvs9rmxfoCsrVHsDzaNdLocK9kwzXVGsg mQ0dk9x6ESzfIKCMNmxaDyffC6D3ErpBLygAYx9A4hzXqhRCzWT8f7gseTfmBZFG cR1gNUFKBoakN0RLXE93PkmX/0wxTj78LOm5sLeJXFqS5rmzgCSxbeifJvBwpFVL IUXRc7x0Jv9CRLwNybcl2c77D1L+Z43ZNJ8U2ZLb7J6l9OeEUJOcfFPpQWPmyt7j DqBZx47uOSzKBwlIBiYttnlJu5yxmRIfOGQGzZTy16X2r17zyYdtTluRDgHrkPsl jR2NsW5gIhzZ2/idlaWXNVcvNrMZ/LphbSnbrkLiEDDrH+Kh6o+TMw2P9TY6zOFp u1wIiwuDKUNtsPT1jUF0mVr6Nn4/FlJ3xIm6YMkZKA9j9uRkUKrESd/mJJeW5v9l X3uot6FxzsEMGcChl3wKl8qOIlNVCO3vqguLI+VOuKAfwPzUgWiSa2aENn1Xhk5x cQOEDDKFgk9FZcAVytAd44bfF2pd1nCPovXLUAdfhq5sMIBtrbXjx7dSCsuvW0mR NxS5BYnGh4OlccqRunn/K1bJjK36UwEzpxef4hGizWPeNvBxxltIs+L18GhPjJGM uzStijDxYggdHnPToyETsGsiuC0RL+KpJ1h51UEUptuUa/Q3FRVyo9egLu19Yb+0 7lR+4duV -----END PUBLIC KEY----- `, }, 'ML-DSA-87': { jwk: { kty: 'AKP', alg: 'ML-DSA-87', pub: 'DZXqaBATRN0GRtigxzkLxp7C9fFYxI7Gl-tdfXqJHbzVCTTvRfRwZcu3YmpsUYXBdX2pVsQ51QlqxMslKBRmfNCanBLcfd57qoEIb0K6GIKZGHxlsr9aXNjEGcKMo0ICon0LYTvTWrl72Oz-2yEA_abPK3_dBUFGAYQ6kOQhAHcT1CMmTTck23PnEd5WUpYfZOA9giFX9dNVrrdFWczj_vDOty81ObNKsxfVWT1nG7c60UCJxb2c2tMrBx3rp7Hfc_aOg54W5KHocJi0Eai0ok4buySTe0UCSCUTkeoCcdiABgOFBiXRYzpm3Lz4uot6hSgFpuh67fE9Zpgtn64vfI-O1mgcPrPPpd3yA92Jrq-dvXuM55w1RmA_hha3U5Sh2vm0tD1U57q945UppFReIv_8NAKBkxQ_vHil7ySm-m7IAM-sTUY86_IqMZqisxoz7Ff7ZR2vIiUm3-L0ow4B8uPsCv2ZlUoVXvMF6XQiOHsgqgP1rfH8DmfmPFudwiXrAW6wEmi10skPmkN92aC3TPG6nmaNryQ7f8J82yVmGxW9U7zMbg21qNkRGBi_1YwEt6D8V2pUWv5U1a4p4-Ma0f4uQG4g0odM-WGomlh7pZWZf3sffiPXk9wBrGisxtCJuaB5vtkheWxpEfWqnhc3QdOWfrsRg6P1h7M95SNVW0U8A38wwrqPOpzEnckCVdCrZz2b2KVln6a4twfINg1-3lEZR4rkEmTaTYlLFlXzbRFWBBPGATxeRxhQ_9N5VhHi7STWPFD5HIJyVqz436bbVvM6Py_oldT_xt_tWlPc0w4Pesy2CgaCPlJCnx6cjEg_sRBUcRkBoHqa7aZj4JFFm9bzEaiJ2MKfkHVT4xdbEimMHsD0HkIQpg5-zoB2Jsqgc6Qi3L57hZi-Q1V0G2lmdZ5WZkQ2m5hxle4hHtAmghgynK2p0qzDWHScxHcdd2sInYqQgMYbnvs04YYKWpIfndCUBs9q_EONN5tn8gfSwHEKlQ-KpplEL5kc-99h2uaZsxRlJOF6_z8EZ-aKaKY8jvoAV0g4kZJH5UKy_MkBiva6r-zXUmo88qJjXQatOOSdfvJUTiZiSfcpBQqF9SSDD9WWsgInaOCKO_fAFf9fuacXDMEj0esUx1YrVEe_77S5uObg-UrK405U1JhKJJvd7o8xQKxenv5BJdsbbyYQDbSSe9BrCqeHEgmfRHTXdSvl_3QOP0Ej-dT8YJJHZ1lrujU7Zg5f5Kg99tU5GdLMbHX2kt4F2a0NX09HikEemvUg4NLPhjOihfVkChr-zdF69nfsnTiaQrMgpIcl9jttN99_8Gju-LU8OWbb92m9RLxAUFP115v22f77YPoILm92IjMZMkxEhGneoclWhnudkyR7YoTBjCnT5b7AC9_05uls637FmVf7Ck8-MF4gLil3dstXi4g24bitYhxxqWwiqF4vsDouSGUnuKCMwx3TLsII_xk77TjpQP4vpLdYM3tn94AVlTMMhnI-OZkVJk-_mIbywCwRHlQb5nzVCc0BWlM1kb9PJys2IfciS8LWEoxeq9moDX5w72yJKoLN3CWpD3VdJAiW79zUaySw-IeW0XaHnlze5fYnOozG8lIeyQ9sMZasMiFovGnR3b7jyMtA38U33v16fouWuBILOu0m_QOpRDI9i3rjRM6hdC48zCtNSzc1_1VPYkWDSFK1oVAjdd8-2rjyqdPeUwnqD26VA9_d3R7x8ThrazdbRC8U1hr9jpqNHuZ4LGYu3Ui8wB-lSt9QMaHz517MY_zBEoNGyvbQWtlM7mvLu12KoMM7nvGrPJnvD-HmxTqsVQolD8_lIV5ao72yiKDpArVr6RuV4PpI0j_Wy4-yDCuwBW0gjnB9GvCwOTeByYXJT6Ul7dgHck4BbF3IyFgvmY--ceWr5mBrbAC9LJP_4Wf5O6ul3hFrhiG6zSV4zzBYLnEwfW6LNLEZjZKgmBYiC5s1xlxYWDdcQ5FGmLQ9uEDkr4VItXQWvIIdeBQPyujxmd965Mig9-Sa7SCyV_3wH8fQnGlvU-jJMGL0zvzB2gcu7hMLMagUBj1AKXj-UxpbX1i95f2TOiZDwMeCCszgvCjQ21XKg07TBXrrOiFcgcADgdo-HJr7O1T3ozOIulq1PDM7QZH6i3wDD0j3b0NCdqKCWqhfLXz2-FszyUHmA_GCzOLVzrLT2DcGWIcQbkvF0yZPgTyqKArKa8qytOerdH6oCJ0bRl96855sMVjuUdyVLX7XW_rVTwsOwV0gVAx8SrzovtDFeHRNl7BQKMsyQ1BjWu25jqKJ598vAi3LCZv0kMdiC24qPdgZU4e2aUkco11EnD6nJgdqsVFxufCl4BD_D9g5Wy42fJt4ZgNPAcbUf341KERyReeBEQj-qlPB3IUTIXcJw68GScebhxb0W_tGKMBC6-ip4QfNW7UTxUxVxmCV7h0yRnBBlkuUR1eYQwWRmEPjKd3dLHvgHtr266NQmE1tnKtJlsdPKb0ztrI9vogsENsgGNFQ2tHoeX8vqxcagGznlPVPfc3DlqjBSeTFQaPWvmCQHVKgxkbvffKzFQyFvXEqt6bGGtkwBoRJ_IIwtSeWQ2nFPBe3rlyKrtSnQFIMibJbbYvPVE03Cld9R61r-GGDSQz4aXekLzePEVwnxpe4mWJGco7ctQyE73PekL1uo2g0bRK-KgaE878OiLRBo5T7c633xEf2hMy9532M2GVdTZuoE0LL-wpAh9GmNdvJZc7g2sINvwZi778v2WHcYEKqXvdmrX-Shyh3QkzgIGZrDzM4UlxxUWaXfZ0Z6PNguk7Jafqf3xuUe9Z8zfAJl5c_VA3k8dn7IRg99hRsh-TGBCzqzgjJq4p4XMWP2QuxFTGSgHRe2GSCFzd5-lPjrj76ZyT3MPUxQe_bV2VE-Oys3MT-VkCCM8jFCANXdrfltG6jSiUZ2uJUWNqdNnxPglmmyrgff_m-5CyIRWYXQsIdZGspqdjzb4F6RbBKfL2PQlM6zUfo9JNmE8YQq815Nxkex8vDOrImnew312fZA6rRjr9_uE4lEbw3U7PFlCKBUPvPnsdgedjKYhiS0xU6iS1NDKOvYhcrkCkiU67EmFD4U0-OCv9Kpbb5bIxTJuv405NxJBElAMVI-ya0ns7D4-xUPn05E7PhtGZT0eHwItjT6omThTsTHwB_bQqYfNrjrObO1l1go2hQ-cUadZYsG5l47CB5RlFhANtaC8tiq4KJi48TmEEApB_0VwOI4EmI7SR0oaqx3HRXZfeGevCx2yC9aCYM4HqcyqP2g_1HwsOYzwq4XDEbK5Yl1dtYABxPoo7t8FBq2sSmfrBJWFv_nvreb_DPwbfoSeCy9knqvOktSQRrPMmo-nNGpandBvjmrjSk3EdeziAP7XNre5I-bn_2voDxkzGFtUM-wzlL379ASRGej8FkNWaOyqGP6Anq5PSJ', priv: 'LZSOlEPbU9S5_mSsMULffTyxZu6qKEOQ1nfEi2NCscg', }, pkcs8: `-----BEGIN PRIVATE KEY----- MDQCAQAwCwYJYIZIAWUDBAMTBCKAIC2UjpRD21PUuf5krDFC3308sWbuqihDkNZ3 xItjQrHI -----END PRIVATE KEY-----`, spki: `-----BEGIN PUBLIC KEY----- MIIKMjALBglghkgBZQMEAxMDggohAA2V6mgQE0TdBkbYoMc5C8aewvXxWMSOxpfr XX16iR281Qk070X0cGXLt2JqbFGFwXV9qVbEOdUJasTLJSgUZnzQmpwS3H3ee6qB CG9CuhiCmRh8ZbK/WlzYxBnCjKNCAqJ9C2E701q5e9js/tshAP2mzyt/3QVBRgGE OpDkIQB3E9QjJk03JNtz5xHeVlKWH2TgPYIhV/XTVa63RVnM4/7wzrcvNTmzSrMX 1Vk9Zxu3OtFAicW9nNrTKwcd66ex33P2joOeFuSh6HCYtBGotKJOG7skk3tFAkgl E5HqAnHYgAYDhQYl0WM6Zty8+LqLeoUoBaboeu3xPWaYLZ+uL3yPjtZoHD6zz6Xd 8gPdia6vnb17jOecNUZgP4YWt1OUodr5tLQ9VOe6veOVKaRUXiL//DQCgZMUP7x4 pe8kpvpuyADPrE1GPOvyKjGaorMaM+xX+2UdryIlJt/i9KMOAfLj7Ar9mZVKFV7z Bel0Ijh7IKoD9a3x/A5n5jxbncIl6wFusBJotdLJD5pDfdmgt0zxup5mja8kO3/C fNslZhsVvVO8zG4NtajZERgYv9WMBLeg/FdqVFr+VNWuKePjGtH+LkBuINKHTPlh qJpYe6WVmX97H34j15PcAaxorMbQibmgeb7ZIXlsaRH1qp4XN0HTln67EYOj9Yez PeUjVVtFPAN/MMK6jzqcxJ3JAlXQq2c9m9ilZZ+muLcHyDYNft5RGUeK5BJk2k2J SxZV820RVgQTxgE8XkcYUP/TeVYR4u0k1jxQ+RyCclas+N+m21bzOj8v6JXU/8bf 7VpT3NMOD3rMtgoGgj5SQp8enIxIP7EQVHEZAaB6mu2mY+CRRZvW8xGoidjCn5B1 U+MXWxIpjB7A9B5CEKYOfs6AdibKoHOkIty+e4WYvkNVdBtpZnWeVmZENpuYcZXu IR7QJoIYMpytqdKsw1h0nMR3HXdrCJ2KkIDGG577NOGGClqSH53QlAbPavxDjTeb Z/IH0sBxCpUPiqaZRC+ZHPvfYdrmmbMUZSThev8/BGfmimimPI76AFdIOJGSR+VC svzJAYr2uq/s11JqPPKiY10GrTjknX7yVE4mYkn3KQUKhfUkgw/VlrICJ2jgijv3 wBX/X7mnFwzBI9HrFMdWK1RHv++0ubjm4PlKyuNOVNSYSiSb3e6PMUCsXp7+QSXb G28mEA20knvQawqnhxIJn0R013Ur5f90Dj9BI/nU/GCSR2dZa7o1O2YOX+SoPfbV ORnSzGx19pLeBdmtDV9PR4pBHpr1IODSz4YzooX1ZAoa/s3RevZ37J04mkKzIKSH JfY7bTfff/Bo7vi1PDlm2/dpvUS8QFBT9deb9tn++2D6CC5vdiIzGTJMRIRp3qHJ VoZ7nZMke2KEwYwp0+W+wAvf9ObpbOt+xZlX+wpPPjBeIC4pd3bLV4uINuG4rWIc calsIqheL7A6LkhlJ7igjMMd0y7CCP8ZO+046UD+L6S3WDN7Z/eAFZUzDIZyPjmZ FSZPv5iG8sAsER5UG+Z81QnNAVpTNZG/TycrNiH3IkvC1hKMXqvZqA1+cO9siSqC zdwlqQ91XSQIlu/c1GsksPiHltF2h55c3uX2JzqMxvJSHskPbDGWrDIhaLxp0d2+ 48jLQN/FN979en6LlrgSCzrtJv0DqUQyPYt640TOoXQuPMwrTUs3Nf9VT2JFg0hS taFQI3XfPtq48qnT3lMJ6g9ulQPf3d0e8fE4a2s3W0QvFNYa/Y6ajR7meCxmLt1I vMAfpUrfUDGh8+dezGP8wRKDRsr20FrZTO5ry7tdiqDDO57xqzyZ7w/h5sU6rFUK JQ/P5SFeWqO9soig6QK1a+kbleD6SNI/1suPsgwrsAVtII5wfRrwsDk3gcmFyU+l Je3YB3JOAWxdyMhYL5mPvnHlq+Zga2wAvSyT/+Fn+Turpd4Ra4Yhus0leM8wWC5x MH1uizSxGY2SoJgWIgubNcZcWFg3XEORRpi0PbhA5K+FSLV0FryCHXgUD8ro8Znf euTIoPfkmu0gslf98B/H0Jxpb1PoyTBi9M78wdoHLu4TCzGoFAY9QCl4/lMaW19Y veX9kzomQ8DHggrM4Lwo0NtVyoNO0wV66zohXIHAA4HaPhya+ztU96MziLpatTwz O0GR+ot8Aw9I929DQnaiglqoXy189vhbM8lB5gPxgszi1c6y09g3BliHEG5LxdMm T4E8qigKymvKsrTnq3R+qAidG0ZfevOebDFY7lHclS1+11v61U8LDsFdIFQMfEq8 6L7QxXh0TZewUCjLMkNQY1rtuY6iieffLwItywmb9JDHYgtuKj3YGVOHtmlJHKNd RJw+pyYHarFRcbnwpeAQ/w/YOVsuNnybeGYDTwHG1H9+NShEckXngREI/qpTwdyF EyF3CcOvBknHm4cW9Fv7RijAQuvoqeEHzVu1E8VMVcZgle4dMkZwQZZLlEdXmEMF kZhD4ynd3Sx74B7a9uujUJhNbZyrSZbHTym9M7ayPb6ILBDbIBjRUNrR6Hl/L6sX GoBs55T1T33Nw5aowUnkxUGj1r5gkB1SoMZG733ysxUMhb1xKremxhrZMAaESfyC MLUnlkNpxTwXt65ciq7Up0BSDImyW22Lz1RNNwpXfUeta/hhg0kM+Gl3pC83jxFc J8aXuJliRnKO3LUMhO9z3pC9bqNoNG0SvioGhPO/Doi0QaOU+3Ot98RH9oTMved9 jNhlXU2bqBNCy/sKQIfRpjXbyWXO4NrCDb8GYu+/L9lh3GBCql73Zq1/kocod0JM 4CBmaw8zOFJccVFml32dGejzYLpOyWn6n98blHvWfM3wCZeXP1QN5PHZ+yEYPfYU bIfkxgQs6s4IyauKeFzFj9kLsRUxkoB0Xthkghc3efpT464++mck9zD1MUHv21dl RPjsrNzE/lZAgjPIxQgDV3a35bRuo0olGdriVFjanTZ8T4JZpsq4H3/5vuQsiEVm F0LCHWRrKanY82+BekWwSny9j0JTOs1H6PSTZhPGEKvNeTcZHsfLwzqyJp3sN9dn 2QOq0Y6/f7hOJRG8N1OzxZQigVD7z57HYHnYymIYktMVOoktTQyjr2IXK5ApIlOu xJhQ+FNPjgr/SqW2+WyMUybr+NOTcSQRJQDFSPsmtJ7Ow+PsVD59OROz4bRmU9Hh 8CLY0+qJk4U7Ex8Af20KmHza46zmztZdYKNoUPnFGnWWLBuZeOwgeUZRYQDbWgvL YquCiYuPE5hBAKQf9FcDiOBJiO0kdKGqsdx0V2X3hnrwsdsgvWgmDOB6nMqj9oP9 R8LDmM8KuFwxGyuWJdXbWAAcT6KO7fBQatrEpn6wSVhb/5763m/wz8G36EngsvZJ 6rzpLUkEazzJqPpzRqWp3Qb45q40pNxHXs4gD+1za3uSPm5/9r6A8ZMxhbVDPsM5 S9+/QEkRno/BZDVmjsqhj+gJ6uT0iQ== -----END PUBLIC KEY-----`, }, } ================================================ FILE: tap/generate_options.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' export default async ( QUnit: QUnit, lib: typeof jose, _lib: Pick, ) => { const { module, test } = QUnit module('generate_options.ts') for (const extractable of [undefined, true, false]) { test(`secret CryptoKey extractable: ${extractable ?? 'default (false)'}`, async (t) => { const expected = extractable ?? false const secret = (await lib.generateSecret('HS256', { extractable })) as CryptoKey t.equal(secret.extractable, expected) }) } for (const extractable of [undefined, true, false]) { test(`CryptoKeyPair extractable: ${extractable ?? 'default (false)'}`, async (t) => { const expected = extractable ?? false const kp = await lib.generateKeyPair('ES256', { extractable }) t.equal(kp.privateKey.extractable, expected) t.equal(kp.publicKey.extractable, true) }) } for (const modulusLength of [undefined, 2048, 3072]) { test(`RSA modulusLength ${modulusLength ?? 'default (2048)'}`, async (t) => { const expected = modulusLength ?? 2048 const { publicKey } = (await lib.generateKeyPair('RS256', { modulusLength, })) as CryptoKeyPair t.equal((publicKey.algorithm as RsaHashedKeyAlgorithm).modulusLength, expected) }) } } ================================================ FILE: tap/hmac.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' import * as roundtrip from './sign.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('hmac.ts') const algorithms = ['HS256', 'HS384', 'HS512'] function digestSizeSecretsFor(alg: string) { return [ keys.generateSecret(alg, { extractable: true }), crypto.getRandomValues(new Uint8Array(parseInt(alg.slice(2, 5), 10) >> 3)), ] } function nonDigestSizeSecretFor(alg: string) { const length = parseInt(alg.slice(2, 5), 10) >> 3 return [ crypto.getRandomValues(new Uint8Array(length - 1)), crypto.getRandomValues(new Uint8Array(length + 1)), ] } for (const alg of algorithms) { test(alg, async (t) => { for await (const secret of digestSizeSecretsFor(alg)) { await roundtrip.jws(t, lib, keys, alg, secret) } }) test(`${alg} w/ non-digest output length secrets`, async (t) => { for await (const secret of nonDigestSizeSecretFor(alg)) { await roundtrip.jws(t, lib, keys, alg, secret) } }) test(`${alg} JWT`, async (t) => { await roundtrip.jwt(t, lib, keys, alg, await digestSizeSecretsFor(alg)[0]) }) } } ================================================ FILE: tap/jwk.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import { KEYS } from './fixtures.js' import type * as jose from '../src/index.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('jwk.ts') type Vector = [string, JsonWebKey] const algorithms: Vector[] = [ ['ECDH-ES', KEYS.P256.jwk], ['ECDH-ES', KEYS.P384.jwk], ['ECDH-ES', KEYS.P521.jwk], ['ECDH-ES', KEYS.X25519.jwk], ['Ed25519', KEYS.Ed25519.jwk], ['EdDSA', KEYS.Ed25519.jwk], ['ES256', KEYS.P256.jwk], ['ES384', KEYS.P384.jwk], ['ES512', KEYS.P521.jwk], ['PS256', KEYS.RSA.jwk], ['PS384', KEYS.RSA.jwk], ['PS512', KEYS.RSA.jwk], ['RS256', KEYS.RSA.jwk], ['RS384', KEYS.RSA.jwk], ['RS512', KEYS.RSA.jwk], ['RSA-OAEP-256', KEYS.RSA.jwk], ['RSA-OAEP-384', KEYS.RSA.jwk], ['RSA-OAEP-512', KEYS.RSA.jwk], ['RSA-OAEP', KEYS.RSA.jwk], ['ML-DSA-44', KEYS['ML-DSA-44'].jwk], ['ML-DSA-65', KEYS['ML-DSA-65'].jwk], ['ML-DSA-87', KEYS['ML-DSA-87'].jwk], ] function publicJwk(jwk: JsonWebKey) { const { d, p, q, dp, dq, qi, k, priv, ...result } = jwk return result } for (const vector of algorithms.slice()) { algorithms.push([vector[0], publicJwk(vector[1])]) } function title(alg: string, jwk: JsonWebKey, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${alg} ` if (alg === 'EdDSA' || alg === 'ECDH-ES') { result += `${jwk.crv} ` } result += jwk.d || jwk.priv ? 'Private' : 'Public' result += ' JWK Import' return result } for (const vector of algorithms) { const [alg, jwk] = vector const execute = async (t: typeof QUnit.assert) => { const key = await lib.importJWK({ ...jwk, ext: true } as jose.JWK, alg) const exported = await lib.exportJWK(key) for (const prop of [...new Set([...Object.keys(jwk), ...Object.keys(exported)])]) { t.strictEqual( exported[prop as keyof JsonWebKey], jwk[prop as keyof JsonWebKey], `${prop} mismatch`, ) } if (env.isNode && lib.importJWK !== keys.importJWK) { const nCrypto = globalThis.process.getBuiltinModule('node:crypto') t.deepEqual( await lib.exportJWK( nCrypto[jwk.d || jwk.priv ? 'createPrivateKey' : 'createPublicKey']({ format: 'jwk', key: jwk, }), ), exported, ) } t.ok(1) } const op = `${jwk.d || jwk.priv ? 'private' : 'public'} jwk import` if (env.supported(alg, op) && env.supported(jwk.crv, op)) { test(title(alg, jwk), execute) } else { test(title(alg, jwk, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/jwks.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' export default (QUnit: QUnit, lib: typeof jose) => { const { module, test } = QUnit module('jwks.ts') const jwksUri = 'https://www.googleapis.com/oauth2/v3/certs' test('[createRemoteJWKSet] fetches the JWKSet', async (t: typeof QUnit.assert) => { const response = await fetch(jwksUri).then((r) => r.json()) const { alg, kid } = response.keys[0] const jwks = lib.createRemoteJWKSet(new URL(jwksUri)) t.false(jwks.coolingDown) t.false(jwks.fresh) t.equal(jwks.jwks(), undefined) await t.rejects(jwks({ alg: 'RS256' }), 'multiple matching keys found in the JSON Web Key Set') await t.rejects( jwks({ kid: 'foo', alg: 'RS256' }), 'no applicable key found in the JSON Web Key Set', ) t.ok(await Promise.all([jwks({ alg, kid }), jwks({ alg, kid })])) t.true(jwks.coolingDown) t.true(jwks.fresh) t.ok(jwks.jwks()) }) test('[createLocalJWKSet] establishes local JWKSet', async (t: typeof QUnit.assert) => { const keys = [ { e: 'AQAB', n: 'wAR7gpvDJx2cUR15R1gyBYxEXanhOIDzk7evzadBpNCEpf6HA6utqMrf8dZ3EXSslKPSPBD5Qrz63kc2u8y7NqzwJQIi_i5xR6AxAyWLG3_kOHBwxnct6talLCZqgr8pDwnyP1BPnIaNf2hZxgS-UZbHCAVycd1n2qCdyb4FzFhcaNtiOLg5VSfgvtOdhHQlDXW-DBvwatpd9HzzTP6l5MZRyQ-N_AoGbIfhNCZRUfnb-A8IBPSqXBWN4TEpt-0yHAOIhWnSpu66AYE4f1efZdHVFCTQZ13e5bS-5RQra4pfmGqU9hog1j1SpHnDTia-s__qGi43rev2MqzY-qeUlw', d: 'buWn14TSLtMhJo_ZLWU4bo_WJCoq0xFWm-eodyOz-9YZ5iycGXibcTLKJ8fvOHuj-KysjNhYvTybvqhuagQR08AJabZUM2zrK6zO4bxbHOS-EAKQf27xbAHPnzIIrb5tnivmZr6hXAsxyXWg84ZlzIVCKdXLhQuUIWZF-u_uNVeJSUTDMRVTL2J0mzAGTXqi-yHejapEeLS7lFXDe6cpDnBVXauJfB4GmSUOjxtdAEVW7uGNQJGarGwRz6l3Tpy_xQiYl8e_IrU1N6qAN_HJEBrdgfK7js93RcsxHGbtdnj1ylevZqGFpB1UXrWE4JSz3sJgyXrmKNFFWOCjalMccQ', p: '98OCXxur1omXdjfWkDubqkI3xRehVQryIhqt0go-1yLS4Nwa7KyrdAbzTo81bCHN0A-NlmIvHA4YZc8QUHftq65s4nCbb3g_CwTfGCJEVCvoaTO2EE6Pd8VrGu2PVsN4SM52Gc0TNJGS54yUhyCWDTi1onUBEg8gnqpMSoWuaVM', q: 'xmaRdSJf4wN5Vse5jiIjZy5jy9veHHhzXxTmW25epr9KTMERUDDCzX0bNbnF8jCvDFN5ebzcEe-9nkWyzJ17wVcJTouEfw8A5pBPcx6Gr8Kd8WIrUjuom4xu-4619kMItoV4j62_nq3p0QUGot_6CgUdq63PCp9Fh-sHv8wViy0', dp: '0OCXwbzfYu_-rCCpGFHYi3Jl-BhS4BJpTc02K3SNw-vM4ttNK6jqptfRObLMNAxPqg_iqxy9YKaVdQdbVqu0yF811rVepVw3sf96YatJ9bhKqJ566EaC91ONV1dd16TVfHPq5xeYEGKF-gXvlfgn6J-dqYeAzovIUVt7E_ydrJc', dq: 'sYDOnqe0dhyDkNp77ugoGIZujtMVcw9o2SaPujmSwUjfprANV1tozgQiNf0RVk-sLTD5u6r2ka2WTmY5Q8uaDy5Zi0ZTsoGv4pg2HN6wzcsnF_EmpRnvDcuk97eEoOD0iKf9Zz6h88vRJ0qB13Lf99r_4rtMQ0qgIKxscHKcy7k', qi: '7uvpgL15VFjd_zjhU0fPVeTzAa6Vg3P3Q2v5DLwLkAIlQDqF50maTYztxtxssVNJtEMIxKefwrmGkyVCXNhrGHZDoj7wj-2o0k878bQqtltCO2TPm9TSYZgW7dR3ji0t4Msc5DcrQL002M_Vxqr9MAunQcAsnulRTepQM2n-aOc', kty: 'RSA', kid: 'ZuLUAgyr6RQV3ERjDukHzOO_90rVbrPiE1vD_HtPFuM', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', d: 'LXGufKH6IBb4pUKh-iKX-ba1dBSGOkenUTHCd5STUG_JX3gsWUC5NPeTqrQzHkjV3otZytN3TgyZkr-QXDurEEtotD6Y1Ma85aljkuNfKTWWWoE1KwNmPZp0BQRB8lfGjmrNcC49tpw6owX4GvbqId_ifQupN32rY3t4qfq9xpO9SAqZF0oUMoS7xE0zChsCJmNYpD9jx87p5Vud1naeaZPlvwWW0ITV4kp2zjYSbBh5DkI52rSrGjkuzlsJ_lKJk5YB557OHhN9XTRBnjqlwwWevh6QAoUivqpcelcplgmfxTHoII1opovYXn8AVt-DbGSO_7LLJ0Sw9sJR5GAqcQ', p: '9RdDqZ3O73lH6nWUGi0abQRRfgvj-HM0zP7GSDQ185l-ZByletl1VuJ86qYJTUY8Q3Gagv6_eXmQMo_14-0wT_FPUMiTMYsjw5QNgFgjlJTM1AayS_U5ddix_Ut7Kti7EXgM0gsavsIazv2-xwCrFzD4sa-t2FWELzzWxgt8wbs', q: '2kX8MN8ItGnn7NnPx-0iqe8kkhy5s9gJRiD3mxN9E6xzRCnf488yhc3aBwI9kZzQtV0XVjX5VhCws5vnJv9b7KA8NATDhpGNrqy2h9ncmsjTTjafUg3jb6QG08kIKDR-A97Mc-MJbIUNzYs10BAG4z9wk7t1bdo4gZJEvjiXVHM', dp: 'Ahggy-I9Um6G3soCafbYsvXGfH09hXH2kYnbx-IqU9qL6e8UuugAyK1Gw_qHOdHP0gO2fkgO-sq_IK96OmhccVJuixIrr9CwjYtGUkJui2Z6GZW1EFEYHJmta6ypcMRJVOzhrynJILgn4nzolGq9C4WvmlUV9zND3eN3MloGxuE', dq: 'uXKWlusX2TjVvM0-FO2r8tdkqeNP_7XAA15FIPOI5Cszb6loOIQ0t6wy3puPteSXClBCYJPQ-MeLab4-wUpaTovBOq0FdpK53ruNBZUbMkMIDL6p1CxKnPKufkeh747RtfYYnSk7O4E8PfNV0CWdxHuE6W9ukNvEAIpGb5tjL3M', qi: '3BLQ03cHEmO8nUT7U8M_H_JciEWAH8XWh_9nihIhXzLKYbNmWM16Ah0F9DUg0GPeiG7e_08ZJ4X3oK1bHnnXdns6NSOEoULWfHl5LUY5PoFPYaBDy3f6td2SCTE83p1YzegXKysWEk1snA2ROq4UEfz1vL8v64RtwR3SvNrAyOI', kty: 'RSA', alg: 'RS256', kid: 'hJU8GvYjtifxLVuBDSNmhLBF19wQHaZvQhfpT3wKzpE', }, { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', kid: 'a-5xuiQoRqlLBtec9jRpXoGTVOP10SGnj2Und0CHHxw', }, ] const jwks = { keys: [ { e: 'AQAB', n: 'wAR7gpvDJx2cUR15R1gyBYxEXanhOIDzk7evzadBpNCEpf6HA6utqMrf8dZ3EXSslKPSPBD5Qrz63kc2u8y7NqzwJQIi_i5xR6AxAyWLG3_kOHBwxnct6talLCZqgr8pDwnyP1BPnIaNf2hZxgS-UZbHCAVycd1n2qCdyb4FzFhcaNtiOLg5VSfgvtOdhHQlDXW-DBvwatpd9HzzTP6l5MZRyQ-N_AoGbIfhNCZRUfnb-A8IBPSqXBWN4TEpt-0yHAOIhWnSpu66AYE4f1efZdHVFCTQZ13e5bS-5RQra4pfmGqU9hog1j1SpHnDTia-s__qGi43rev2MqzY-qeUlw', kty: 'RSA', kid: 'ZuLUAgyr6RQV3ERjDukHzOO_90rVbrPiE1vD_HtPFuM', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', kid: 'hJU8GvYjtifxLVuBDSNmhLBF19wQHaZvQhfpT3wKzpE', }, { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'a-5xuiQoRqlLBtec9jRpXoGTVOP10SGnj2Und0CHHxw', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', use: 'enc', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', use: 'sig', key_ops: [], }, { // this is not valid e: 'AQAB', kty: 'RSA', }, ], } const JWKS = lib.createLocalJWKSet(jwks) // Signed JWT { const [jwk] = keys const key = await lib.importJWK({ ...jwk, alg: 'PS256' }) const jwt = await new lib.SignJWT() .setProtectedHeader({ alg: 'PS256', kid: jwk.kid }) .sign(key) const { key: resolvedKey } = await lib.jwtVerify(jwt, JWKS) t.ok(resolvedKey) t.equal((resolvedKey as jose.KeyLike).type, 'public') } // Compact JWS { const [jwk] = keys const key = await lib.importJWK({ ...jwk, alg: 'PS256' }) const jws = await new lib.CompactSign(new Uint8Array(1)) .setProtectedHeader({ alg: 'PS256', kid: jwk.kid }) .sign(key) const { key: resolvedKey } = await lib.compactVerify(jws, JWKS) t.ok(resolvedKey) t.equal((resolvedKey as jose.KeyLike).type, 'public') } // Flattened JWS { const [jwk] = keys const key = await lib.importJWK({ ...jwk, alg: 'PS256' }) const jws = await new lib.FlattenedSign(new Uint8Array(1)) .setProtectedHeader({ alg: 'PS256' }) .setUnprotectedHeader({ kid: jwk.kid }) .sign(key) const { key: resolvedKey } = await lib.flattenedVerify(jws, JWKS) t.ok(resolvedKey) t.equal((resolvedKey as jose.KeyLike).type, 'public') } // General JWS { const [jwk] = keys const key = await lib.importJWK({ ...jwk, alg: 'PS256' }) const jws = await new lib.GeneralSign(new Uint8Array(1)) .addSignature(key) .setProtectedHeader({ alg: 'PS256' }) .setUnprotectedHeader({ kid: jwk.kid }) .sign() const { key: resolvedKey } = await lib.generalVerify(jws, JWKS) t.ok(resolvedKey) t.equal((resolvedKey as jose.KeyLike).type, 'public') } { await t.rejects( JWKS({ alg: 'RS256' }), 'multiple matching keys found in the JSON Web Key Set', ) // async iterator (KeyLike) let error = (await JWKS({ alg: 'RS256' }).catch( (err) => err, )) as jose.errors.JWKSMultipleMatchingKeys { const cache = new WeakSet() for await (const ko of error) { t.equal(ko.type, 'public') cache.add(ko) } error = (await JWKS({ alg: 'RS256' }).catch( (err) => err, )) as jose.errors.JWKSMultipleMatchingKeys let i = 0 for await (const ko of error) { i++ t.true(cache.has(ko)) } t.equal(i, 2) } } { const [, { kid }] = keys await t.rejects( JWKS({ alg: 'PS256', kid }), 'no applicable key found in the JSON Web Key Set', ) } { await JWKS({ alg: 'ES256' }) } }) } ================================================ FILE: tap/jws.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' import * as roundtrip from './sign.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('jws.ts') const algorithms = [ 'Ed25519', 'EdDSA', 'ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87', ] const kps: Record = {} function title(alg: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${alg}` return result } for (const alg of algorithms) { const execute = async (t: typeof QUnit.assert) => { if (!kps[alg]) { kps[alg] = await keys.generateKeyPair(alg, { extractable: true }) } await roundtrip.jws(t, lib, keys, alg, kps[alg]) } const jwt = async (t: typeof QUnit.assert) => { if (!kps[alg]) { kps[alg] = await keys.generateKeyPair(alg, { extractable: true }) } await roundtrip.jwt(t, lib, keys, alg, kps[alg]) } if (env.supported(alg)) { test(title(alg), execute) test(`${title(alg)} JWT`, jwt) } else { test(title(alg, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/keyobject-stub.ts ================================================ /// import { promisify } from 'node:util' import * as crypto from 'node:crypto' import { Buffer } from 'node:buffer' const generate = promisify(crypto.generateKeyPair) const stub: Pick< typeof import('../src/index.js'), 'exportJWK' | 'generateKeyPair' | 'generateSecret' | 'importJWK' > = { // @ts-expect-error exportJWK(key) { let k: crypto.KeyObject if (key instanceof Uint8Array) { k = crypto.createSecretKey(key) } else { // @ts-expect-error k = key } return (k as unknown as crypto.KeyObject).export({ format: 'jwk' }) }, // @ts-expect-error importJWK(jwk) { if (jwk.k) { return Buffer.from(jwk.k, 'base64url') } if (jwk.d || jwk.priv) { return crypto.createPrivateKey({ format: 'jwk', key: jwk as crypto.JsonWebKey }) } return crypto.createPublicKey({ format: 'jwk', key: jwk as crypto.JsonWebKey }) }, // @ts-expect-error generateSecret(alg) { let length: number switch (alg) { case 'HS256': case 'HS384': case 'HS512': case 'A128CBC-HS256': case 'A192CBC-HS384': case 'A256CBC-HS512': length = parseInt(alg.slice(-3), 10) break case 'A128KW': case 'A192KW': case 'A256KW': case 'A128GCMKW': case 'A192GCMKW': case 'A256GCMKW': case 'A128GCM': case 'A192GCM': case 'A256GCM': length = parseInt(alg.slice(1, 4), 10) break default: throw new Error('unreachable') } return crypto.createSecretKey(crypto.randomBytes(length >> 3)) }, // @ts-expect-error async generateKeyPair(alg, options) { switch (alg) { case 'RS256': case 'RS384': case 'RS512': case 'PS256': case 'PS384': case 'PS512': case 'RSA-OAEP': case 'RSA-OAEP-256': case 'RSA-OAEP-384': case 'RSA-OAEP-512': { const modulusLength = options?.modulusLength ?? 2048 const keypair = await generate('rsa', { modulusLength, publicExponent: 0x10001, }) return keypair } case 'ES256': return generate('ec', { namedCurve: 'P-256' }) case 'ES384': return generate('ec', { namedCurve: 'P-384' }) case 'ES512': return generate('ec', { namedCurve: 'P-521' }) case 'Ed25519': // Fall through case 'EdDSA': return generate('ed25519') case 'ECDH-ES': case 'ECDH-ES+A128KW': case 'ECDH-ES+A192KW': case 'ECDH-ES+A256KW': { const crv = options?.crv ?? 'P-256' switch (crv) { case 'P-256': case 'P-384': case 'P-521': return generate('ec', { namedCurve: crv }) case 'X25519': return generate('x25519') default: Error('unreachable') } } case 'ML-DSA-44': case 'ML-DSA-65': case 'ML-DSA-87': return generate(alg.toLowerCase()) default: Error('unreachable') } }, } export { stub } ================================================ FILE: tap/noop.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' // @ts-ignore export default (QUnit: QUnit, lib: typeof jose) => {} ================================================ FILE: tap/pbes2.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import type * as jose from '../src/index.js' import * as roundtrip from './encrypt.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('pbes2.ts') const algorithms = ['PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW'] function title(alg: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${alg}` return result } for (const alg of algorithms) { const execute = async (t: typeof QUnit.assert) => { const password = new TextEncoder().encode('letmein') await roundtrip.jwe(t, lib, keys, alg, 'A128GCM', password) } const jwt = async (t: typeof QUnit.assert) => { const password = new TextEncoder().encode('letmein') await roundtrip.jwt(t, lib, keys, alg, 'A128GCM', password) } if (env.supported(alg)) { test(title(alg), execute) test(`${title(alg)} JWT`, jwt) } else { test(title(alg, true), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/pem.ts ================================================ import type QUnit from 'qunit' import * as env from './env.js' import { KEYS } from './fixtures.js' import type * as jose from '../src/index.js' function normalize(pem: string) { return pem.replace(/\s+$/, '') } export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('pem.ts') type Vector = [string | string[], string] const algorithms: Vector[] = [ ['ES256', KEYS.P256.pkcs8], ['ES256', KEYS.P256.spki], ['ES256', KEYS.P256.x509], ['ES384', KEYS.P384.pkcs8], ['ES384', KEYS.P384.spki], ['ES384', KEYS.P384.x509], ['ES512', KEYS.P521.pkcs8], ['ES512', KEYS.P521.spki], ['ES512', KEYS.P521.x509], ['PS256', KEYS.RSA.pkcs8], ['PS256', KEYS.RSA.spki], ['PS256', KEYS.RSA.x509], ['PS384', KEYS.RSA.pkcs8], ['PS384', KEYS.RSA.spki], ['PS384', KEYS.RSA.x509], ['PS512', KEYS.RSA.pkcs8], ['PS512', KEYS.RSA.spki], ['PS512', KEYS.RSA.x509], ['RS256', KEYS.RSA.pkcs8], ['RS256', KEYS.RSA.spki], ['RS256', KEYS.RSA.x509], ['RS384', KEYS.RSA.pkcs8], ['RS384', KEYS.RSA.spki], ['RS384', KEYS.RSA.x509], ['RS512', KEYS.RSA.pkcs8], ['RS512', KEYS.RSA.spki], ['RS512', KEYS.RSA.x509], ['RSA-OAEP-256', KEYS.RSA.pkcs8], ['RSA-OAEP-256', KEYS.RSA.spki], ['RSA-OAEP-256', KEYS.RSA.x509], ['RSA-OAEP-384', KEYS.RSA.pkcs8], ['RSA-OAEP-384', KEYS.RSA.spki], ['RSA-OAEP-384', KEYS.RSA.x509], ['RSA-OAEP-512', KEYS.RSA.pkcs8], ['RSA-OAEP-512', KEYS.RSA.spki], ['RSA-OAEP-512', KEYS.RSA.x509], ['RSA-OAEP', KEYS.RSA.pkcs8], ['RSA-OAEP', KEYS.RSA.spki], ['RSA-OAEP', KEYS.RSA.x509], [['ECDH-ES', 'P-256'], KEYS.P256.pkcs8], [['ECDH-ES', 'P-256'], KEYS.P256.spki], [['ECDH-ES', 'P-256'], KEYS.P256.x509], [['ECDH-ES', 'P-384'], KEYS.P384.pkcs8], [['ECDH-ES', 'P-384'], KEYS.P384.spki], [['ECDH-ES', 'P-384'], KEYS.P384.x509], [['ECDH-ES', 'P-521'], KEYS.P521.pkcs8], [['ECDH-ES', 'P-521'], KEYS.P521.spki], [['ECDH-ES', 'P-521'], KEYS.P521.x509], [['ECDH-ES', 'X25519'], KEYS.X25519.pkcs8], [['ECDH-ES', 'X25519'], KEYS.X25519.spki], ['Ed25519', KEYS.Ed25519.pkcs8], ['Ed25519', KEYS.Ed25519.spki], ['Ed25519', KEYS.Ed25519.x509], [['EdDSA', 'Ed25519'], KEYS.Ed25519.pkcs8], [['EdDSA', 'Ed25519'], KEYS.Ed25519.spki], [['EdDSA', 'Ed25519'], KEYS.Ed25519.x509], ['ML-DSA-44', KEYS['ML-DSA-44'].pkcs8], ['ML-DSA-44', KEYS['ML-DSA-44'].spki], ['ML-DSA-65', KEYS['ML-DSA-65'].pkcs8], ['ML-DSA-65', KEYS['ML-DSA-65'].spki], ['ML-DSA-87', KEYS['ML-DSA-87'].pkcs8], ['ML-DSA-87', KEYS['ML-DSA-87'].spki], ] function title(alg: string, crv: string | undefined, pem: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${alg} ` if (crv) result += `${crv} ` result += pem.startsWith('-----BEGIN PRIVATE KEY-----') ? 'PKCS8 Private Key Import' : pem.startsWith('-----BEGIN PUBLIC KEY-----') ? 'SPKI Public Key Import' : 'X.509 Certificate Import' return result } for (const vector of algorithms) { const [, pem] = vector let [alg] = vector let crv!: string if (Array.isArray(alg)) { ;[alg, crv] = alg } let x509 = false let importFn: typeof lib.importSPKI | typeof lib.importPKCS8 | typeof lib.importX509 let exportFn: typeof lib.exportSPKI | typeof lib.exportPKCS8 switch (true) { case pem.startsWith('-----BEGIN PRIVATE KEY-----'): { importFn = lib.importPKCS8 exportFn = lib.exportPKCS8 break } case pem.startsWith('-----BEGIN PUBLIC KEY-----'): { importFn = lib.importSPKI exportFn = lib.exportSPKI break } case pem.startsWith('-----BEGIN CERTIFICATE-----'): { importFn = lib.importX509 exportFn = lib.exportSPKI x509 = true break } default: throw new Error() } const execute = async (t: typeof QUnit.assert) => { const k = await importFn(pem, alg as string, { extractable: true }) if (!x509) { t.strictEqual(normalize(await exportFn(k)), normalize(pem)) if (env.isNode && lib.importJWK !== keys.importJWK) { const nCrypto = globalThis.process.getBuiltinModule('node:crypto') if (pem.startsWith('-----BEGIN PRIVATE KEY-----')) { if (!alg.startsWith('ML-DSA-')) t.strictEqual( normalize(await exportFn(nCrypto.createPrivateKey(pem))), normalize(pem), ) } else { t.strictEqual(normalize(await exportFn(nCrypto.createPublicKey(pem))), normalize(pem)) } } } else { await exportFn(k) } t.ok(1) } if (env.supported(alg, 'pem import') && env.supported(crv, 'pem import')) { test(title(alg, crv, pem), execute) } else { test(title(alg, crv, pem, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/rsaes.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' import * as roundtrip from './encrypt.js' import * as env from './env.js' export default ( QUnit: QUnit, lib: typeof jose, keys: Pick, ) => { const { module, test } = QUnit module('rsaes.ts') const kps: Record = {} const algorithms = ['RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] function title(alg: string, supported = true) { let result = '' if (!supported) { result = '[not supported] ' } result += `${alg}` return result } for (const alg of algorithms) { const execute = async (t: typeof QUnit.assert) => { if (!kps[alg]) { kps[alg] = await keys.generateKeyPair(alg, { extractable: true }) } await roundtrip.jwe(t, lib, keys, alg, 'A128GCM', kps[alg]) } const jwt = async (t: typeof QUnit.assert) => { if (!kps[alg]) { kps[alg] = await keys.generateKeyPair(alg, { extractable: true }) } await roundtrip.jwt(t, lib, keys, alg, 'A128GCM', kps[alg]) } if (env.supported(alg)) { test(title(alg), execute) test(`${title(alg)} JWT`, jwt) } else { test(title(alg, false), async (t) => { await t.rejects(execute(t)) }) } } } ================================================ FILE: tap/run-browser.ts ================================================ import QUnit from 'qunit' import run from './run.js' import * as lib from '../src/index.js' run(QUnit, lib, lib, (stats) => { // @ts-ignore globalThis.stats = stats }) ================================================ FILE: tap/run-bun.ts ================================================ import QUnit from 'qunit' import run from './run.js' import * as lib from '../src/index.js' const stats: QUnit.DoneDetails = await new Promise((resolve) => { run(QUnit, lib, lib, resolve) }) if (stats?.failed !== 0) { // @ts-ignore process.exit(1) } ================================================ FILE: tap/run-deno.ts ================================================ import QUnit from 'qunit' import run from './run.js' import * as lib from '../src/index.js' run(QUnit, lib, lib, (stats) => { if (stats?.failed !== 0) { // @ts-ignore Deno.exit(1) } }) ================================================ FILE: tap/run-electron.ts ================================================ // @ts-ignore import { app } from 'electron' import QUnit from 'qunit' import run from './run.js' import * as lib from '../src/index.js' app.on('ready', () => { run(QUnit, lib, lib, (stats) => { if (stats?.failed !== 0) { // @ts-ignore app.exit(1) } else { app.exit(0) } }) }) ================================================ FILE: tap/run-node.ts ================================================ /// import { parseArgs } from 'node:util' import * as lib from '../src/index.js' import QUnit from 'qunit' import run from './run.js' const { values: { keys }, } = parseArgs({ options: { keys: { type: 'string' } } }) import { stub } from './keyobject-stub.js' const stats: QUnit.DoneDetails = await new Promise(async (resolve) => { run(QUnit, lib, keys === 'KeyObject' ? stub : lib, resolve) }) if (stats?.failed !== 0) { process.exitCode = 1 } ================================================ FILE: tap/run-workerd.ts ================================================ import QUnit from 'qunit' import run from './run.js' import * as lib from '../src/index.js' export default { async test() { await new Promise((resolve, reject) => { run(QUnit, lib, lib, (results) => { if (results?.failed !== 0) { reject() } else { // @ts-ignore resolve() } }) }) }, } ================================================ FILE: tap/run.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' const skipFetch = // @ts-ignore typeof fetch === 'undefined' || (typeof process !== 'undefined' && 'CITGM' in process.env) export default async ( QUnit: QUnit, lib: typeof jose, keys: Pick, done: (details: QUnit.DoneDetails) => void, ) => { // @ts-ignore QUnit.reporters.tap.init(QUnit) QUnit.config.autostart = false QUnit.config.testTimeout = 10_000 const modules = await Promise.all([ !skipFetch ? import('./jwks.js') : import('./noop.js'), import('./aes.js'), import('./aeskw.js'), import('./cookbook.js'), import('./ecdh.js'), import('./generate_options.js'), import('./hmac.js'), import('./jwk.js'), import('./jws.js'), import('./pbes2.js'), import('./pem.js'), import('./rsaes.js'), ]) for (const { default: module } of modules) { await module(QUnit, lib, keys) } QUnit.start() QUnit.done(done) } ================================================ FILE: tap/sign.ts ================================================ import type QUnit from 'qunit' import type * as jose from '../src/index.js' type keyType = Uint8Array | jose.CryptoKey | jose.KeyObject | jose.GenerateKeyPairResult function isKeyPair(input: keyType): input is jose.GenerateKeyPairResult { return 'publicKey' in input && 'privateKey' in input } async function getKeys( secretOrKeyPair: keyType, jwk: false, keys: Pick, ): Promise> async function getKeys( secretOrKeyPair: keyType, jwk: true, keys: Pick, ): Promise> async function getKeys( secretOrKeyPair: keyType, jwk: boolean, keys: Pick, ) { let sKey = isKeyPair(secretOrKeyPair) ? secretOrKeyPair.privateKey : secretOrKeyPair let vKey = isKeyPair(secretOrKeyPair) ? secretOrKeyPair.publicKey : secretOrKeyPair if (jwk) { // @ts-ignore return [await keys.exportJWK(sKey), await keys.exportJWK(vKey)] } return [sKey, vKey] } function jwkWithProps(jwk: jose.JWK, alg: string) { jwk = structuredClone(jwk) jwk.alg = alg jwk.use = 'sig' if (jwk.k) { jwk.key_ops = ['sign', 'verify'] } else if (jwk.d || jwk.priv) { jwk.key_ops = ['sign'] } else { jwk.key_ops = ['verify'] } return jwk } export async function jws( t: typeof QUnit.assert, lib: typeof jose, keys: Pick, alg: string, secretOrKeyPair: keyType, payload = crypto.getRandomValues(new Uint8Array(16)), ) { // Test Uint8Array, CryptoKey, and KeyObject key inputs { const [sKey, vKey] = await getKeys(secretOrKeyPair, false, keys) const jws = await new lib.FlattenedSign(payload).setProtectedHeader({ alg }).sign(sKey) { const verified = await lib.flattenedVerify(jws, vKey) t.deepEqual([...verified.payload], [...payload]) } { const verified = await lib.flattenedVerify(jws, async () => vKey) t.propContains(verified, { key: vKey, payload, protectedHeader: { alg }, }) } } if (secretOrKeyPair instanceof Uint8Array) return // Test JWK key input { const [sKey, vKey] = await getKeys(secretOrKeyPair, true, keys) const jws = await new lib.FlattenedSign(payload).setProtectedHeader({ alg }).sign(sKey) await new lib.FlattenedSign(payload).setProtectedHeader({ alg }).sign(jwkWithProps(sKey, alg)) for (const key of [ vKey, async () => vKey, jwkWithProps(vKey, alg), async () => jwkWithProps(vKey, alg), ]) { // @ts-ignore const verified = await lib.flattenedVerify(jws, key) t.deepEqual([...verified.payload], [...payload]) } } // Test JWK key input with all JWK properties { const [sKey, vKey] = await getKeys(secretOrKeyPair, true, keys) const jws = await new lib.FlattenedSign(payload) .setProtectedHeader({ alg }) .sign(jwkWithProps(sKey, alg)) const verified = await lib.flattenedVerify(jws, { ...vKey, alg, use: 'sig', key_ops: ['verify'], }) t.deepEqual([...verified.payload], [...payload]) } } export async function jwt( t: typeof QUnit.assert, lib: typeof jose, keys: Pick, alg: string, secretOrKeyPair: keyType, ) { const [sKey, vKey] = await getKeys(secretOrKeyPair, false, keys) const jwt = await new lib.SignJWT({ foo: 'bar', '🤷‍♂️': '🤷‍♀️' }) .setProtectedHeader({ alg, '🤷‍♂️': '🤷‍♀️' }) .sign(sKey) const verified = await lib.jwtVerify(jwt, vKey) t.deepEqual(verified, { payload: { foo: 'bar', '🤷‍♂️': '🤷‍♀️', }, protectedHeader: { alg, '🤷‍♂️': '🤷‍♀️', }, }) } ================================================ FILE: tap/tsconfig.json ================================================ { "include": ["**/*.ts"], "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "types": [], "allowSyntheticDefaultImports": true, "strict": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitAny": true } } ================================================ FILE: test/jwe/compact.decrypt.test.ts ================================================ import test from 'ava' import { compactDecrypt } from '../../src/index.js' test('JWE format validation', async (t) => { await t.throwsAsync(compactDecrypt(null, new Uint8Array(0)), { message: 'Compact JWE must be a string or Uint8Array', code: 'ERR_JWE_INVALID', }) await t.throwsAsync(compactDecrypt('...', new Uint8Array(0)), { message: 'Invalid Compact JWE', code: 'ERR_JWE_INVALID', }) }) ================================================ FILE: test/jwe/compact.encrypt.test.ts ================================================ import test from 'ava' import { CompactEncrypt } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.initializationVector = new Uint8Array(12) t.context.secret = new Uint8Array(16) }) test('CompactEncrypt', async (t) => { const jwe = await new CompactEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(t.context.secret) t.deepEqual( jwe, 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..AAAAAAAAAAAAAAAA.Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M.Y5cdeOSFYNyxcPWQlrVFzw', ) }) test('CompactEncrypt.prototype.setProtectedHeader', (t) => { t.throws( () => new CompactEncrypt(t.context.plaintext).setProtectedHeader({}).setProtectedHeader({}), { instanceOf: TypeError, message: 'setProtectedHeader can only be called once', }, ) }) test('CompactEncrypt.prototype.setKeyManagementParameters', (t) => { t.throws( () => new CompactEncrypt(t.context.plaintext) .setKeyManagementParameters({}) .setKeyManagementParameters({}), { instanceOf: TypeError, message: 'setKeyManagementParameters can only be called once', }, ) }) test('CompactEncrypt.prototype.setInitializationVector', (t) => { t.throws( () => new CompactEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setInitializationVector(t.context.initializationVector), { instanceOf: TypeError, message: 'setInitializationVector can only be called once', }, ) }) test('CompactEncrypt.prototype.setContentEncryptionKey', (t) => { t.throws( () => new CompactEncrypt(t.context.plaintext) .setContentEncryptionKey(t.context.secret) .setContentEncryptionKey(t.context.secret), { instanceOf: TypeError, message: 'setContentEncryptionKey can only be called once', }, ) }) test('CompactEncrypt.prototype.encrypt must have a JOSE header', async (t) => { await t.throwsAsync(new CompactEncrypt(t.context.plaintext).encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()', }) }) test('CompactEncrypt.prototype.encrypt JOSE header have an alg', async (t) => { await t.throwsAsync( new CompactEncrypt(t.context.plaintext) .setProtectedHeader({ enc: 'A128GCM' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE "alg" (Algorithm) Header Parameter missing or invalid', }, ) }) test('CompactEncrypt.prototype.encrypt JOSE header have an enc', async (t) => { await t.throwsAsync( new CompactEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid', }, ) }) ================================================ FILE: test/jwe/flattened.decrypt.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { FlattenedEncrypt, flattenedDecrypt } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.additionalAuthenticatedData = encode('The Fellowship of the Ring') t.context.initializationVector = new Uint8Array(12) t.context.secret = new Uint8Array(16) }) test('JWE format validation', async (t) => { const fullJwe = await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ bar: 'baz' }) .setUnprotectedHeader({ foo: 'bar' }) .setSharedUnprotectedHeader({ alg: 'dir', enc: 'A128GCM' }) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) .encrypt(t.context.secret) await t.notThrowsAsync(flattenedDecrypt(fullJwe, t.context.secret)) { await t.throwsAsync(flattenedDecrypt(null, t.context.secret), { message: 'Flattened JWE must be an object', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.protected = undefined jwe.header = undefined jwe.unprotected = undefined await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JOSE Header missing', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } const assertion = { message: 'JWE Initialization Vector incorrect type', code: 'ERR_JWE_INVALID', } jwe.iv = 12 await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) jwe.iv = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) jwe.iv = undefined await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Initialization Vector missing', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.ciphertext = undefined const assertion = { message: 'JWE Ciphertext missing or incorrect type', code: 'ERR_JWE_INVALID', } await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) jwe.ciphertext = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) } { const jwe = { ...fullJwe } const assertion = { message: 'JWE Authentication Tag incorrect type', code: 'ERR_JWE_INVALID', } jwe.tag = 12 await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) jwe.tag = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) jwe.tag = undefined await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Authentication Tag missing', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.protected = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Protected Header incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } const assertion = { message: 'JWE Protected Header is invalid', code: 'ERR_JWE_INVALID', } jwe.protected = `1${jwe.protected}` await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), assertion) } { const jwe = { ...fullJwe } jwe.encrypted_key = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Encrypted Key incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.aad = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE AAD incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.header = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Shared Unprotected Header incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.unprotected = null await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Per-Recipient Unprotected Header incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.unprotected = { foo: 'bar' } await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.unprotected = { enc: 'A128GCM' } await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'missing JWE Algorithm (alg) in JWE Header', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.unprotected = { alg: 'dir' } await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'missing JWE Encryption Algorithm (enc) in JWE Header', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...fullJwe } jwe.encrypted_key = 'foo' await t.throwsAsync(flattenedDecrypt(jwe, t.context.secret), { message: 'Encountered unexpected JWE Encrypted Key', code: 'ERR_JWE_INVALID', }) } }) test('AES CBC + HMAC', async (t) => { const secret = crypto.randomFillSync(new Uint8Array(32)) const jwe = await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256' }) .encrypt(secret) await t.notThrowsAsync(flattenedDecrypt(jwe, secret)) { const jweBadTag = { ...jwe } jweBadTag.tag = 'foo' await t.throwsAsync(flattenedDecrypt(jweBadTag, secret), { code: 'ERR_JWE_DECRYPTION_FAILED', message: 'decryption operation failed', }) } { const jweBadEnc = { ...jwe } jweBadEnc.ciphertext = 'foo' await t.throwsAsync(flattenedDecrypt(jweBadEnc, secret), { code: 'ERR_JWE_DECRYPTION_FAILED', message: 'decryption operation failed', }) } { const altSecret = new Uint8Array(32) altSecret.set(secret.slice(0, 16), 16) altSecret.set(secret.slice(16), 0) await t.throwsAsync(flattenedDecrypt(jwe, altSecret), { code: 'ERR_JWE_DECRYPTION_FAILED', message: 'decryption operation failed', }) } }) test('decrypt PBES2 p2c limit', async (t) => { const jwe = await new FlattenedEncrypt(new Uint8Array(0)) .setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A128CBC-HS256' }) .setKeyManagementParameters({ p2c: 2049 }) .encrypt(new Uint8Array(32)) await t.notThrowsAsync( flattenedDecrypt(jwe, new Uint8Array(32), { keyManagementAlgorithms: ['PBES2-HS256+A128KW'] }), ) await t.throwsAsync( flattenedDecrypt(jwe, new Uint8Array(32), { maxPBES2Count: 2048, keyManagementAlgorithms: ['PBES2-HS256+A128KW'], }), { message: 'JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds', code: 'ERR_JWE_INVALID', }, ) }) test('decrypt with PBES2 is not allowed by default', async (t) => { const jwe = await new FlattenedEncrypt(new Uint8Array(0)) .setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A128CBC-HS256' }) .encrypt(new Uint8Array(32)) await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32)), { message: '"alg" (Algorithm) Header Parameter value not allowed', code: 'ERR_JOSE_ALG_NOT_ALLOWED', }) }) ================================================ FILE: test/jwe/flattened.encrypt.test.ts ================================================ import test from 'ava' import { FlattenedEncrypt, decodeProtectedHeader } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.additionalAuthenticatedData = encode('The Fellowship of the Ring') t.context.initializationVector = new Uint8Array(12) t.context.secret = new Uint8Array(16) }) test('FlattenedEncrypt', async (t) => { { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir' }) .setUnprotectedHeader({ enc: 'A128GCM' }) .encrypt(t.context.secret) t.deepEqual(jwe, { ciphertext: 'Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M', header: { enc: 'A128GCM', }, iv: 'AAAAAAAAAAAAAAAA', protected: 'eyJhbGciOiJkaXIifQ', tag: 'OYBq53cJNorm8LoZf4SwsA', }) } { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir' }) .setSharedUnprotectedHeader({ enc: 'A128GCM' }) .encrypt(t.context.secret) t.deepEqual(jwe, { ciphertext: 'Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M', unprotected: { enc: 'A128GCM', }, iv: 'AAAAAAAAAAAAAAAA', protected: 'eyJhbGciOiJkaXIifQ', tag: 'OYBq53cJNorm8LoZf4SwsA', }) } { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setSharedUnprotectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(t.context.secret) t.deepEqual(jwe, { ciphertext: 'Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M', unprotected: { alg: 'dir', enc: 'A128GCM', }, iv: 'AAAAAAAAAAAAAAAA', tag: 'vrBCoJmYwG3M6xCZ5VSR3g', }) } { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir' }) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) .setSharedUnprotectedHeader({ enc: 'A128GCM' }) .encrypt(t.context.secret) t.deepEqual(jwe, { aad: 'VGhlIEZlbGxvd3NoaXAgb2YgdGhlIFJpbmc', ciphertext: 'Svw4TvnFg_PTTKPXFteMF4Lmisk8ODBNko7607TNs49EbT0BKRz9tEep2dmks9KPvD-CfX7hW1M', unprotected: { enc: 'A128GCM', }, iv: 'AAAAAAAAAAAAAAAA', protected: 'eyJhbGciOiJkaXIifQ', tag: 'gEwNlfPZ-O-dG7dTFkhMyQ', }) } { for (const value of [ undefined, null, {}, '', 'foo', 1, 0, true, false, [], new FlattenedEncrypt(new Uint8Array()), ]) { t.throws(() => new FlattenedEncrypt(value), { instanceOf: TypeError, message: 'plaintext must be an instance of Uint8Array', }) } } }) test('FlattenedEncrypt.prototype.setProtectedHeader', (t) => { t.throws( () => new FlattenedEncrypt(t.context.plaintext).setProtectedHeader({}).setProtectedHeader({}), { instanceOf: TypeError, message: 'setProtectedHeader can only be called once', }, ) }) test('FlattenedEncrypt.prototype.setUnprotectedHeader', (t) => { t.throws( () => new FlattenedEncrypt(t.context.plaintext).setUnprotectedHeader({}).setUnprotectedHeader({}), { instanceOf: TypeError, message: 'setUnprotectedHeader can only be called once', }, ) }) test('FlattenedEncrypt.prototype.setSharedUnprotectedHeader', (t) => { t.throws( () => new FlattenedEncrypt(t.context.plaintext) .setSharedUnprotectedHeader({}) .setSharedUnprotectedHeader({}), { instanceOf: TypeError, message: 'setSharedUnprotectedHeader can only be called once', }, ) }) test('FlattenedEncrypt.prototype.setInitializationVector', (t) => { t.throws( () => new FlattenedEncrypt(t.context.plaintext) .setInitializationVector(t.context.initializationVector) .setInitializationVector(t.context.initializationVector), { instanceOf: TypeError, message: 'setInitializationVector can only be called once', }, ) }) test('FlattenedEncrypt.prototype.setContentEncryptionKey', (t) => { t.throws( () => new FlattenedEncrypt(t.context.plaintext) .setContentEncryptionKey(t.context.secret) .setContentEncryptionKey(t.context.secret), { instanceOf: TypeError, message: 'setContentEncryptionKey can only be called once', }, ) }) test('FlattenedEncrypt.prototype.encrypt must have a JOSE header', async (t) => { await t.throwsAsync(new FlattenedEncrypt(t.context.plaintext).encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()', }) }) test('FlattenedEncrypt.prototype.encrypt JOSE header must be disjoint', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .setUnprotectedHeader({ alg: 'dir' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', }, ) await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .setSharedUnprotectedHeader({ alg: 'dir' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint', }, ) }) test('FlattenedEncrypt.prototype.encrypt JOSE header have an alg', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ enc: 'A128GCM' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE "alg" (Algorithm) Header Parameter missing or invalid', }, ) }) test('FlattenedEncrypt.prototype.encrypt JOSE header have an enc', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir' }) .encrypt(t.context.secret), { code: 'ERR_JWE_INVALID', message: 'JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid', }, ) }) test('Default PBES2 Count', async (t) => { t.is( decodeProtectedHeader( await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A128GCM' }) .encrypt(t.context.secret), ).p2c, 2048, ) }) ================================================ FILE: test/jwe/general.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { GeneralEncrypt, generalDecrypt, generateKeyPair } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.additionalAuthenticatedData = encode('The Fellowship of the Ring') t.context.initializationVector = crypto.randomFillSync(new Uint8Array(12)) t.context.secret = crypto.randomFillSync(new Uint8Array(32)) t.context.secret2 = crypto.randomFillSync(new Uint8Array(16)) }) test('General JWE encryption', async (t) => { const generalJwe = await new GeneralEncrypt(t.context.plaintext) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) .setProtectedHeader({ enc: 'A256GCM' }) .setSharedUnprotectedHeader({ foo: 'bar' }) .addRecipient(t.context.secret) .setUnprotectedHeader({ alg: 'A256GCMKW' }) .addRecipient(t.context.secret2) .setUnprotectedHeader({ alg: 'A128GCMKW' }) .encrypt() t.true(generalJwe.aad && typeof generalJwe.aad === 'string') t.true(generalJwe.ciphertext && typeof generalJwe.ciphertext === 'string') t.true(generalJwe.iv && typeof generalJwe.iv === 'string') t.true(generalJwe.protected && typeof generalJwe.protected === 'string') t.true( generalJwe.unprotected && typeof generalJwe.unprotected === 'object' && Object.keys(generalJwe.unprotected).length === 1, ) t.true(generalJwe.tag && typeof generalJwe.tag === 'string') t.is(generalJwe.recipients.length, 2) for (const recipient of generalJwe.recipients) { t.true(recipient.encrypted_key && typeof recipient.encrypted_key === 'string') t.true( recipient.header && typeof recipient.header === 'object' && Object.keys(recipient.header).length !== 0, ) } for (const secret of [t.context.secret, t.context.secret2]) { await generalDecrypt(generalJwe, secret) } }) test('General JWE encryption (single recipient dir)', async (t) => { const generalJwe = await new GeneralEncrypt(t.context.plaintext) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) .setProtectedHeader({ enc: 'A256GCM' }) .setSharedUnprotectedHeader({ alg: 'A256GCMKW' }) .addRecipient(t.context.secret) .encrypt() t.true(generalJwe.aad && typeof generalJwe.aad === 'string') t.true(generalJwe.ciphertext && typeof generalJwe.ciphertext === 'string') t.true(generalJwe.iv && typeof generalJwe.iv === 'string') t.true(generalJwe.protected && typeof generalJwe.protected === 'string') t.true(generalJwe.tag && typeof generalJwe.tag === 'string') t.true( generalJwe.unprotected && typeof generalJwe.unprotected === 'object' && Object.keys(generalJwe.unprotected).length === 1, ) t.is(generalJwe.recipients.length, 1) t.true( generalJwe.recipients[0].encrypted_key && typeof generalJwe.recipients[0].encrypted_key === 'string', ) t.false('header' in generalJwe.recipients[0]) await generalDecrypt(generalJwe, t.context.secret) }) test('General JWE encryption (single recipient ECDH-ES)', async (t) => { const kp = await generateKeyPair('ECDH-ES') const generalJwe = await new GeneralEncrypt(t.context.plaintext) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) .setProtectedHeader({ enc: 'A256GCM' }) .setSharedUnprotectedHeader({ alg: 'ECDH-ES' }) .addRecipient(kp.publicKey) .encrypt() t.true(generalJwe.aad && typeof generalJwe.aad === 'string') t.true(generalJwe.ciphertext && typeof generalJwe.ciphertext === 'string') t.true(generalJwe.iv && typeof generalJwe.iv === 'string') t.true(generalJwe.protected && typeof generalJwe.protected === 'string') t.true(generalJwe.tag && typeof generalJwe.tag === 'string') t.deepEqual(generalJwe.recipients, [{}]) t.true( generalJwe.unprotected && typeof generalJwe.unprotected === 'object' && Object.keys(generalJwe.unprotected).length === 1, ) await generalDecrypt(generalJwe, kp.privateKey) }) test('General JWE format validation', async (t) => { const encrypt = new GeneralEncrypt(t.context.plaintext) .setProtectedHeader({ bar: 'baz' }) .setSharedUnprotectedHeader({ foo: 'bar' }) .setAdditionalAuthenticatedData(t.context.additionalAuthenticatedData) encrypt.addRecipient(t.context.secret).setUnprotectedHeader({ alg: 'A256GCMKW', enc: 'A256GCM' }) const generalJwe = await encrypt.encrypt() { await t.throwsAsync(generalDecrypt(null, t.context.secret), { message: 'General JWE must be an object', code: 'ERR_JWE_INVALID', }) } { await t.throwsAsync(generalDecrypt({ recipients: null }, t.context.secret), { message: 'JWE Recipients missing or incorrect type', code: 'ERR_JWE_INVALID', }) } { await t.throwsAsync(generalDecrypt({ recipients: [null] }, t.context.secret), { message: 'JWE Recipients missing or incorrect type', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...generalJwe, recipients: [] } await t.throwsAsync(generalDecrypt(jwe, t.context.secret), { message: 'JWE Recipients has no members', code: 'ERR_JWE_INVALID', }) } { const jwe = { ...generalJwe, recipients: [{}] } await t.throwsAsync(generalDecrypt(jwe, t.context.secret), { message: 'decryption operation failed', code: 'ERR_JWE_DECRYPTION_FAILED', }) } { const jwe = { ...generalJwe, recipients: [generalJwe.recipients[0]] } await t.notThrowsAsync(generalDecrypt(jwe, t.context.secret)) } { const jwe = { ...generalJwe, recipients: [generalJwe.recipients[0], {}] } await t.notThrowsAsync(generalDecrypt(jwe, t.context.secret)) } { const jwe = { ...generalJwe, recipients: [{}, generalJwe.recipients[0]] } await t.notThrowsAsync(generalDecrypt(jwe, t.context.secret)) } }) test('Default PBES2 Count', async (t) => { const jwe = await new GeneralEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A128GCM' }) .addRecipient(t.context.secret) .addRecipient(t.context.secret) .encrypt(t.context.secret) const [{ header: bob }, { header: charlie }] = jwe.recipients t.is(bob.p2c, 2048) t.is(charlie.p2c, 2048) t.true(bob.p2s !== charlie.p2s) }) ================================================ FILE: test/jwe/zip.test.ts ================================================ import test from 'ava' import * as crypto from 'node:crypto' import { FlattenedEncrypt, flattenedDecrypt, CompactEncrypt, compactDecrypt, GeneralEncrypt, generalDecrypt, EncryptJWT, jwtDecrypt, generateSecret, } from '../../src/index.js' const hasCompressionStreams = typeof globalThis.CompressionStream !== 'undefined' const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) test.before(async (t) => { t.context.plaintext = encode("It's a dangerous business, Frodo, going out your door.") t.context.secret = await generateSecret('A128GCM') }) if (hasCompressionStreams) { test('FlattenedEncrypt/flattenedDecrypt with zip: DEF', async (t) => { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) const { plaintext } = await flattenedDecrypt(jwe, t.context.secret) t.deepEqual(plaintext, t.context.plaintext) }) test('CompactEncrypt/compactDecrypt with zip: DEF', async (t) => { const jwe = await new CompactEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) const { plaintext } = await compactDecrypt(jwe, t.context.secret) t.deepEqual(plaintext, t.context.plaintext) }) test('GeneralEncrypt/generalDecrypt with zip: DEF', async (t) => { const jwe = await new GeneralEncrypt(t.context.plaintext) .setProtectedHeader({ enc: 'A128GCM', zip: 'DEF' }) .addRecipient(t.context.secret) .setUnprotectedHeader({ alg: 'A128GCMKW' }) .encrypt() const { plaintext } = await generalDecrypt(jwe, t.context.secret) t.deepEqual(plaintext, t.context.plaintext) }) test('EncryptJWT/jwtDecrypt with zip: DEF', async (t) => { const jwt = await new EncryptJWT({ foo: 'bar' }) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) const { payload, protectedHeader } = await jwtDecrypt(jwt, t.context.secret) t.is(payload.foo, 'bar') t.is(protectedHeader.zip, 'DEF') }) test('jwtDecrypt with zip: DEF and maxDecompressedLength option', async (t) => { const jwt = await new EncryptJWT({ foo: 'bar' }) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) // Should succeed with a generous limit await t.notThrowsAsync(jwtDecrypt(jwt, t.context.secret, { maxDecompressedLength: Infinity })) // Should succeed with a reasonable limit await t.notThrowsAsync(jwtDecrypt(jwt, t.context.secret, { maxDecompressedLength: 1024 })) }) test('maxDecompressedLength: 0 disables zip support on decrypt', async (t) => { const jwe = await new CompactEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) await t.throwsAsync(compactDecrypt(jwe, t.context.secret, { maxDecompressedLength: 0 }), { message: 'JWE "zip" (Compression Algorithm) Header Parameter is not supported.', code: 'ERR_JOSE_NOT_SUPPORTED', }) }) test('maxDecompressedLength: Infinity disables bomb protection', async (t) => { // Create a large plaintext const largePlaintext = new Uint8Array(300_000) const jwe = await new CompactEncrypt(largePlaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) // Default limit (250KB) should reject await t.throwsAsync(compactDecrypt(jwe, t.context.secret), { message: 'Decompressed plaintext exceeded the configured limit', code: 'ERR_JWE_INVALID', }) // Infinity should allow const { plaintext } = await compactDecrypt(jwe, t.context.secret, { maxDecompressedLength: Infinity, }) t.is(plaintext.byteLength, 300_000) }) test('maxDecompressedLength enforcement', async (t) => { const plaintext = new Uint8Array(1024) const jwe = await new CompactEncrypt(plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) await t.throwsAsync(compactDecrypt(jwe, t.context.secret, { maxDecompressedLength: 512 }), { message: 'Decompressed plaintext exceeded the configured limit', code: 'ERR_JWE_INVALID', }) await t.notThrowsAsync(compactDecrypt(jwe, t.context.secret, { maxDecompressedLength: 2048 })) }) test('maxDecompressedLength must be 0, a positive safe integer, or Infinity', async (t) => { const jwe = await new CompactEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret) for (const value of [-1, 0.5, NaN, -Infinity]) { await t.throwsAsync(compactDecrypt(jwe, t.context.secret, { maxDecompressedLength: value }), { instanceOf: TypeError, message: 'maxDecompressedLength must be 0, a positive safe integer, or Infinity', }) } }) test('invalid deflate data is properly rejected', async (t) => { const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 }) const cek = crypto.randomBytes(32) const iv = crypto.randomBytes(12) const protectedHeader = { alg: 'RSA-OAEP-256', enc: 'A256GCM', zip: 'DEF' } const protectedB64 = Buffer.from(JSON.stringify(protectedHeader)).toString('base64url') const cipher = crypto.createCipheriv('aes-256-gcm', cek, iv, { authTagLength: 16 }) cipher.setAAD(Buffer.from(protectedB64)) const ciphertext = Buffer.concat([cipher.update(new Uint8Array([0x00])), cipher.final()]) const tag = cipher.getAuthTag() const encryptedKey = crypto.publicEncrypt({ key: publicKey, oaepHash: 'sha256' }, cek) const flattened = { protected: protectedB64, encrypted_key: Buffer.from(encryptedKey).toString('base64url'), iv: Buffer.from(iv).toString('base64url'), ciphertext: Buffer.from(ciphertext).toString('base64url'), tag: Buffer.from(tag).toString('base64url'), } await t.throwsAsync(flattenedDecrypt(flattened, privateKey), { code: 'ERR_JWE_INVALID', message: 'Failed to decompress plaintext', }) t.pass() }) } test('unsupported zip value on encrypt throws JOSENotSupported', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'gzip' }) .encrypt(t.context.secret), { message: 'Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.', code: 'ERR_JOSE_NOT_SUPPORTED', }, ) }) test('zip must be in a protected header on encrypt', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .setSharedUnprotectedHeader({ zip: 'DEF' }) .encrypt(t.context.secret), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }, ) await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .setUnprotectedHeader({ zip: 'DEF' }) .encrypt(t.context.secret), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }, ) }) test('zip must be in a protected header on encrypt (multi-recipient)', async (t) => { const secret1 = await generateSecret('A128KW') const secret2 = await generateSecret('A128KW') await t.throwsAsync( new GeneralEncrypt(t.context.plaintext) .setProtectedHeader({ enc: 'A128GCM' }) .setSharedUnprotectedHeader({ zip: 'DEF' }) .addRecipient(secret1) .setUnprotectedHeader({ alg: 'A128KW' }) .addRecipient(secret2) .setUnprotectedHeader({ alg: 'A128KW' }) .encrypt(), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }, ) await t.throwsAsync( new GeneralEncrypt(t.context.plaintext) .setProtectedHeader({ enc: 'A128GCM' }) .addRecipient(secret1) .setUnprotectedHeader({ alg: 'A128KW' }) .addRecipient(secret2) .setUnprotectedHeader({ alg: 'A128KW', zip: 'DEF' }) .encrypt(), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }, ) }) test('zip must be in a protected header on decrypt', async (t) => { const jwe = await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(t.context.secret) // Tamper: put zip in unprotected header const tampered = { ...jwe, unprotected: { zip: 'DEF' } } await t.throwsAsync(flattenedDecrypt(tampered, t.context.secret), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }) // Tamper: put zip in per-recipient header const tampered2 = { ...jwe, header: { zip: 'DEF' } } await t.throwsAsync(flattenedDecrypt(tampered2, t.context.secret), { message: 'JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.', code: 'ERR_JWE_INVALID', }) }) test('unsupported zip value on decrypt throws JOSENotSupported', async (t) => { // Build a JWE without zip, then tamper the protected header to have zip: gzip const jwe = await new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(t.context.secret) // Replace protected header with one containing zip: gzip const tampered = { ...jwe, protected: btoa(JSON.stringify({ alg: 'dir', enc: 'A128GCM', zip: 'gzip' })) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''), } await t.throwsAsync(flattenedDecrypt(tampered, t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', }) }) if (!hasCompressionStreams) { test('zip: DEF throws JOSENotSupported when CompressionStream is unavailable', async (t) => { await t.throwsAsync( new FlattenedEncrypt(t.context.plaintext) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', zip: 'DEF' }) .encrypt(t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', }, ) }) } ================================================ FILE: test/jwk/embedded.test.ts ================================================ import test from 'ava' import { importJWK, EmbeddedJWK, FlattenedSign, flattenedVerify } from '../../src/index.js' function pubjwk(jwk) { const { d, p, q, dp, dq, qi, ext, alg, ...publicJwk } = jwk return publicJwk } test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.key = { crv: 'P-256', alg: 'ES256', ext: false, x: 'Sp3KpzPjwcCF04_W2GvSSf-vGDvp3Iv2kQYqAjnMB-Y', y: 'lZmecT2quXe0i9f7b4qHvDAFDpxs0oxCoJx4tOOqsks', d: 'hRVo5TGE_d_4tQC1KEQIlCdo9rteZmLSmaMPpFOjeDI', kty: 'EC', } const privateKey = await importJWK(t.context.key) t.context.token = await new FlattenedSign( encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256', jwk: pubjwk(t.context.key) }) .sign(privateKey) t.context.tokenMissingJwk = await new FlattenedSign( encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256' }) .sign(privateKey) t.context.tokenInvalidJWK = await new FlattenedSign( encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256', jwk: null }) .sign(privateKey) t.context.tokenPrivateJWK = await new FlattenedSign( encode('It’s a dangerous business, Frodo, going out your door.'), ) .setProtectedHeader({ alg: 'ES256', jwk: t.context.key }) .sign(privateKey) }) test('EmbeddedJWK', async (t) => { await t.notThrowsAsync(async () => { const { key: resolvedKey } = await flattenedVerify(t.context.token, EmbeddedJWK) t.truthy(resolvedKey) t.is(resolvedKey.type, 'public') }) }) test('EmbeddedJWK requires "jwk" to be an object', async (t) => { await t.throwsAsync(flattenedVerify(t.context.tokenMissingJwk, EmbeddedJWK), { code: 'ERR_JWS_INVALID', message: '"jwk" (JSON Web Key) Header Parameter must be a JSON object', }) await t.throwsAsync(flattenedVerify(t.context.tokenInvalidJWK, EmbeddedJWK), { code: 'ERR_JWS_INVALID', message: '"jwk" (JSON Web Key) Header Parameter must be a JSON object', }) }) test('EmbeddedJWK requires "jwk" to be a public one', async (t) => { await t.throwsAsync(flattenedVerify(t.context.tokenPrivateJWK, EmbeddedJWK), { code: 'ERR_JWS_INVALID', message: '"jwk" (JSON Web Key) Header Parameter must be a public key', }) }) ================================================ FILE: test/jwk/issue-459.test.ts ================================================ import test from 'ava' import { importX509 } from '../../src/index.js' const cert = `-----BEGIN CERTIFICATE----- MIID5jCCAs6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJVUzEL MAkGA1UECBMCVE4xEDAOBgNVBAoTB01TSUdOSUExDDAKBgNVBAsTA1JORDEXMBUG A1UEAxMOTVNJR05JQSBSTkQgQ0ExJjAkBgkqhkiG9w0BCQEWF3BhdmxvLmx5c292 QG1zaWduaWEuY29tMB4XDTE5MTExMzA4MjAwMFoXDTI4MTExMzA4MjAwMFowgYAx CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJUTjEQMA4GA1UEChMHTVNJR05JQTEUMBIG A1UECxMLTVNJR05JQSBSTkQxFDASBgNVBAMTC01TSUdOSUEgUk5EMSYwJAYJKoZI hvcNAQkBFhdwYXZsby5seXNvdkBtc2lnbmlhLmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAKRoo30zttpiFlBKnOAmlOcT07xPms7Z6/ZdN9KnE/Po NQx7g6+Ap6b+trA2WDG80jEtwAy5XJcm81rBvJJvjwWQhiPjhXHvEibl+5zTYEXQ tvl3qKNdikXuXPBLI/rwmZTNZd2aa5biVoLEY+cQVLOjdAZS9ZIkeuLYeLEZfNky 7rLa4XyRO4W4XEUWgafOp+ZSXATOz48XCb+fmaek4d8epsVJ/X3Qww9I9mqg8QA7 /EH9ASOYvbMzOjSuDjYBCRq4SJw/YBJDnBcBJSESzLJDDCJQyP4BOD2+P5UZ/OWS NyzEDCLfLsiCVjdt0mNXrn/tGpdLoy1rVfC2SOAoZEUCAwEAAaNvMG0wDAYDVR0T AQH/BAIwADAdBgNVHQ4EFgQUU/luo/bbBOlrQ7wrC3+ggkITcSYwCwYDVR0PBAQD AgSwMBEGCWCGSAGG+EIBAQQEAwIFoDAeBglghkgBhvhCAQ0EERYPeGNhIGNlcnRp ZmljYXRlMA0GCSqGSIb3DQEBCwUAA4IBAQAXroFZ9FeP10gtQguptDo6U0SIAB9n qjN1IktyqatfUuVtThuxXAb3QQ7kYmGCZEaOIKoFdVc8i9aR5ZrYC1VIN4+cGLv7 P36Zl2q4i2G/X0QzniPPvsPyOUXeTVs3k6Sxe07uWdxsglq9LcVW++PvGYzotZP+ ZtmTzYAQtgadhPNo7+QmTO1FDju9p9hTFK7WhmXAO48bF9jrFiTkbwmo6PdlQiqi PQYlbfO0XV727QUZ1YyG8rR/3VVRsBOmwZBKCj0dkh9eiRcNpJloqe1uZ83EBG/W Cic5wE9P+Ol/pFNJFpfjXMsmT8lkCK954aYf2xoH1bHkONYAEEk0iQu/ -----END CERTIFICATE-----` test('https://github.com/panva/jose/issues/459', (t) => { return t.notThrowsAsync(() => importX509(cert, 'RS256')) }) ================================================ FILE: test/jwk/jwk2key.test.ts ================================================ import test from 'ava' import { importJWK, exportJWK } from '../../src/index.js' test('JWK must be an object', async (t) => { await t.throwsAsync(importJWK(true), { instanceOf: TypeError, message: 'JWK must be an object', }) await t.throwsAsync(importJWK(null), { instanceOf: TypeError, message: 'JWK must be an object', }) await t.throwsAsync(importJWK(Boolean), { instanceOf: TypeError, message: 'JWK must be an object', }) await t.throwsAsync(importJWK([]), { instanceOf: TypeError, message: 'JWK must be an object', }) await t.throwsAsync(importJWK(''), { instanceOf: TypeError, message: 'JWK must be an object', }) const nullPrototype = Object.create(null) nullPrototype.crv = 'P-256' nullPrototype.kty = 'EC' nullPrototype.x = 'q3zAwR_kUwtdLEwtB2oVfucXiLHmEhu9bJUFYjJxYGs' nullPrototype.y = '8h0D-ONoU-iZqrq28TyUxEULxuGwJZGMJYTMbeMshvI' await t.notThrowsAsync(importJWK(nullPrototype, 'ES256')) }) test('JWK kty must be recognized', async (t) => { await t.throwsAsync(importJWK({ kty: 'unrecognized' }, 'HS256'), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Unsupported "kty" (Key Type) Parameter value', }) }) test('oct JWK must have "k"', async (t) => { await t.throwsAsync(importJWK({ kty: 'oct' }, 'HS256'), { instanceOf: TypeError, message: 'missing "k" (Key Value) Parameter value', }) }) test('RSA JWK with oth is not supported', async (t) => { await t.throwsAsync(importJWK({ kty: 'RSA', oth: [] }, 'RS256'), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'RSA JWK "oth" (Other Primes Info) Parameter value is not supported', }) }) test('oct JWK', async (t) => { const oct = { k: 'FyCq1CKBflh3I5gikEjpYrdOXllzxB_yc02za8ERknI', kty: 'oct', } t.deepEqual( [...(await importJWK(oct, 'HS256'))], [ 23, 32, 170, 212, 34, 129, 126, 88, 119, 35, 152, 34, 144, 72, 233, 98, 183, 78, 94, 89, 115, 196, 31, 242, 115, 77, 179, 107, 193, 17, 146, 114, ], ) }) test('Uin8tArray can be transformed to a JWK', async (t) => { t.deepEqual( await exportJWK(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])), { k: 'AQIDBAUGBwgJCgsMDQ4P', kty: 'oct', }, ) }) test('secret KeyLike can be transformed to a JWK', async (t) => { const keylike = await importJWK( { ext: true, k: 'AQIDBAUGBwgJCgsMDQ4P', kty: 'oct', }, 'HS256', ) t.deepEqual(await exportJWK(keylike), { k: 'AQIDBAUGBwgJCgsMDQ4P', kty: 'oct', }) }) ================================================ FILE: test/jwk/thumbprint.test.ts ================================================ import test from 'ava' import { calculateJwkThumbprint, calculateJwkThumbprintUri } from '../../src/index.js' const jwk = { kty: 'RSA', n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw', e: 'AQAB', alg: 'RS256', } test('https://www.rfc-editor.org/rfc/rfc7638#section-3.1', async (t) => { t.is(await calculateJwkThumbprint(jwk), 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs') t.is(await calculateJwkThumbprint(jwk, 'sha256'), 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs') t.is( await calculateJwkThumbprint(jwk, 'sha384'), 'R9_OfJjSjaw8Fuum86UzK5ixTdN9bo9BaqPSiseq89DWfmqCdpSgUHus-cxDUNc8', ) t.is( await calculateJwkThumbprint(jwk, 'sha512'), 'DpvEwocfn3FjeWWQjcJHzWrpKTIymKwgoL1xVgQcud48-qZDSRCr1zfWZQdHAJn_ciqXqPTSARyg-L-NyNGpVA', ) }) test('https://www.rfc-editor.org/rfc/rfc9278', async (t) => { t.is( await calculateJwkThumbprintUri(jwk), 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs', ) t.is( await calculateJwkThumbprintUri(jwk, 'sha256'), 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs', ) t.is( await calculateJwkThumbprintUri(jwk, 'sha384'), 'urn:ietf:params:oauth:jwk-thumbprint:sha-384:R9_OfJjSjaw8Fuum86UzK5ixTdN9bo9BaqPSiseq89DWfmqCdpSgUHus-cxDUNc8', ) t.is( await calculateJwkThumbprintUri(jwk, 'sha512'), 'urn:ietf:params:oauth:jwk-thumbprint:sha-512:DpvEwocfn3FjeWWQjcJHzWrpKTIymKwgoL1xVgQcud48-qZDSRCr1zfWZQdHAJn_ciqXqPTSARyg-L-NyNGpVA', ) }) test('Key must be one of type CryptoKey, KeyObject, or JSON Web Key.', async (t) => { await t.throwsAsync(calculateJwkThumbprint(true), { instanceOf: TypeError, message: /Key must be one of type CryptoKey, KeyObject, or JSON Web Key./, }) await t.throwsAsync(calculateJwkThumbprint(null), { instanceOf: TypeError, message: /Key must be one of type CryptoKey, KeyObject, or JSON Web Key./, }) await t.throwsAsync(calculateJwkThumbprint(Boolean), { instanceOf: TypeError, message: /Key must be one of type CryptoKey, KeyObject, or JSON Web Key./, }) await t.throwsAsync(calculateJwkThumbprint([]), { instanceOf: TypeError, message: /Key must be one of type CryptoKey, KeyObject, or JSON Web Key./, }) await t.throwsAsync(calculateJwkThumbprint(''), { instanceOf: TypeError, message: /Key must be one of type CryptoKey, KeyObject, or JSON Web Key./, }) const nullPrototype = Object.create(null) nullPrototype.crv = 'P-256' nullPrototype.kty = 'EC' nullPrototype.x = 'q3zAwR_kUwtdLEwtB2oVfucXiLHmEhu9bJUFYjJxYGs' nullPrototype.y = '8h0D-ONoU-iZqrq28TyUxEULxuGwJZGMJYTMbeMshvI' await t.notThrowsAsync(calculateJwkThumbprint(nullPrototype)) }) test('JWK kty must be recognized', async (t) => { await t.throwsAsync(calculateJwkThumbprint({ kty: 'unrecognized' }), { code: 'ERR_JOSE_NOT_SUPPORTED', message: '"kty" (Key Type) Parameter missing or unsupported', }) }) test('EC JWK', async (t) => { const ec = { crv: 'P-256', kty: 'EC', x: 'q3zAwR_kUwtdLEwtB2oVfucXiLHmEhu9bJUFYjJxYGs', y: '8h0D-ONoU-iZqrq28TyUxEULxuGwJZGMJYTMbeMshvI', } await t.throwsAsync(calculateJwkThumbprint({ ...ec, crv: undefined }), { code: 'ERR_JWK_INVALID', message: '"crv" (Curve) Parameter missing or invalid', }) await t.throwsAsync(calculateJwkThumbprint({ ...ec, x: undefined }), { code: 'ERR_JWK_INVALID', message: '"x" (X Coordinate) Parameter missing or invalid', }) await t.throwsAsync(calculateJwkThumbprint({ ...ec, y: undefined }), { code: 'ERR_JWK_INVALID', message: '"y" (Y Coordinate) Parameter missing or invalid', }) t.is(await calculateJwkThumbprint(ec), 'ZrBaai73Hi8Fg4MElvDGzIne2NsbI75RHubOViHYE5Q') }) test('OKP JWK', async (t) => { const okp = { crv: 'Ed25519', kty: 'OKP', x: '5fL1GDeyNTIxtuzTeFnvZTo4Oz0EkMfAdhIJA-EFn0w', } await t.throwsAsync(calculateJwkThumbprint({ ...okp, crv: undefined }), { code: 'ERR_JWK_INVALID', message: '"crv" (Subtype of Key Pair) Parameter missing or invalid', }) await t.throwsAsync(calculateJwkThumbprint({ ...okp, x: undefined }), { code: 'ERR_JWK_INVALID', message: '"x" (Public Key) Parameter missing or invalid', }) t.is(await calculateJwkThumbprint(okp), '1OzNmMHhNzbSJyoePAtdoVedRZlFvER3K3RAzCrfX0k') }) test('RSA JWK', async (t) => { const rsa = { e: 'AQAB', kty: 'RSA', n: 'ok6WYUlmj2J1p-Sm0kwaZlAbWetUooe2LR6iAOJfntavWlyBO0shK_550YG3lQ6R1YeKisNAqbQ1pjqo3vwvR_v_AWtZ1gY1h6KX4DhCv0nNMexZ4g67LxEweoQ4_InMMiwMyQ3CRVJ3P1w0TQZYqzfSye-llY39tyzHeHeuotgrZrM427iUuIJdN38nZ2vW9VpK3bo_Nsvl12ZBe6x7DBzWEFHqQDFyjy8lH8EZyxqDArLA7T5OAcEdkm3RI8jBbsrUD9IySCE5SdEU3n0VGNGkT88DFU85QGvLpL2ITbGX0amaJvxYjIRhIYTfZS6Mqoxr6K1LIwP8pu0VD2Ca5Q', } await t.throwsAsync(calculateJwkThumbprint({ ...rsa, e: undefined }), { code: 'ERR_JWK_INVALID', message: '"e" (Exponent) Parameter missing or invalid', }) await t.throwsAsync(calculateJwkThumbprint({ ...rsa, n: undefined }), { code: 'ERR_JWK_INVALID', message: '"n" (Modulus) Parameter missing or invalid', }) t.is(await calculateJwkThumbprint(rsa), 'dQiQXSGtV4XcPK143Cu2-ZSsQtVNjQZrleUMs9nLnKQ') }) test('oct JWK', async (t) => { const oct = { k: 'FyCq1CKBflh3I5gikEjpYrdOXllzxB_yc02za8ERknI', kty: 'oct', } await t.throwsAsync(calculateJwkThumbprint({ ...oct, k: undefined }), { code: 'ERR_JWK_INVALID', message: '"k" (Key Value) Parameter missing or invalid', }) t.is(await calculateJwkThumbprint(oct), 'prDKy90VJzrDTpm8-W2Q_pv_kzrX_zyZ7ANjRAasDxc') }) test('AKP JWK', async (t) => { const akp = { kty: 'AKP', alg: 'ML-DSA-44', pub: 'unH59k4RuutY-pxvu24U5h8YZD2rSVtHU5qRZsoBmBMcRPgmu9VuNOVdteXi1zNIXjnqJg_GAAxepLqA00Vc3lO0bzRIKu39VFD8Lhuk8l0V-cFEJC-zm7UihxiQMMUEmOFxe3x1ixkKZ0jqmqP3rKryx8tSbtcXyfea64QhT6XNje2SoMP6FViBDxLHBQo2dwjRls0k5a-XSQSu2OTOiHLoaWsLe8pQ5FLNfTDqmkrawDEdZyxr3oSWJAsHQxRjcIiVzZuvwxYy1zl2STiP2vy_fTBaPemkleynQzqPg7oPCyXEE8bjnJbrfWkbNNN8438e6tHPIX4l7zTuzz98YPhLjt_d6EBdT4MldsYe-Y4KLyjaGHcAlTkk9oa5RhRwW89T0z_t1DSO3dvfKLUGXh8gd1BD6Fz5MfgpF5NjoafnQEqDjsAAhrCXY4b-Y3yYJEdX4_dp3dRGdHG_rWcPmgX4JG7lCnser4f8QGnDriqiAzJYEXeS8LzUngg_0bx0lqv_KcyU5IaLISFO0xZSU5mmEPvdSoDnyAcV8pV44qhLtAvd29n0ehG259oRihtljTWeiu9V60a1N2tbZVl5mEqSK-6_xZvNYA1TCdzNctvweH24unV7U3wer9XA9Q6kvJWDVJ4oKaQsKMrCSMlteBJMRxWbGK7ddUq6F7GdQw-3j2M-qdJvVKm9UPjY9rc1lPgol25-oJxTu7nxGlbJUH-4m5pevAN6NyZ6lfhbjWTKlxkrEKZvQXs_Yf6cpXEwpI_ZJeriq1UC1XHIpRkDwdOY9MH3an4RdDl2r9vGl_IwlKPNdh_5aF3jLgn7PCit1FNJAwC8fIncAXgAlgcXIpRXdfJk4bBiO89GGccSyDh2EgXYdpG3XvNgGWy7npuSoNTE7WIyblAk13UQuO4sdCbMIuriCdyfE73mvwj15xgb07RZRQtFGlFTmnFcIdZ90zDrWXDbANntv7KCKwNvoTuv64bY3HiGbj-NQ-U9eMylWVpvr4hrXcES8c9K3PqHWADZC0iIOvlzFv4VBoc_wVflcOrL_SIoaNFCNBAZZq-2v5lAgpJTqVOtqJ_HVraoSfcKy5g45p-qULunXj6Jwq21fobQiKubBKKOZwcJFyJD7F4ACKXOrz-HIvSHMCWW_9dVrRuCpJw0s0aVFbRqopDNhu446nqb4_EDYQM1tTHMozPd_jKxRRD0sH75X8ZoToxFSpLBDbtdWcenxj-zBf6IGWfZnmaetjKEBYJWC7QDQx1A91pJVJCEgieCkoIfTqkeQuePpIyu48g2FG3P1zjRF-kumhUTfSjo5qS0YiZQy0E1BMs6M11EvuxXRsHClLHoy5nLYI2Sj4zjVjYyxSHyPRPGGo9hwB34yWxzYNtPPGiqXS_dNCpi_zRZwRY4lCGrQ-hYTEWIK1Dm5OlttvC4_eiQ1dv63NiGkLRJ5kJA3bICN0fzCDY-MBqnd1cWn8YVBijVkgtaoascjL9EywDgJdeHnXK0eeOvUxHHhXJVkNqcibn8O4RQdpVU60TSA-uiu675ytIjcBHC6kTv8A8pmkj_4oypPd-F92YIJC741swkYQoeIHj8rE-ThcMUkF7KqC5VORbZTRp8HsZSqgiJcIPaouuxd1-8Rxrid3fXkE6p8bkrysPYoxWEJgh7ZFsRCPDWX-yTeJwFN0PKFP1j0F6YtlLfK5wv-c4F8ZQHA_-yc_gODicy7KmWDZgbTP07e7gEWzw4MFRrndjbDQ', } await t.throwsAsync(calculateJwkThumbprint({ ...akp, alg: undefined }), { code: 'ERR_JWK_INVALID', message: '"alg" (Algorithm) Parameter missing or invalid', }) await t.throwsAsync(calculateJwkThumbprint({ ...akp, pub: undefined }), { code: 'ERR_JWK_INVALID', message: '"pub" (Public key) Parameter missing or invalid', }) t.is(await calculateJwkThumbprint(akp), 'T4xl70S7MT6Zeq6r9V9fPJGVn76wfnXJ21-gyo0Gu6o') }) ================================================ FILE: test/jwks/local.test.ts ================================================ import test from 'ava' import { createLocalJWKSet } from '../../src/index.js' test('LocalJWKSet', async (t) => { for (const f of [ null, {}, { keys: null }, { keys: {} }, { keys: [null] }, { keys: [0] }, { keys: [undefined] }, { keys: [[]] }, 1, Boolean, ]) { t.throws(() => createLocalJWKSet(f), { code: 'ERR_JWKS_INVALID' }) } const jwks = { keys: [] } const set = createLocalJWKSet(jwks) const clone = set.jwks() t.false(clone === jwks) t.false(clone === set.jwks()) t.deepEqual(clone, jwks) t.deepEqual(clone, set.jwks()) }) ================================================ FILE: test/jwks/remote.test.ts ================================================ import anyTest, { type TestFn } from 'ava' import timekeeper from 'timekeeper' import { createServer } from 'http' import { once } from 'events' import { MockAgent, setGlobalDispatcher } from 'undici' import type { Server } from 'node:net' const agent = new MockAgent() agent.disableNetConnect() setGlobalDispatcher(agent) import { jwtVerify, SignJWT, importJWK, createRemoteJWKSet, errors, FlattenedJWSInput, } from '../../src/index.js' const now = 1604416038 interface WithServer { server: Server } const test = anyTest as TestFn test.before(async (t) => { agent.disableNetConnect() t.context.server = createServer().unref().listen(3000) t.context.server.removeAllListeners('request') await once(t.context.server, 'listening') }) test.after(async (t) => { agent.enableNetConnect() await new Promise((resolve) => t.context.server.close(resolve)) }) test.afterEach(() => { agent.disableNetConnect() }) test.afterEach((t) => { t.context.server.removeAllListeners('request') agent.assertNoPendingInterceptors() }) test.afterEach(() => timekeeper.reset()) test.serial('RemoteJWKSet', async (t) => { const keys = [ { e: 'AQAB', n: 'wAR7gpvDJx2cUR15R1gyBYxEXanhOIDzk7evzadBpNCEpf6HA6utqMrf8dZ3EXSslKPSPBD5Qrz63kc2u8y7NqzwJQIi_i5xR6AxAyWLG3_kOHBwxnct6talLCZqgr8pDwnyP1BPnIaNf2hZxgS-UZbHCAVycd1n2qCdyb4FzFhcaNtiOLg5VSfgvtOdhHQlDXW-DBvwatpd9HzzTP6l5MZRyQ-N_AoGbIfhNCZRUfnb-A8IBPSqXBWN4TEpt-0yHAOIhWnSpu66AYE4f1efZdHVFCTQZ13e5bS-5RQra4pfmGqU9hog1j1SpHnDTia-s__qGi43rev2MqzY-qeUlw', d: 'buWn14TSLtMhJo_ZLWU4bo_WJCoq0xFWm-eodyOz-9YZ5iycGXibcTLKJ8fvOHuj-KysjNhYvTybvqhuagQR08AJabZUM2zrK6zO4bxbHOS-EAKQf27xbAHPnzIIrb5tnivmZr6hXAsxyXWg84ZlzIVCKdXLhQuUIWZF-u_uNVeJSUTDMRVTL2J0mzAGTXqi-yHejapEeLS7lFXDe6cpDnBVXauJfB4GmSUOjxtdAEVW7uGNQJGarGwRz6l3Tpy_xQiYl8e_IrU1N6qAN_HJEBrdgfK7js93RcsxHGbtdnj1ylevZqGFpB1UXrWE4JSz3sJgyXrmKNFFWOCjalMccQ', p: '98OCXxur1omXdjfWkDubqkI3xRehVQryIhqt0go-1yLS4Nwa7KyrdAbzTo81bCHN0A-NlmIvHA4YZc8QUHftq65s4nCbb3g_CwTfGCJEVCvoaTO2EE6Pd8VrGu2PVsN4SM52Gc0TNJGS54yUhyCWDTi1onUBEg8gnqpMSoWuaVM', q: 'xmaRdSJf4wN5Vse5jiIjZy5jy9veHHhzXxTmW25epr9KTMERUDDCzX0bNbnF8jCvDFN5ebzcEe-9nkWyzJ17wVcJTouEfw8A5pBPcx6Gr8Kd8WIrUjuom4xu-4619kMItoV4j62_nq3p0QUGot_6CgUdq63PCp9Fh-sHv8wViy0', dp: '0OCXwbzfYu_-rCCpGFHYi3Jl-BhS4BJpTc02K3SNw-vM4ttNK6jqptfRObLMNAxPqg_iqxy9YKaVdQdbVqu0yF811rVepVw3sf96YatJ9bhKqJ566EaC91ONV1dd16TVfHPq5xeYEGKF-gXvlfgn6J-dqYeAzovIUVt7E_ydrJc', dq: 'sYDOnqe0dhyDkNp77ugoGIZujtMVcw9o2SaPujmSwUjfprANV1tozgQiNf0RVk-sLTD5u6r2ka2WTmY5Q8uaDy5Zi0ZTsoGv4pg2HN6wzcsnF_EmpRnvDcuk97eEoOD0iKf9Zz6h88vRJ0qB13Lf99r_4rtMQ0qgIKxscHKcy7k', qi: '7uvpgL15VFjd_zjhU0fPVeTzAa6Vg3P3Q2v5DLwLkAIlQDqF50maTYztxtxssVNJtEMIxKefwrmGkyVCXNhrGHZDoj7wj-2o0k878bQqtltCO2TPm9TSYZgW7dR3ji0t4Msc5DcrQL002M_Vxqr9MAunQcAsnulRTepQM2n-aOc', kty: 'RSA', kid: 'ZuLUAgyr6RQV3ERjDukHzOO_90rVbrPiE1vD_HtPFuM', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', d: 'LXGufKH6IBb4pUKh-iKX-ba1dBSGOkenUTHCd5STUG_JX3gsWUC5NPeTqrQzHkjV3otZytN3TgyZkr-QXDurEEtotD6Y1Ma85aljkuNfKTWWWoE1KwNmPZp0BQRB8lfGjmrNcC49tpw6owX4GvbqId_ifQupN32rY3t4qfq9xpO9SAqZF0oUMoS7xE0zChsCJmNYpD9jx87p5Vud1naeaZPlvwWW0ITV4kp2zjYSbBh5DkI52rSrGjkuzlsJ_lKJk5YB557OHhN9XTRBnjqlwwWevh6QAoUivqpcelcplgmfxTHoII1opovYXn8AVt-DbGSO_7LLJ0Sw9sJR5GAqcQ', p: '9RdDqZ3O73lH6nWUGi0abQRRfgvj-HM0zP7GSDQ185l-ZByletl1VuJ86qYJTUY8Q3Gagv6_eXmQMo_14-0wT_FPUMiTMYsjw5QNgFgjlJTM1AayS_U5ddix_Ut7Kti7EXgM0gsavsIazv2-xwCrFzD4sa-t2FWELzzWxgt8wbs', q: '2kX8MN8ItGnn7NnPx-0iqe8kkhy5s9gJRiD3mxN9E6xzRCnf488yhc3aBwI9kZzQtV0XVjX5VhCws5vnJv9b7KA8NATDhpGNrqy2h9ncmsjTTjafUg3jb6QG08kIKDR-A97Mc-MJbIUNzYs10BAG4z9wk7t1bdo4gZJEvjiXVHM', dp: 'Ahggy-I9Um6G3soCafbYsvXGfH09hXH2kYnbx-IqU9qL6e8UuugAyK1Gw_qHOdHP0gO2fkgO-sq_IK96OmhccVJuixIrr9CwjYtGUkJui2Z6GZW1EFEYHJmta6ypcMRJVOzhrynJILgn4nzolGq9C4WvmlUV9zND3eN3MloGxuE', dq: 'uXKWlusX2TjVvM0-FO2r8tdkqeNP_7XAA15FIPOI5Cszb6loOIQ0t6wy3puPteSXClBCYJPQ-MeLab4-wUpaTovBOq0FdpK53ruNBZUbMkMIDL6p1CxKnPKufkeh747RtfYYnSk7O4E8PfNV0CWdxHuE6W9ukNvEAIpGb5tjL3M', qi: '3BLQ03cHEmO8nUT7U8M_H_JciEWAH8XWh_9nihIhXzLKYbNmWM16Ah0F9DUg0GPeiG7e_08ZJ4X3oK1bHnnXdns6NSOEoULWfHl5LUY5PoFPYaBDy3f6td2SCTE83p1YzegXKysWEk1snA2ROq4UEfz1vL8v64RtwR3SvNrAyOI', kty: 'RSA', alg: 'RS256', kid: 'hJU8GvYjtifxLVuBDSNmhLBF19wQHaZvQhfpT3wKzpE', }, { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', kid: 'a-5xuiQoRqlLBtec9jRpXoGTVOP10SGnj2Und0CHHxw', }, ] const jwks = { keys: [ { e: 'AQAB', n: 'wAR7gpvDJx2cUR15R1gyBYxEXanhOIDzk7evzadBpNCEpf6HA6utqMrf8dZ3EXSslKPSPBD5Qrz63kc2u8y7NqzwJQIi_i5xR6AxAyWLG3_kOHBwxnct6talLCZqgr8pDwnyP1BPnIaNf2hZxgS-UZbHCAVycd1n2qCdyb4FzFhcaNtiOLg5VSfgvtOdhHQlDXW-DBvwatpd9HzzTP6l5MZRyQ-N_AoGbIfhNCZRUfnb-A8IBPSqXBWN4TEpt-0yHAOIhWnSpu66AYE4f1efZdHVFCTQZ13e5bS-5RQra4pfmGqU9hog1j1SpHnDTia-s__qGi43rev2MqzY-qeUlw', kty: 'RSA', kid: 'ZuLUAgyr6RQV3ERjDukHzOO_90rVbrPiE1vD_HtPFuM', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', kid: 'hJU8GvYjtifxLVuBDSNmhLBF19wQHaZvQhfpT3wKzpE', }, { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'a-5xuiQoRqlLBtec9jRpXoGTVOP10SGnj2Und0CHHxw', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', use: 'enc', }, { e: 'AQAB', n: '0PjQVV2ZAT27Y0h7hfAWWcnPetORCvR1_gHvEUxtlrlnhZia7utHl7BCJH9HP17YHMMBeeEkmUDflYoUL6MDl4DYHgVDq8jZfu1pxH1XqrpeswqOVoReknEe0F5kRt_mPtIoShI2Qv-pxGAw392akAXTirVRLL4Fn_0Oiifxp182P7eTPy41rlKDevLHuKHBZzzaes_33YE2epY2YCLp9k3mZ-tJEei2qiq0T1fERQicGUL8kppOnz0cDNuKRBHyYtXWhjhuDQ8OZQHNLfte9cqzTJMJ4Leu4MGjikSZMsk-_aRFnXtYHH0orwY-giSenRnwNaReAXaR1Px9ReljAQ', alg: 'RS256', kty: 'RSA', use: 'sig', key_ops: [], }, ], } agent .get('https://as.example.com') .intercept({ path: '/jwks', headers(headers) { return !!headers['user-agent'].match(/jose\/v\d+\.\d+\.\d+/) }, }) .reply(200, jwks) const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url) // Signed JWT { const [jwk] = keys const key = await importJWK({ ...jwk, alg: 'PS256' }) const jwt = await new SignJWT().setProtectedHeader({ alg: 'PS256', kid: jwk.kid }).sign(key) await t.notThrowsAsync(async () => { const { key: resolvedKey } = await jwtVerify(jwt, JWKS) t.truthy(resolvedKey) t.true(resolvedKey instanceof CryptoKey) t.is((resolvedKey as CryptoKey).type, 'public') }) } { const [jwk] = keys const key = await importJWK({ ...jwk, alg: 'RS256' }) const jwt = await new SignJWT().setProtectedHeader({ alg: 'RS256' }).sign(key) let error: errors.JWKSMultipleMatchingKeys = await t.throwsAsync(jwtVerify(jwt, JWKS), { code: 'ERR_JWKS_MULTIPLE_MATCHING_KEYS', message: 'multiple matching keys found in the JSON Web Key Set', }) // async iterator (KeyLike) { const cache = new WeakSet() for await (const ko of error) { t.like(ko, { type: 'public' }) cache.add(ko) } error = await t.throwsAsync(jwtVerify(jwt, JWKS), { code: 'ERR_JWKS_MULTIPLE_MATCHING_KEYS', message: 'multiple matching keys found in the JSON Web Key Set', }) let i = 0 for await (const ko of error) { i++ t.true(cache.has(ko)) } t.is(i, 2) } } { const [, jwk] = keys const key = await importJWK({ ...jwk, alg: 'PS256' }) const jwt = await new SignJWT().setProtectedHeader({ alg: 'PS256', kid: jwk.kid }).sign(key) await t.throwsAsync(jwtVerify(jwt, JWKS), { code: 'ERR_JWKS_NO_MATCHING_KEY', message: 'no applicable key found in the JSON Web Key Set', }) } { const [, , jwk] = keys const key = await importJWK({ ...jwk, alg: 'ES256' }) const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256' }).sign(key) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } }) test.serial('refreshes the JWKS once off cooldown', async (t) => { timekeeper.freeze(now * 1000) const jwk = { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', } const jwks = { keys: [ { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'one', }, ], } const mockAgent = agent.get('https://as.example.com') mockAgent.intercept({ path: '/jwks' }).reply(200, jwks) const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url) const key = await importJWK({ ...jwk, alg: 'ES256' }) { const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256', kid: 'one' }).sign(key) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } { const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256', kid: 'two' }).sign(key) await t.throwsAsync(jwtVerify(jwt, JWKS), { code: 'ERR_JWKS_NO_MATCHING_KEY', message: 'no applicable key found in the JSON Web Key Set', }) jwks.keys[0].kid = 'two' mockAgent.intercept({ path: '/jwks' }).reply(200, jwks) timekeeper.travel((now + 30) * 1000) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } }) test.serial('createRemoteJWKSet manual reload', async (t) => { timekeeper.freeze(now * 1000) const jwk = { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', } const jwks = { keys: [ { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'one', }, ], } const mockAgent = agent.get('https://as.example.com') mockAgent.intercept({ path: '/jwks' }).reply(200, jwks) const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url) t.false(JWKS.coolingDown) t.false(JWKS.fresh) t.false(JWKS.reloading) t.is(JWKS.jwks(), undefined) const key = await importJWK({ ...jwk, alg: 'ES256' }) { const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256', kid: 'two' }).sign(key) await t.throwsAsync(jwtVerify(jwt, JWKS), { code: 'ERR_JWKS_NO_MATCHING_KEY', message: 'no applicable key found in the JSON Web Key Set', }) jwks.keys[0].kid = 'two' mockAgent.intercept({ path: '/jwks' }).reply(200, jwks) t.true(JWKS.coolingDown) t.true(JWKS.fresh) t.false(JWKS.reloading) t.notDeepEqual(JWKS.jwks(), jwks) const reload = JWKS.reload() t.true(JWKS.reloading) await reload t.true(JWKS.coolingDown) t.true(JWKS.fresh) t.false(JWKS.reloading) t.deepEqual(JWKS.jwks(), jwks) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) JWKS.jwks()!.keys = [] t.deepEqual(JWKS.jwks(), jwks) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } }) test.serial('refreshes the JWKS once stale', async (t) => { timekeeper.freeze(now * 1000) const jwk = { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', } const jwks = { keys: [ { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'one', }, ], } agent.get('https://as.example.com').intercept({ path: '/jwks' }).reply(200, jwks).times(2) const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url, { cacheMaxAge: 60 * 10 * 1000 }) const key = await importJWK({ ...jwk, alg: 'ES256' }) { const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256', kid: 'one' }).sign(key) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) timekeeper.travel((now + 60 * 10) * 1000) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } }) test.serial('can be configured to never be stale', async (t) => { timekeeper.freeze(now * 1000) const jwk = { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', d: 'XikZvoy8ayRpOnuz7ont2DkgMxp_kmmg1EKcuIJWX_E', kty: 'EC', } const jwks = { keys: [ { crv: 'P-256', x: 'fqCXPnWs3sSfwztvwYU9SthmRdoT4WCXxS8eD8icF6U', y: 'nP6GIc42c61hoKqPcZqkvzhzIJkBV3Jw3g8sGG7UeP8', kty: 'EC', kid: 'one', }, ], } agent.get('https://as.example.com').intercept({ path: '/jwks' }).reply(200, jwks) const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url, { cacheMaxAge: Infinity }) const key = await importJWK({ ...jwk, alg: 'ES256' }) { const jwt = await new SignJWT().setProtectedHeader({ alg: 'ES256', kid: 'one' }).sign(key) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) timekeeper.travel((now + 60 * 10) * 1000) await t.notThrowsAsync(jwtVerify(jwt, JWKS)) } }) test.serial('throws on invalid JWKSet', async (t) => { const mockAgent = agent.get('https://as.example.com') mockAgent.intercept({ path: '/jwks' }).reply(200, 'null') const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url) await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JWKS_INVALID', message: 'JSON Web Key Set malformed', }) mockAgent.intercept({ path: '/jwks' }).reply(200, {}) await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JWKS_INVALID', message: 'JSON Web Key Set malformed', }) mockAgent.intercept({ path: '/jwks' }).reply(200, { keys: null }) await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JWKS_INVALID', message: 'JSON Web Key Set malformed', }) mockAgent .intercept({ path: '/jwks' }) .reply(200, { keys: [null] }) await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JWKS_INVALID', message: 'JSON Web Key Set malformed', }) mockAgent.intercept({ path: '/jwks' }).reply(404) await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JOSE_GENERIC', message: 'Expected 200 OK from the JSON Web Key Set HTTP response', }) mockAgent.intercept({ path: '/jwks' }).reply(200, '{') await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput), { code: 'ERR_JOSE_GENERIC', message: 'Failed to parse the JSON Web Key Set HTTP response as JSON', }) }) test.serial('can have headers configured', async (t) => { agent .get('https://as.example.com') .intercept({ path: '/jwks', headers: { 'x-custom': 'foo', }, }) .reply(200, 'null') const url = new URL('https://as.example.com/jwks') const JWKS = createRemoteJWKSet(url, { headers: { 'x-custom': 'foo' } }) await JWKS().catch(() => {}) t.pass() }) test('handles ENOTFOUND', async (t) => { agent.enableNetConnect() const url = new URL('https://op.example.com/jwks') const JWKS = createRemoteJWKSet(url) const err = await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput)) t.true(err instanceof Error) t.true(err.cause instanceof Error && 'code' in err.cause && err.cause.code === 'ENOTFOUND') }) test('handles ECONNREFUSED', async (t) => { agent.enableNetConnect() const url = new URL('http://localhost:3001/jwks') const JWKS = createRemoteJWKSet(url) const err = await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput)) t.true(err instanceof Error) t.true(err.cause instanceof Error && 'code' in err.cause && err.cause.code === 'ECONNREFUSED') }) test.serial('handles a timeout', async (t) => { t.timeout(1000) agent.enableNetConnect() const url = new URL('http://localhost:3000/jwks') const JWKS = createRemoteJWKSet(url, { timeoutDuration: 500, }) const err = await t.throwsAsync(JWKS({ alg: 'RS256' }, {} as FlattenedJWSInput)) t.true(err instanceof errors.JWKSTimeout) }) ================================================ FILE: test/jws/compact.sign.test.ts ================================================ import test from 'ava' import { CompactSign } from '../../src/index.js' test.before((t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.payload = encode('It’s a dangerous business, Frodo, going out your door.') t.context.secret = new Uint8Array(32) }) test('CompactSign', async (t) => { const jws = await new CompactSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) t.is( jws, 'eyJhbGciOiJIUzI1NiJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.UKohvCM6JaKEJlDt7ApBPgcQMW4lmp-UGXfwPmCfUaA', ) }) test('CompactSign.prototype.setProtectedHeader', (t) => { t.throws(() => new CompactSign(t.context.payload).setProtectedHeader({}).setProtectedHeader({}), { instanceOf: TypeError, message: 'setProtectedHeader can only be called once', }) }) test('CompactSign.prototype.sign must have a JOSE header', async (t) => { await t.throwsAsync(new CompactSign(t.context.payload).sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()', }) }) test('CompactSign.prototype.sign JOSE header have an alg', async (t) => { await t.throwsAsync( new CompactSign(t.context.payload).setProtectedHeader({}).sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'JWS "alg" (Algorithm) Header Parameter missing or invalid', }, ) }) ================================================ FILE: test/jws/compact.verify.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { compactVerify, CompactSign } from '../../src/index.js' test.before(async (t) => { t.context.secret = crypto.randomFillSync(new Uint8Array(32)) }) test('JWS format validation', async (t) => { { await t.notThrowsAsync(async () => { await compactVerify( await new CompactSign(new Uint8Array()) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret), t.context.secret, ) }) } await t.throwsAsync(compactVerify(null, new Uint8Array(0)), { message: 'Compact JWS must be a string or Uint8Array', code: 'ERR_JWS_INVALID', }) await t.throwsAsync(compactVerify('.....', new Uint8Array(0)), { message: 'Invalid Compact JWS', code: 'ERR_JWS_INVALID', }) }) test('sign empty data', async (t) => { const jws = await new CompactSign(new Uint8Array(0)) .setProtectedHeader({ alg: 'HS256' }) .sign(new Uint8Array(32)) t.is(jws.split('.')[1], '') const { payload } = await compactVerify(jws, new Uint8Array(32)) t.is(payload.byteLength, 0) }) ================================================ FILE: test/jws/crit.test.ts ================================================ import test from 'ava' import { FlattenedSign } from '../../src/index.js' const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) test('crit member checks check', async (t) => { await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256' }) .setUnprotectedHeader({ crit: ['b64'] }) .sign(new Uint8Array(32)), { code: 'ERR_JWS_INVALID', message: '"crit" (Critical) Header Parameter MUST be integrity protected', }, ) await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', crit: [null], b64: false }) .sign(new Uint8Array(32)), { code: 'ERR_JWS_INVALID', message: '"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present', }, ) await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', crit: ['nope'], nope: 'foo' }) .sign(new Uint8Array(32)), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Extension Header Parameter "nope" is not recognized', }, ) }) ================================================ FILE: test/jws/flattened.sign.test.ts ================================================ import test from 'ava' import { FlattenedSign } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.payload = encode('It’s a dangerous business, Frodo, going out your door.') t.context.secret = new Uint8Array(32) }) test('FlattenedSign', async (t) => { { const jws = await new FlattenedSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setUnprotectedHeader({ foo: 'bar' }) .sign(t.context.secret) t.deepEqual(jws, { header: { foo: 'bar', }, payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', protected: 'eyJhbGciOiJIUzI1NiJ9', signature: 'UKohvCM6JaKEJlDt7ApBPgcQMW4lmp-UGXfwPmCfUaA', }) } { const jws = await new FlattenedSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) t.deepEqual(jws, { protected: 'eyJhbGciOiJIUzI1NiJ9', payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signature: 'UKohvCM6JaKEJlDt7ApBPgcQMW4lmp-UGXfwPmCfUaA', }) } { const jws = await new FlattenedSign(t.context.payload) .setUnprotectedHeader({ alg: 'HS256' }) .sign(t.context.secret) t.deepEqual(jws, { header: { alg: 'HS256', }, payload: 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', signature: 'O7HdMZ_6_aEQWLGGItmCKN3pf8-nZ9mHnPfT7rrPCwk', }) } { for (const value of [ undefined, null, {}, '', 'foo', 1, 0, true, false, [], new FlattenedSign(new Uint8Array()), ]) { t.throws(() => new FlattenedSign(value), { instanceOf: TypeError, message: 'payload must be an instance of Uint8Array', }) } } }) test('FlattenedSign.prototype.setProtectedHeader', (t) => { t.throws( () => new FlattenedSign(t.context.payload).setProtectedHeader({}).setProtectedHeader({}), { instanceOf: TypeError, message: 'setProtectedHeader can only be called once', }, ) }) test('FlattenedSign.prototype.setUnprotectedHeader', (t) => { t.throws( () => new FlattenedSign(t.context.payload).setUnprotectedHeader({}).setUnprotectedHeader({}), { instanceOf: TypeError, message: 'setUnprotectedHeader can only be called once', }, ) }) test('FlattenedSign.prototype.sign must have a JOSE header', async (t) => { await t.throwsAsync(new FlattenedSign(t.context.payload).sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()', }) }) test('FlattenedSign.prototype.sign JOSE header must be disjoint', async (t) => { await t.throwsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setUnprotectedHeader({ alg: 'HS256' }) .sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', }, ) }) test('FlattenedSign.prototype.sign JOSE header have an alg', async (t) => { await t.throwsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({}) .setUnprotectedHeader({}) .sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'JWS "alg" (Algorithm) Header Parameter missing or invalid', }, ) await t.notThrowsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setUnprotectedHeader({ foo: 'bar' }) .sign(t.context.secret), ) await t.notThrowsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret), ) await t.notThrowsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({ foo: 'bar' }) .setUnprotectedHeader({ alg: 'HS256' }) .sign(t.context.secret), ) await t.notThrowsAsync( new FlattenedSign(t.context.payload) .setUnprotectedHeader({ alg: 'HS256' }) .sign(t.context.secret), ) }) ================================================ FILE: test/jws/flattened.verify.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { FlattenedSign, flattenedVerify } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.secret = crypto.randomFillSync(new Uint8Array(32)) }) test('JWS format validation', async (t) => { const fullJws = await new FlattenedSign(t.context.plaintext) .setProtectedHeader({ bar: 'baz' }) .setUnprotectedHeader({ alg: 'HS256' }) .sign(t.context.secret) { await t.throwsAsync(flattenedVerify(null, t.context.secret), { message: 'Flattened JWS must be an object', code: 'ERR_JWS_INVALID', }) } { const jws = { ...fullJws } jws.protected = undefined jws.header = undefined await t.throwsAsync(flattenedVerify(jws, t.context.secret), { message: 'Flattened JWS must have either of the "protected" or "header" members', code: 'ERR_JWS_INVALID', }) } { await t.notThrowsAsync(async () => { await flattenedVerify( await new FlattenedSign(new Uint8Array()) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret), t.context.secret, ) }) } { const jws = { ...fullJws } jws.signature = undefined const assertion = { message: 'JWS Signature missing or incorrect type', code: 'ERR_JWS_INVALID', } await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) jws.signature = null await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } const assertion = { message: 'JWS Protected Header incorrect type', code: 'ERR_JWS_INVALID', } jws.protected = null await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } const assertion = { message: 'JWS Unprotected Header incorrect type', code: 'ERR_JWS_INVALID', } jws.header = null await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } const assertion = { message: 'JWS Protected Header is invalid', code: 'ERR_JWS_INVALID', } jws.protected = `1${jws.protected}` await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } const assertion = { message: 'JWS Payload missing', code: 'ERR_JWS_INVALID', } jws.payload = undefined await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } jws.header = { alg: 'HS256', bar: 'bar' } const assertion = { message: 'JWS Protected and JWS Unprotected Header Parameter names must be disjoint', code: 'ERR_JWS_INVALID', } await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } jws.header = undefined const assertion = { message: 'JWS "alg" (Algorithm) Header Parameter missing or invalid', code: 'ERR_JWS_INVALID', } await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } jws.payload = null const assertion = { message: 'JWS Payload must be a string', code: 'ERR_JWS_INVALID', } await t.throwsAsync(flattenedVerify(jws, t.context.secret), assertion) } { const jws = { ...fullJws } const assertion = { message: 'signature verification failed', code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED', } await t.throwsAsync(flattenedVerify(jws, crypto.randomFillSync(new Uint8Array(32))), assertion) } }) test('sign empty data', async (t) => { const jws = await new FlattenedSign(new Uint8Array(0)) .setProtectedHeader({ alg: 'HS256' }) .sign(new Uint8Array(32)) t.is(jws.payload, '') const { payload } = await flattenedVerify(jws, new Uint8Array(32)) t.is(payload.byteLength, 0) }) ================================================ FILE: test/jws/general.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { GeneralSign, generalVerify } from '../../src/index.js' test.before(async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.plaintext = encode('It’s a dangerous business, Frodo, going out your door.') t.context.secret = crypto.randomFillSync(new Uint8Array(48)) }) test('General JWS signing', async (t) => { const generalJws = await new GeneralSign(t.context.plaintext) .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz' }) .setUnprotectedHeader({ alg: 'HS256' }) .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz' }) .setUnprotectedHeader({ alg: 'HS384' }) .sign() t.is( generalJws.payload, 'SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4', ) t.is(generalJws.signatures.length, 2) }) test('General JWS signing b64:false', async (t) => { const generalJws = await new GeneralSign(t.context.plaintext) .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz', b64: false, crit: ['b64'] }) .setUnprotectedHeader({ alg: 'HS256' }) .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz', b64: false, crit: ['b64'] }) .setUnprotectedHeader({ alg: 'HS384' }) .sign() t.is(generalJws.payload, '') t.is(generalJws.signatures.length, 2) }) test('General JWS signing validations', async (t) => { const sig = new GeneralSign(t.context.plaintext) t.throws( () => { sig .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz', crit: ['b64'], b64: false, alg: 'HS256' }) .setProtectedHeader({ bar: 'baz', crit: ['b64'], b64: false, alg: 'HS256' }) }, { instanceOf: TypeError, message: 'setProtectedHeader can only be called once' }, ) t.throws( () => { sig .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz', crit: ['b64'], b64: true, alg: 'HS384' }) .setUnprotectedHeader({ foo: 'bar' }) .setUnprotectedHeader({ foo: 'bar' }) }, { instanceOf: TypeError, message: 'setUnprotectedHeader can only be called once' }, ) await t.throwsAsync(sig.sign(), { message: 'inconsistent use of JWS Unencoded Payload (RFC7797)', code: 'ERR_JWS_INVALID', }) }) test('General JWS verify format validation', async (t) => { const sig = new GeneralSign(t.context.plaintext) sig .addSignature(t.context.secret) .setProtectedHeader({ bar: 'baz' }) .setUnprotectedHeader({ alg: 'HS256' }) const generalJws = await sig.sign() { await t.notThrowsAsync(async () => { await generalVerify( await new GeneralSign(new Uint8Array()) .addSignature(t.context.secret) .setProtectedHeader({ alg: 'HS256' }) .sign(), t.context.secret, ) }) } { await t.throwsAsync(generalVerify(null, t.context.secret), { message: 'General JWS must be an object', code: 'ERR_JWS_INVALID', }) } { await t.throwsAsync(generalVerify({ signatures: null }, t.context.secret), { message: 'JWS Signatures missing or incorrect type', code: 'ERR_JWS_INVALID', }) } { await t.throwsAsync(generalVerify({ signatures: [null] }, t.context.secret), { message: 'JWS Signatures missing or incorrect type', code: 'ERR_JWS_INVALID', }) } { const jws = { payload: generalJws.payload, signatures: [] } await t.throwsAsync(generalVerify(jws, t.context.secret), { message: 'signature verification failed', code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED', }) } { await t.notThrowsAsync(generalVerify(generalJws, t.context.secret)) } { const { payload, signatures } = generalJws const jws = { payload, signatures: [...signatures, {}] } await t.notThrowsAsync(generalVerify(jws, t.context.secret)) } { const { payload, signatures } = generalJws const jws = { payload, signatures: [{}, ...signatures] } await t.notThrowsAsync(generalVerify(jws, t.context.secret)) } }) test('sign empty data', async (t) => { const jws = await new GeneralSign(new Uint8Array(0)) .addSignature(new Uint8Array(32)) .setProtectedHeader({ alg: 'HS256' }) .sign() t.is(jws.payload, '') const { payload } = await generalVerify(jws, new Uint8Array(32)) t.is(payload.byteLength, 0) }) ================================================ FILE: test/jws/restrictions.test.ts ================================================ import test from 'ava' import * as crypto from 'crypto' import { base64url, exportPKCS8, flattenedDecrypt, FlattenedEncrypt, FlattenedSign, flattenedVerify, generateKeyPair, importJWK, } from '../../src/index.js' function pubjwk(jwk) { const { d, p, q, dp, dq, qi, ...publicJwk } = jwk return publicJwk } test.before((t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) t.context.payload = encode('It’s a dangerous business, Frodo, going out your door.') t.context.rsa2040 = { e: 'AQAB', n: '4waoB9XUAsGc-bhkfY-v3hKEqmLYF4nS-8nji5R5KoOOeWC2hCkvbMfd2IlKRdMU7EmGXNx2BD2FVIqN9mWyZKJlzR2125lgJ-VxCymGv1A9057LEAFIrXsCUqwjPO07hCzZNv8IAAQzq53pnlAgb3TbfrxW24tamhCtaKHb5upAwo4jhYnfzex2--vD7mPxMoTuikno-eD_hxYmA52Uh1gu3wEWy44KA6aFJBpP7m4G5StuHSCXxiOWDaqMeFsMX1jqrom7SwbGJ7j0sf3ZqWrZR4x1pB3wk5Sixi_lmDfOkXhiizYnvJJ5rzr_f0bVdXeAe2U8vpEJSQeA36T7', d: 'T7ZPm2it50XZ-yiOSDQCWSQBZt4L57_hz7ykY6b_IDlO9jlJ_H-FgllvAI-7_2ZNC2YJgmN6IKUFQpjfnas5hvIqcmnDJ9bjlz6NgZDUGipvevVfcUAyJ49wUlzUhpj5c8BXiGLliTPwfIqWs5qIuPm78_TnPnDgoAXJDVr_njr_C6CJFVfWni_6MTeT8iSApGrIJ-tovLlUWSQAyKfWT0QK8dclmREla6-B4YPFwMuBViiSI8dGFpw1O-sEn7D1aMWRepWKr-dgvukuksdL39LxBeGz-iHpI_DMdSB63t5kyjT7GPlbBkD__4ie_Vl4bG30dUZvH_Vt6nXxyXEh', p: 'D0TqaIEyAQHzW1SnC_b1AM9sehg6baMhw4mwSQHqVQreS6a2FKor2xVUdWzT3EurRJ-NThGyi81b30MsKjV_eZ1pIA2r5ulab5CgsV4pkk9LZPZYtz4Rylv9RY3ArtBxziF_BPhTbyy5LXUr5TZUpTQn-LGPOc2sjDaB80uD9XE', q: 'Dt5ET14bw-tNwsgBK-IuhuPzxeCYSi6AkiDqdJuykcN8z8Of-OyPbQSVgTNbvQSZ1anaV0-pvrkER5ilIjcaGtaBmQmCUZe0vKBynakRWXR16SE2mqJAjAmO6VD25cxdnNghdg8ue5XsdyjuQ0dnTXibsIcVyjfaIZgjbhi6mys', dp: 'BD0cWXBsAi2ZceQZD1A6CUSLl4U8Sw07JT4Gyu2WMI940EVyTGBFFmdgb8yLL59t5vnnzyFIkFisxVivXPRG8-rHsRc6fjqPWWMryLEcFzqd8mQUkqHPbH6G25UTRTQmM5PG4AlTmAwxR7Y8PkAL1WSaKAaafPBkkvPatUBkXHE', dq: 'Bv-nBfkVdr6PRu2gZ4i7P_GTMQTMirai_KYT1rnnb2emm6HI7oJj7PwoZ73GJA5DX2jphwnPrCApHI6ExLtNRW7NaD0qo7-WaufXq9EGgqYoTom8y0MuwPxK0hazcW4mborqDUmOJsxml5yjsvWscbIhDxI3No3d1sxneQ6Y4Cs', qi: 'AkpKJwYKvaB73G7IQK5yxXCOvypwSEUK1bOcbhOZSQPPNtKvov11dVsGs-pqHo0Su06IXQv0Ayyy3uxXvsY8CyZ3CbPWMXM06Z3Gr0kZWWfNu8NiFwXvkbe24P-KeXIQGFTfdqSMTfm2an1YNE9e9F36rZ0EkhdKzmwzudA7jtw', kty: 'RSA', } t.context.rsa2048 = { e: 'AQAB', n: '3egkthDOqRIif9azx83Q-HKcVNUeDALom8e5L1rjljB82EKYt5zgfKlgLW4NuJQgLDx5jA7Ez_nz-8lIcVTez-C75_M-Thv2wLhk4ZAYAZZPEmZr76zH8-lClnhxcqFnkOABqLmopr2gF1gBG_IkqEnH_h_yH486YkCd0G2ZNJAzjCOCQMR5pzBnIxC4YUAX7r_-9ilQ8Lv3MSJ0MLv6cujJTneyjWnoh_SfOXRsY6f3gkfR9APj9Q5A8PA8gvbYyN8EHr4OYb1KwNjs_0_X0Aq0e1NJx3H5eiZe6UaFleIbEVZYoNtlNJO3kmGESaxepkWTRAqBZVTfj4KnKX_9Ow', d: 'Q17sbl4x8ACyeq97i4jADf312oeNhMYJSupbHbZxbDKyZJHrfatiOFbP_VrxTX2jOurtWAlP1Xiki2fz13yV3PT095nQ67PvuVkCP70YnLq-rO5tjKmfVz0VW0ub3dqE7-YietBLFLxzc0Ljq1FbscAcuNmID-7TIetOPm5X2i3wxOuiEV2xz1nHVuys3yoO3z4rAitGFl943k71P4FKxK9mp9oTQTnDfKauP0eSOD10L2NiYLkUTg9YC8A0EQopdZatDVAz3hWitNpJg28fWe7rp7yR2YBb4nFcNuygZmzEzp7x0r87NnTt9t4Hjd1rNFbd3hdT6Dy9_pejkmdU2Q', p: '8_HZDF8wp0ujYxiyvAXOL6Xsnv08bQ80uzDOGCnHWWKeFn8n43gc49AdBiJ2ljmfQxExiK0wmetR76zdxTJBIWQdGI2ZJkb6lAIOEBhrXzrWXUSMPgc3qYbQRkmexrAAdwG1nWUymMDd36K5d_YEmL172a-gyEAXMGGW81AaeVU', q: '6N9-STJXjcrwedkJpmmjKLBpU_Nw0UfpKAkh3Xf6jpaF-A2Cvr07JqDVzExMfpYDCcki7IW8SLK5wXVfWCZqXXl4bsb5LAJLnRIglgnDxItlmRf5CWHw7lmBD6BguEIhXU3xPiNrK3XiBVS4k2yDaYHHoAPYXDfTCGpbts7SXE8', dp: 'k_OmtH43P__8BGpCXQ8YUoXL0VG9iFekn7OmC7mrEmdhgjt0sd1ziCf8sm_MhKhGE6Ml68M-qtuyQi8SAjvMjLfvfajDrhd2erYUWWa2GHfS85ZTiHtQIx2EzFxyVAcDASqkP-XUnhi7eJt06XDosMqbhxeh6FIWvl0x9DgtFlE', dq: 'UqK8VY0ftJlHLHXwDrV9yHqRZdEFP76c5jAXbFee-epAL_3bX4QW8WYxeAW7P1BMU7SkR_pNDh8d-6CC7Oz04aaxLd49nXhTDLHaDmP4rE4rB2CSZtnyfSIVwk3PBJOy80EtUjePWCTEx8-AkA_5sf7zr7ytkkvc_yd-1CggTdE', qi: 'd72eV7EbpvaSA3ZiQdGXpMMr41o0ih1WnV80Bxraugj1vMqxLlhVdDhDCVoF3LEoXVz8n2NEl1F6k2o3Gt9C5pXUDJwRGS41FwYVp8RN-aWviJM43mM0oQndJomZyDzjOKzpTzlNlAkFQQbfoagc4sbg-0JxK9rWdnMDW5AR1BU', kty: 'RSA', } }) async function testRSAsig(t, alg) { const message = `${alg} requires key modulusLength to be 2048 bits or larger` const keyBad = t.context.rsa2040 const keyOk = t.context.rsa2048 await t.throwsAsync( new FlattenedSign(t.context.payload) .setProtectedHeader({ alg }) .sign(await importJWK(keyBad, alg)), { instanceOf: TypeError, message }, ) const jws = await new FlattenedSign(t.context.payload) .setProtectedHeader({ alg }) .sign(await importJWK(keyOk, alg)) await t.throwsAsync(flattenedVerify(jws, await importJWK(pubjwk(keyBad), alg)), { instanceOf: TypeError, message, }) } testRSAsig.title = (title, alg) => `${alg} requires key modulusLength to be 2048 bits or larger` async function testRSAenc(t, alg) { const message = `${alg} requires key modulusLength to be 2048 bits or larger` const keyBad = t.context.rsa2040 const keyOk = t.context.rsa2048 await t.throwsAsync( new FlattenedEncrypt(t.context.payload) .setProtectedHeader({ alg, enc: 'A256GCM' }) .encrypt(await importJWK(pubjwk(keyBad), alg)), { instanceOf: TypeError, message }, ) const jwe = await new FlattenedEncrypt(t.context.payload) .setProtectedHeader({ alg, enc: 'A256GCM' }) .encrypt(await importJWK(pubjwk(keyOk), alg)) await t.throwsAsync(flattenedDecrypt(jwe, await importJWK(keyBad, alg)), { instanceOf: TypeError, message, }) } testRSAenc.title = (title, alg) => `${alg} requires key modulusLength to be 2048 bits or larger` async function testECDSASigEncoding(t, alg) { const { privateKey, publicKey } = await generateKeyPair(alg, { extractable: true }) const jws = await new FlattenedSign(t.context.payload) .setProtectedHeader({ alg }) .sign(privateKey) const derEncodedSignature = base64url.encode( crypto.sign(`sha${alg.slice(2, 5)}`, Buffer.from('foo'), await exportPKCS8(privateKey)), ) await t.throwsAsync(flattenedVerify({ ...jws, signature: derEncodedSignature }, publicKey), { message: 'signature verification failed', code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED', }) } testECDSASigEncoding.title = (title, alg) => `${alg} swallows invalid signature encoding errors` test(testRSAsig, 'RS256') test(testRSAsig, 'PS256') test(testRSAsig, 'RS384') test(testRSAsig, 'PS384') test(testRSAsig, 'RS512') test(testRSAsig, 'PS512') test(testRSAenc, 'RSA-OAEP') test(testRSAenc, 'RSA-OAEP-256') test(testRSAenc, 'RSA-OAEP-384') test(testRSAenc, 'RSA-OAEP-512') test(testECDSASigEncoding, 'ES256') test(testECDSASigEncoding, 'ES384') test(testECDSASigEncoding, 'ES512') ================================================ FILE: test/jws/unencoded.test.ts ================================================ import test from 'ava' import { FlattenedSign, flattenedVerify } from '../../src/index.js' const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) test('JSON Web Signature (JWS) Unencoded Payload Option', async (t) => { const jws = await new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', b64: false, crit: ['b64'] }) .sign(new Uint8Array(32)) t.deepEqual(jws, { payload: '', protected: 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19', signature: 'VklKdp4tVYD61VNPDBTqxqdEQcUL3JK-D4dGXu9NvWs', }) await t.notThrowsAsync(flattenedVerify({ ...jws, payload: 'foo' }, new Uint8Array(32))) await t.notThrowsAsync(flattenedVerify({ ...jws, payload: encode('foo') }, new Uint8Array(32))) }) test('b64 check', async (t) => { await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', b64: null, crit: ['b64'] }) .sign(new Uint8Array(32)), { code: 'ERR_JWS_INVALID', message: 'The "b64" (base64url-encode payload) Header Parameter must be a boolean', }, ) await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', crit: ['b64'] }) .sign(new Uint8Array(32)), { code: 'ERR_JWS_INVALID', message: 'Extension Header Parameter "b64" is missing' }, ) await t.throwsAsync( new FlattenedSign(encode('foo')) .setProtectedHeader({ alg: 'HS256', crit: ['b64'] }) .setUnprotectedHeader({ b64: false }) .sign(new Uint8Array(32)), { code: 'ERR_JWS_INVALID', message: 'Extension Header Parameter "b64" MUST be integrity protected', }, ) }) ================================================ FILE: test/jwt/decrypt.test.ts ================================================ import test from 'ava' import timekeeper from 'timekeeper' import { EncryptJWT, jwtDecrypt, CompactEncrypt } from '../../src/index.js' const now = 1604416038 test.before(async (t) => { t.context.secret = new Uint8Array(32) t.context.payload = { 'urn:example:claim': true } timekeeper.freeze(now * 1000) }) test.after(timekeeper.reset) test('Basic JWT Claims Set verification', async (t) => { const issuer = 'urn:example:issuer' const subject = 'urn:example:subject' const audience = 'urn:example:audience' const jti = 'urn:example:jti' const nbf = now - 10 const iat = now - 20 const exp = now + 10 const typ = 'urn:example:typ' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', typ }) .setIssuer(issuer) .setSubject(subject) .setAudience(audience) .setJti(jti) .setNotBefore(nbf) .setExpirationTime(exp) .setIssuedAt(iat) .encrypt(t.context.secret) t.deepEqual( await jwtDecrypt(jwt, t.context.secret, { issuer, subject, audience, jti, typ, maxTokenAge: '30s', }), { payload: { aud: 'urn:example:audience', exp: 1604416048, iat: 1604416018, iss: 'urn:example:issuer', jti: 'urn:example:jti', nbf: 1604416028, sub: 'urn:example:subject', 'urn:example:claim': true, }, protectedHeader: { alg: 'dir', enc: 'A256GCM', typ: 'urn:example:typ', }, }, ) await t.notThrowsAsync(jwtDecrypt(new TextEncoder().encode(jwt), t.context.secret)) }) test('Payload must be an object', async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) for (const value of [0, 1, -1, true, false, null, [], '']) { const token = await new CompactEncrypt(encode(JSON.stringify(value))) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.throwsAsync(jwtDecrypt(token, t.context.secret), { code: 'ERR_JWT_INVALID', message: 'JWT Claims Set must be a top-level JSON object', }) } }) test('Payload must JSON parseable', async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) const token = await new CompactEncrypt(encode('{')) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.throwsAsync(jwtDecrypt(token, t.context.secret), { code: 'ERR_JWT_INVALID', message: 'JWT Claims Set must be a top-level JSON object', }) }) test('contentEncryptionAlgorithms and keyManagementAlgorithms options', async (t) => { const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { keyManagementAlgorithms: ['RSA-OAEP'], }), { code: 'ERR_JOSE_ALG_NOT_ALLOWED', message: '"alg" (Algorithm) Header Parameter value not allowed', }, ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { keyManagementAlgorithms: [null], }), { instanceOf: TypeError, message: '"keyManagementAlgorithms" option must be an array of strings', }, ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { contentEncryptionAlgorithms: ['A128GCM'], }), { code: 'ERR_JOSE_ALG_NOT_ALLOWED', message: '"enc" (Encryption Algorithm) Header Parameter value not allowed', }, ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { contentEncryptionAlgorithms: [null], }), { instanceOf: TypeError, message: '"contentEncryptionAlgorithms" option must be an array of strings', }, ) }) test('typ verification', async (t) => { { const typ = 'urn:example:typ' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', typ }) .encrypt(t.context.secret) await t.notThrowsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'application/urn:example:typ', }), ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'application/urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) } { const typ = 'application/urn:example:typ' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', typ }) .encrypt(t.context.secret) await t.notThrowsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'urn:example:typ', }), ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'application/urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { typ: 'urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) } }) test('Issuer[] verification', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setIssuer(issuer) .encrypt(t.context.secret) await t.notThrowsAsync( jwtDecrypt(jwt, t.context.secret, { issuer: [issuer], }), ) }) test('Issuer[] verification failed', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setIssuer(issuer) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { issuer: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "iss" claim value' }, ) }) test('Issuer[] verification failed []', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setIssuer([issuer]) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { issuer: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "iss" claim value' }, ) }) test('Audience[] verification', async (t) => { const audience = 'urn:example:audience' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setAudience(audience) .encrypt(t.context.secret) await t.notThrowsAsync( jwtDecrypt(jwt, t.context.secret, { audience: [audience], }), ) }) test('Audience[] verification failed', async (t) => { const audience = 'urn:example:audience' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setAudience(audience) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { audience: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "aud" claim value' }, ) }) test('Audience[] verification failed []', async (t) => { const audience = 'urn:example:audience' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setAudience([audience]) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { audience: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "aud" claim value' }, ) }) test('Subject verification failed', async (t) => { const subject = 'urn:example:subject' const jwt = await new EncryptJWT(t.context.payload) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .setSubject(subject) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { subject: 'urn:example:subject:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "sub" claim value' }, ) }) async function numericDateNumber(t, claim) { const jwt = await new EncryptJWT({ [claim]: null }) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.throwsAsync(jwtDecrypt(jwt, t.context.secret), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `"${claim}" claim must be a number`, }) } numericDateNumber.title = (t, claim) => `${claim} must be a number` test('clockTolerance num', async (t) => { const jwt = await new EncryptJWT({ exp: now }) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.notThrowsAsync(jwtDecrypt(jwt, t.context.secret, { clockTolerance: 1 })) await t.notThrowsAsync(jwtDecrypt(jwt, t.context.secret, { clockTolerance: '1s' })) await t.throwsAsync(jwtDecrypt(jwt, t.context.secret, { clockTolerance: null }), { instanceOf: TypeError, message: 'Invalid clockTolerance option type', }) }) async function failingNumericDate(t, claims, assertion, decryptOptions) { const jwt = await new EncryptJWT({ ...claims }) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' }) .encrypt(t.context.secret) await t.throwsAsync(jwtDecrypt(jwt, t.context.secret, { ...decryptOptions }), assertion) } test( 'exp must be in the future', failingNumericDate, { exp: now }, { code: 'ERR_JWT_EXPIRED', message: '"exp" claim timestamp check failed', }, ) test( 'nbf must be at least now', failingNumericDate, { nbf: now + 1 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"nbf" claim timestamp check failed', }, ) test( 'iat must be in the past (maxTokenAge, no exp)', failingNumericDate, { iat: now + 1 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: 5, }, ) test( 'iat must be in the past (maxTokenAge, with exp)', failingNumericDate, { iat: now + 1, exp: now + 10 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: 5, }, ) test( 'iat must be in the past (maxTokenAge, with exp, as a string)', failingNumericDate, { iat: now + 1, exp: now + 10 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: '5s', }, ) test( 'maxTokenAge option', failingNumericDate, { iat: now - 31 }, { code: 'ERR_JWT_EXPIRED', message: '"iat" claim timestamp check failed (too far in the past)', }, { maxTokenAge: '30s', }, ) for (const claim of ['iat', 'nbf', 'exp']) { test(numericDateNumber, claim) } async function replicatedClaimCheck(t, claim) { { const jwt = await new EncryptJWT({ [claim]: 'urn:example' }) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', [claim]: 'urn:example' }) .encrypt(t.context.secret) await t.notThrowsAsync(jwtDecrypt(jwt, t.context.secret)) } { const jwt = await new EncryptJWT({ [claim]: 'urn:example:mismatched' }) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', [claim]: 'urn:example' }) .encrypt(t.context.secret) await t.throwsAsync( jwtDecrypt(jwt, t.context.secret, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `replicated "${claim}" claim header parameter mismatch`, }), ) } } replicatedClaimCheck.title = (t, claim) => `${claim} header claim must match the payload` for (const claim of ['iss', 'sub', 'aud']) { test(replicatedClaimCheck, claim) } ================================================ FILE: test/jwt/encrypt.test.ts ================================================ import anyTest, { type TestFn } from 'ava' import timekeeper from 'timekeeper' import { setters } from './time_setters.js' import { EncryptJWT, compactDecrypt, jwtDecrypt } from '../../src/index.js' const now = 1604416038 interface Context { secret: Uint8Array initializationVector: Uint8Array payload: Record } const test = anyTest as TestFn test.before(async (t) => { t.context.secret = new Uint8Array(16) t.context.initializationVector = new Uint8Array(12) t.context.payload = { 'urn:example:claim': true } timekeeper.freeze(new Date(now * 1000)) }) test.after(timekeeper.reset) test('EncryptJWT', async (t) => { const jwt = await new EncryptJWT(t.context.payload) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(t.context.secret) t.is( jwt, 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..AAAAAAAAAAAAAAAA.eKqvvA6MxuqSRbLVFIidFJb8x4lzPytWkoA.aglYAurAaFCoM8sCqaXSyw', ) }) test('EncryptJWT w/crit', async (t) => { const expected = 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwiY3JpdCI6WyJodHRwOi8vb3BlbmJhbmtpbmcub3JnLnVrL2lhdCJdLCJodHRwOi8vb3BlbmJhbmtpbmcub3JnLnVrL2lhdCI6MH0..AAAAAAAAAAAAAAAA.eKqvvA6MxuqSRbLVFIidFJb8x4lzPytWkoA.Kl-auiUImwUWk4X0xpxa8A' await t.throwsAsync( new EncryptJWT(t.context.payload) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', crit: ['http://openbanking.org.uk/iat'], 'http://openbanking.org.uk/iat': 0, }) .encrypt(t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Extension Header Parameter "http://openbanking.org.uk/iat" is not recognized', }, ) await t.notThrowsAsync(async () => { const jwt = await new EncryptJWT(t.context.payload) .setInitializationVector(t.context.initializationVector) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM', crit: ['http://openbanking.org.uk/iat'], 'http://openbanking.org.uk/iat': 0, }) .encrypt(t.context.secret, { crit: { 'http://openbanking.org.uk/iat': true } }) t.is(jwt, expected) }) await t.throwsAsync(jwtDecrypt(expected, t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Extension Header Parameter "http://openbanking.org.uk/iat" is not recognized', }) await t.notThrowsAsync( jwtDecrypt(expected, t.context.secret, { crit: { 'http://openbanking.org.uk/iat': true } }), ) }) async function testJWTsetFunction(t, method, claim, value, duplicate = false, expected = value) { let enc = new EncryptJWT().setProtectedHeader({ alg: 'dir', enc: 'A128GCM' })[method](value) if (duplicate) { enc = enc[`replicate${method.slice(3)}AsHeader`]() } const jwt = await enc.encrypt(t.context.secret) const { plaintext, protectedHeader, key: resolvedKey, } = await compactDecrypt(jwt, async (header, token) => { t.true('alg' in header) t.true('enc' in header) t.is(header.alg, 'dir') t.is(header.enc, 'A128GCM') t.true('ciphertext' in token) t.true('iv' in token) t.true('protected' in token) t.true('tag' in token) return t.context.secret }) t.is(resolvedKey, t.context.secret) const payload = JSON.parse(new TextDecoder().decode(plaintext)) t.is(payload[claim], expected) if (duplicate) { t.true(claim in protectedHeader) t.is(protectedHeader[claim], expected) } else { t.false(claim in protectedHeader) } } testJWTsetFunction.title = (title, method, claim, value) => `EncryptJWT.prototype.${method} called with ${ value?.constructor?.name || typeof value } (${value}) ${title ? ` (${title})` : ''}` for (const [method, claim, vectors] of setters(now)) { for (const [input, output = input] of vectors) { test(testJWTsetFunction, method, claim, input, false, output) } } test('duplicated', testJWTsetFunction, 'setIssuer', 'iss', 'urn:example:issuer', true) test('duplicated', testJWTsetFunction, 'setSubject', 'sub', 'urn:example:subject', true) test('duplicated', testJWTsetFunction, 'setAudience', 'aud', 'urn:example:audience', true) test('EncryptJWT.prototype.setProtectedHeader', (t) => { t.throws(() => new EncryptJWT(t.context.payload).setProtectedHeader({}).setProtectedHeader({}), { instanceOf: TypeError, message: 'setProtectedHeader can only be called once', }) }) test('EncryptJWT.prototype.setContentEncryptionKey', (t) => { t.throws( () => new EncryptJWT(t.context.payload) .setContentEncryptionKey(t.context.secret) .setContentEncryptionKey(t.context.secret), { instanceOf: TypeError, message: 'setContentEncryptionKey can only be called once', }, ) }) test('EncryptJWT.prototype.setInitializationVector', (t) => { t.throws( () => new EncryptJWT(t.context.payload) .setInitializationVector(t.context.initializationVector) .setInitializationVector(t.context.initializationVector), { instanceOf: TypeError, message: 'setInitializationVector can only be called once', }, ) }) ================================================ FILE: test/jwt/sign.test.ts ================================================ import test from 'ava' import timekeeper from 'timekeeper' import { setters } from './time_setters.js' import { SignJWT, compactVerify, jwtVerify } from '../../src/index.js' const now = 1604416038 test.before(async (t) => { t.context.secret = new Uint8Array(32) t.context.payload = { 'urn:example:claim': true } timekeeper.freeze(now * 1000) }) test.after(timekeeper.reset) test('SignJWT', async (t) => { const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) t.is( jwt, 'eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZX0.yPnOE--rxp3rJaYy0iZaW2Vswvus05G6_ZBdXqIdjGo', ) }) test('SignJWT with default (empty) payload', async (t) => { const jwt = await new SignJWT().setProtectedHeader({ alg: 'HS256' }).sign(t.context.secret) t.is(jwt, 'eyJhbGciOiJIUzI1NiJ9.e30.4E_Bsx-pJi3kOW9wVXN8CgbATwP09D9V5gxh9-9zSZ0') }) test('SignJWT w/crit', async (t) => { const expected = 'eyJhbGciOiJIUzI1NiIsImNyaXQiOlsiaHR0cDovL29wZW5iYW5raW5nLm9yZy51ay9pYXQiXSwiaHR0cDovL29wZW5iYW5raW5nLm9yZy51ay9pYXQiOjB9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZX0.YzOrPZaNql7PpCo43HAJdj-LASP8lOmtb-Bzj9OrNAk' await t.throwsAsync( new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', crit: ['http://openbanking.org.uk/iat'], 'http://openbanking.org.uk/iat': 0, }) .sign(t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Extension Header Parameter "http://openbanking.org.uk/iat" is not recognized', }, ) await t.notThrowsAsync(async () => { const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', crit: ['http://openbanking.org.uk/iat'], 'http://openbanking.org.uk/iat': 0, }) .sign(t.context.secret, { crit: { 'http://openbanking.org.uk/iat': true } }) t.is(jwt, expected) }) await t.throwsAsync(jwtVerify(expected, t.context.secret), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Extension Header Parameter "http://openbanking.org.uk/iat" is not recognized', }) await t.notThrowsAsync( jwtVerify(expected, t.context.secret, { crit: { 'http://openbanking.org.uk/iat': true } }), ) }) test('Signed JWTs cannot use unencoded payload', async (t) => { await t.throwsAsync( () => new SignJWT() .setProtectedHeader({ alg: 'HS256', crit: ['b64'], b64: false }) .sign(t.context.secret), { code: 'ERR_JWT_INVALID', message: 'JWTs MUST NOT use unencoded payload' }, ) await t.throwsAsync(() => new SignJWT().sign(t.context.secret), { code: 'ERR_JWS_INVALID', message: 'either setProtectedHeader or setUnprotectedHeader must be called before #sign()', }) }) async function testJWTsetFunction(t, method, claim, value, expected = value) { const jwt = await new SignJWT() .setProtectedHeader({ alg: 'HS256' }) [method](value) .sign(t.context.secret) const { payload, key: resolvedKey } = await compactVerify(jwt, async (header, token) => { t.true('alg' in header) t.is(header.alg, 'HS256') t.true('payload' in token) t.true('protected' in token) t.true('signature' in token) return t.context.secret }) t.is(resolvedKey, t.context.secret) const claims = JSON.parse(new TextDecoder().decode(payload)) t.true(claim in claims) t.is(claims[claim], expected) } testJWTsetFunction.title = (title, method, claim, value) => `SignJWT.prototype.${method} called with ${value?.constructor?.name || typeof value} (${value})` for (const [method, claim, vectors] of setters(now)) { for (const [input, output = input] of vectors) { test(testJWTsetFunction, method, claim, input, output) } } ================================================ FILE: test/jwt/time_setters.ts ================================================ export function setters(now) { const timeSetters = [ [0], [new Date(now * 1000), now], ['10s', now + 10], ['+10s', now + 10], ['-10s', now - 10], ['+ 10s', now + 10], ['- 10s', now - 10], ['10s from now', now + 10], ['10s ago', now - 10], ] return [ ['setIssuer', 'iss', [['urn:example:issuer']]], ['setSubject', 'sub', [['urn:example:subject']]], ['setAudience', 'aud', [['urn:example:audience']]], ['setJti', 'jti', [['urn:example:jti']]], ['setIssuedAt', 'iat', [[undefined, now], ...timeSetters]], ['setExpirationTime', 'exp', timeSetters], ['setNotBefore', 'nbf', timeSetters], ] } ================================================ FILE: test/jwt/unsecured.test.ts ================================================ import test from 'ava' import timekeeper from 'timekeeper' import { setters } from './time_setters.js' import { UnsecuredJWT, decodeJwt } from '../../src/index.js' const now = 1604416038 test.before(async (t) => { t.context.payload = { 'urn:example:claim': true } timekeeper.freeze(now * 1000) }) test.after(timekeeper.reset) test('UnsecuredJWT', async (t) => { const jwt = new UnsecuredJWT(t.context.payload).encode() t.is(jwt, 'eyJhbGciOiJub25lIn0.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZX0.') }) test('UnsecuredJWT validations', (t) => { t.throws(() => UnsecuredJWT.decode(null), { code: 'ERR_JWT_INVALID', message: 'Unsecured JWT must be a string', }) t.throws(() => UnsecuredJWT.decode('....'), { code: 'ERR_JWT_INVALID', message: 'Invalid Unsecured JWT', }) t.throws(() => UnsecuredJWT.decode('..'), { code: 'ERR_JWT_INVALID', message: 'Invalid Unsecured JWT', }) t.throws(() => UnsecuredJWT.decode('..foo'), { code: 'ERR_JWT_INVALID', message: 'Invalid Unsecured JWT', }) t.throws(() => UnsecuredJWT.decode('eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZX0.'), { code: 'ERR_JWT_INVALID', message: 'Invalid Unsecured JWT', }) }) test('new UnsecuredJWT()', (t) => { t.is(new UnsecuredJWT().encode(), 'eyJhbGciOiJub25lIn0.e30.') }) async function testJWTsetFunction(t, method, claim, value, expected = value) { const jwt = new UnsecuredJWT()[method](value).encode() const claims = decodeJwt(jwt) t.true(claim in claims) t.is(claims[claim], expected) } testJWTsetFunction.title = (title, method, claim, value) => `UnsecuredJWT.prototype.${method} called with ${ value?.constructor?.name || typeof value } (${value})` for (const [method, claim, vectors] of setters(now)) { for (const [input, output = input] of vectors) { test(testJWTsetFunction, method, claim, input, output) } } ================================================ FILE: test/jwt/verify.test.ts ================================================ import test from 'ava' import timekeeper from 'timekeeper' import { SignJWT, jwtVerify, CompactSign } from '../../src/index.js' const now = 1604416038 test.before(async (t) => { t.context.secret = new Uint8Array(32) t.context.payload = { 'urn:example:claim': true } timekeeper.freeze(now * 1000) }) test.after(timekeeper.reset) test('Basic JWT Claims Set verification', async (t) => { const issuer = 'urn:example:issuer' const subject = 'urn:example:subject' const audience = 'urn:example:audience' const jti = 'urn:example:jti' const nbf = now - 10 const iat = now - 20 const exp = now + 10 const typ = 'urn:example:typ' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', typ }) .setIssuer(issuer) .setSubject(subject) .setAudience(audience) .setJti(jti) .setNotBefore(nbf) .setExpirationTime(exp) .setIssuedAt(iat) .sign(t.context.secret) t.deepEqual( await jwtVerify(jwt, t.context.secret, { issuer, subject, audience, jti, typ, maxTokenAge: '30s', }), { payload: { aud: 'urn:example:audience', exp: 1604416048, iat: 1604416018, iss: 'urn:example:issuer', jti: 'urn:example:jti', nbf: 1604416028, sub: 'urn:example:subject', 'urn:example:claim': true, }, protectedHeader: { alg: 'HS256', typ: 'urn:example:typ', }, }, ) await t.notThrowsAsync(jwtVerify(new TextEncoder().encode(jwt), t.context.secret)) }) test('Payload must be an object', async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) for (const value of [0, 1, -1, true, false, null, [], '']) { const token = await new CompactSign(encode(JSON.stringify(value))) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync(jwtVerify(token, t.context.secret), { code: 'ERR_JWT_INVALID', message: 'JWT Claims Set must be a top-level JSON object', }) } }) test('incorrect hmac signature lengths', async (t) => { const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync(jwtVerify(jwt.slice(0, -3), t.context.secret), { code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED', message: 'signature verification failed', }) }) test('Payload must JSON parseable', async (t) => { const encode = TextEncoder.prototype.encode.bind(new TextEncoder()) const token = await new CompactSign(encode('{')) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync(jwtVerify(token, t.context.secret), { code: 'ERR_JWT_INVALID', message: 'JWT Claims Set must be a top-level JSON object', }) }) test('algorithms options', async (t) => { const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { algorithms: ['PS256'], }), { code: 'ERR_JOSE_ALG_NOT_ALLOWED', message: '"alg" (Algorithm) Header Parameter value not allowed', }, ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { algorithms: [null], }), { instanceOf: TypeError, message: '"algorithms" option must be an array of strings', }, ) }) test('typ verification', async (t) => { { const typ = 'urn:example:typ' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', typ }) .sign(t.context.secret) await t.notThrowsAsync( jwtVerify(jwt, t.context.secret, { typ: 'application/urn:example:typ', }), ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { typ: 'urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { typ: 'application/urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) } { const typ = 'application/urn:example:typ' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', typ }) .sign(t.context.secret) await t.notThrowsAsync( jwtVerify(jwt, t.context.secret, { typ: 'urn:example:typ', }), ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { typ: 'application/urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { typ: 'urn:example:typ:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) } { const typ = 'text/plain' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256', typ }) .sign(t.context.secret) await t.notThrowsAsync( jwtVerify(jwt, t.context.secret, { typ: 'text/plain', }), ) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { typ: 'application/text/plain', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "typ" JWT header value' }, ) } }) test('Issuer[] verification', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setIssuer(issuer) .sign(t.context.secret) await t.notThrowsAsync( jwtVerify(jwt, t.context.secret, { issuer: [issuer], }), ) }) test('Issuer[] verification failed', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setIssuer(issuer) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { issuer: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "iss" claim value' }, ) }) test('Issuer[] verification failed []', async (t) => { const issuer = 'urn:example:issuer' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setIssuer([issuer]) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { issuer: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "iss" claim value' }, ) }) test('Audience[] verification', async (t) => { const audience = 'urn:example:audience' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setAudience(audience) .sign(t.context.secret) await t.notThrowsAsync( jwtVerify(jwt, t.context.secret, { audience: [audience], }), ) }) test('Audience[] verification failed', async (t) => { const audience = 'urn:example:audience' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setAudience(audience) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { audience: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "aud" claim value' }, ) }) test('Audience[] verification failed []', async (t) => { const audience = 'urn:example:audience' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setAudience([audience]) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { audience: [], }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "aud" claim value' }, ) }) test('Subject verification failed', async (t) => { const subject = 'urn:example:subject' const jwt = await new SignJWT(t.context.payload) .setProtectedHeader({ alg: 'HS256' }) .setSubject(subject) .sign(t.context.secret) await t.throwsAsync( jwtVerify(jwt, t.context.secret, { subject: 'urn:example:subject:2', }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: 'unexpected "sub" claim value' }, ) }) async function numericDateNumber(t, claim) { const jwt = await new SignJWT({ [claim]: null }) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync(jwtVerify(jwt, t.context.secret), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `"${claim}" claim must be a number`, }) } numericDateNumber.title = (t, claim) => `${claim} must be a number` test('clockTolerance num', async (t) => { const jwt = await new SignJWT({ exp: now }) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.notThrowsAsync(jwtVerify(jwt, t.context.secret, { clockTolerance: 1 })) await t.notThrowsAsync(jwtVerify(jwt, t.context.secret, { clockTolerance: '1s' })) }) async function failingNumericDate(t, claims, assertion, verifyOptions) { const jwt = await new SignJWT({ ...claims }) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) await t.throwsAsync(jwtVerify(jwt, t.context.secret, { ...verifyOptions }), assertion) } test( 'exp must be in the future', failingNumericDate, { exp: now }, { code: 'ERR_JWT_EXPIRED', message: '"exp" claim timestamp check failed', }, ) test( 'nbf must be at least now', failingNumericDate, { nbf: now + 1 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"nbf" claim timestamp check failed', }, ) test( 'iat must be in the past (maxTokenAge, no exp)', failingNumericDate, { iat: now + 1 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: 5, }, ) test( 'iat must be in the past (maxTokenAge, with exp)', failingNumericDate, { iat: now + 1, exp: now + 10 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: 5, }, ) test( 'iat must be in the past (maxTokenAge, with exp, as a string)', failingNumericDate, { iat: now + 1, exp: now + 10 }, { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: '"iat" claim timestamp check failed (it should be in the past)', }, { maxTokenAge: '5s', }, ) test( 'maxTokenAge option', failingNumericDate, { iat: now - 31 }, { code: 'ERR_JWT_EXPIRED', message: '"iat" claim timestamp check failed (too far in the past)', }, { maxTokenAge: '30s', }, ) for (const claim of ['iat', 'nbf', 'exp']) { test(numericDateNumber, claim) } test('Signed JWTs cannot use unencoded payload', async (t) => { await t.throwsAsync( jwtVerify( 'eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.foo.VklKdp4tVYD61VNPDBTqxqdEQcUL3JK-D4dGXu9NvWs', t.context.secret, ), { code: 'ERR_JWT_INVALID', message: 'JWTs MUST NOT use unencoded payload' }, ) }) test('signatures are compared before claim set', async (t) => { // https://github.com/panva/jose/discussions/447 const jwt = await new SignJWT({ exp: 0 }) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) // with valid secret should throw exp failing to verify await t.throwsAsync(jwtVerify(jwt, t.context.secret), { code: 'ERR_JWT_EXPIRED' }) // with invalid secret should throw signature failing to verify await t.throwsAsync(jwtVerify(jwt, new Uint8Array([0x00, 0x01])), { code: 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED', }) }) test('requiredClaims claims check', async (t) => { const jwt = await new SignJWT({ ...t.context.payload, }) .setProtectedHeader({ alg: 'HS256' }) .sign(t.context.secret) const requiredClaims = ['nbf'] for (const [claim, option] of [ ['iss', 'issuer'], ['aud', 'audience'], ['iat', 'maxTokenAge'], ['sub', 'subject'], ]) { await t.throwsAsync(jwtVerify(jwt, t.context.secret, { [option]: 'foo' }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `missing required "${claim}" claim`, }) await t.throwsAsync(jwtVerify(jwt, t.context.secret, { [option]: 'foo', requiredClaims }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `missing required "${claim}" claim`, }) } await t.throwsAsync(jwtVerify(jwt, t.context.secret, { requiredClaims }), { code: 'ERR_JWT_CLAIM_VALIDATION_FAILED', message: `missing required "nbf" claim`, }) t.deepEqual(requiredClaims, ['nbf']) }) ================================================ FILE: test/tsconfig.json ================================================ { "include": ["**/*.test.ts"], "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "Node", "strict": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitAny": true, "esModuleInterop": true } } ================================================ FILE: test/unit/buffer_utils.test.ts ================================================ import test from 'ava' import { uint32be } from '../../src/lib/buffer_utils.js' test('lib/buffer_utils.ts', (t) => { t.throws(() => uint32be(-1), { instanceOf: RangeError }) t.throws(() => uint32be(2 ** 32), { instanceOf: RangeError }) }) ================================================ FILE: test/unit/cek.test.ts ================================================ import test from 'ava' import { generateCek } from '../../src/lib/content_encryption.js' test('lib/cek.ts', (t) => { t.throws(() => generateCek('foo'), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Unsupported JWE Algorithm: foo', }) }) ================================================ FILE: test/unit/check_iv_length.test.ts ================================================ import test from 'ava' import { checkIvLength } from '../../src/lib/content_encryption.js' test('lib/check_iv_length.ts', (t) => { t.throws(() => checkIvLength('A256GCM', new Uint8Array(13)), { code: 'ERR_JWE_INVALID', message: 'Invalid Initialization Vector length', }) t.notThrows(() => checkIvLength('A256GCM', new Uint8Array(12))) }) ================================================ FILE: test/unit/check_key_type.test.ts ================================================ import test from 'ava' const types = 'CryptoKey, KeyObject, JSON Web Key, or Uint8Array' const asymmetricTypes = 'CryptoKey, KeyObject, or JSON Web Key' import * as lib from '../../src/index.js' import { checkKeyType } from '../../src/lib/check_key_type.js' test('lib/check_key_type.ts', async (t) => { const expected = { instanceOf: TypeError, message: new RegExp(`^Key for the .+ algorithm must be (?:one )?of type ${types}\\.`), } t.throws(() => checkKeyType('HS256'), expected) t.throws(() => checkKeyType('HS256', undefined), expected) t.throws(() => checkKeyType('HS256', null), expected) t.throws(() => checkKeyType('HS256', 1), expected) t.throws(() => checkKeyType('HS256', 0), expected) t.throws(() => checkKeyType('HS256', true), expected) t.throws(() => checkKeyType('HS256', Boolean), expected) t.throws(() => checkKeyType('HS256', []), expected) t.throws(() => checkKeyType('HS256', ''), expected) t.throws(() => checkKeyType('HS256', 'foo'), expected) t.throws(() => checkKeyType('PS256', new Uint8Array()), { ...expected, message: new RegExp(`^Key for the .+ algorithm must be (?:one )?of type ${asymmetricTypes}\\.`), }) let secret = await lib.generateSecret('HS256') t.throws(() => checkKeyType('PS256', secret), { ...expected, message: 'CryptoKey instances for asymmetric algorithms must not be of type "secret"', }) t.notThrows(() => checkKeyType('dir', new Uint8Array())) t.notThrows(() => checkKeyType('HS256', new Uint8Array())) t.notThrows(() => checkKeyType('PBES2-HS256+A128KW', new Uint8Array())) t.notThrows(() => checkKeyType('A256GCMKW', new Uint8Array())) t.notThrows(() => checkKeyType('A256KW', new Uint8Array())) secret = await lib.generateSecret('A256GCMKW') t.notThrows(() => checkKeyType('dir', secret)) secret = await lib.generateSecret('HS256') t.notThrows(() => checkKeyType('HS256', secret)) secret = await lib.generateSecret('A256GCMKW') t.notThrows(() => checkKeyType('A256GCMKW', secret)) secret = await lib.generateSecret('A256KW') t.notThrows(() => checkKeyType('A256KW', secret)) let keypair = await lib.generateKeyPair('PS256') t.throws(() => checkKeyType('PS256', keypair.publicKey, 'sign'), { ...expected, message: 'CryptoKey instances for asymmetric algorithm signing must be of type "private"', }) t.throws(() => checkKeyType('HS256', keypair.privateKey), { ...expected, message: 'CryptoKey instances for symmetric algorithms must be of type "secret"', }) t.throws(() => checkKeyType('PS256', keypair.privateKey, 'verify'), { ...expected, message: 'CryptoKey instances for asymmetric algorithm verifying must be of type "public"', }) keypair = await lib.generateKeyPair('ECDH-ES') t.throws(() => checkKeyType('ECDH-ES', keypair.publicKey, 'decrypt'), { ...expected, message: 'CryptoKey instances for asymmetric algorithm decryption must be of type "private"', }) t.throws(() => checkKeyType('ECDH-ES', keypair.privateKey, 'encrypt'), { ...expected, message: 'CryptoKey instances for asymmetric algorithm encryption must be of type "public"', }) }) ================================================ FILE: test/unit/check_key_type_jwk.test.ts ================================================ import test from 'ava' const types = 'CryptoKey, KeyObject, JSON Web Key, or Uint8Array' const asymmetricTypes = 'CryptoKey, KeyObject, or JSON Web Key' import { checkKeyType } from '../../src/lib/check_key_type.js' test('lib/check_key_type.ts with JWK', async (t) => { const expected = { instanceOf: TypeError, message: new RegExp(`^Key for the .+ algorithm must be (?:one )?of type ${types}\\.`), } t.throws(() => checkKeyType('HS256'), expected) t.throws(() => checkKeyType('HS256', undefined), expected) t.throws(() => checkKeyType('HS256', null), expected) t.throws(() => checkKeyType('HS256', 1), expected) t.throws(() => checkKeyType('HS256', 0), expected) t.throws(() => checkKeyType('HS256', true), expected) t.throws(() => checkKeyType('HS256', Boolean), expected) t.throws(() => checkKeyType('HS256', []), expected) t.throws(() => checkKeyType('HS256', ''), expected) t.throws(() => checkKeyType('HS256', 'foo'), expected) t.throws(() => checkKeyType('PS256', new Uint8Array()), { ...expected, message: new RegExp(`^Key for the .+ algorithm must be (?:one )?of type ${asymmetricTypes}\\.`), }) t.notThrows(() => checkKeyType('HS256', { kty: 'oct', k: '' }, 'sign')) t.notThrows(() => checkKeyType('HS256', { kty: 'oct', k: '', use: 'sig' }, 'sign')) t.notThrows(() => checkKeyType('HS256', { kty: 'oct', k: '', key_ops: ['sign'] }, 'sign')) t.notThrows(() => checkKeyType('HS256', { kty: 'oct', k: '', alg: 'HS256' })) t.throws(() => checkKeyType('HS256', { kty: 'oct', k: '', use: 'enc' }, 'sign'), { ...expected, message: 'Invalid key for this operation, its "use" must be "sig" when present', }) t.throws(() => checkKeyType('HS256', { kty: 'oct', k: '', alg: 'HS384' }, 'sign'), { ...expected, message: 'Invalid key for this operation, its "alg" must be "HS256" when present', }) t.throws(() => checkKeyType('HS256', { kty: 'RSA' }), { ...expected, message: 'JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present', }) t.notThrows(() => checkKeyType('PS256', { kty: 'RSA' }, 'verify')) t.throws(() => checkKeyType('PS256', { kty: 'RSA', d: '' }, 'verify'), { ...expected, message: 'JSON Web Key for this operation must be a public JWK', }) t.notThrows(() => checkKeyType('PS256', { kty: 'RSA', d: '' }, 'sign')) t.throws(() => checkKeyType('PS256', { kty: 'RSA' }, 'sign'), { ...expected, message: 'JSON Web Key for this operation must be a private JWK', }) }) ================================================ FILE: test/unit/iv.test.ts ================================================ import test from 'ava' import { generateIv } from '../../src/lib/content_encryption.js' test('lib/iv.ts', (t) => { t.throws(() => generateIv('foo'), { code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Unsupported JWE Algorithm: foo', }) }) ================================================ FILE: test/unit/secs.test.ts ================================================ import test from 'ava' const { secs } = await import('../../src/lib/jwt_claims_set.js') test('lib/secs.ts', (t) => { for (const sign of ['+', '+ ', '']) { for (const v of ['sec', 'secs', 'second', 'seconds', 's']) { t.is(secs(`${sign}1${v}`), 1) t.is(secs(`${sign}1 ${v}`), 1) } for (const v of ['minute', 'minutes', 'min', 'mins', 'm']) { t.is(secs(`${sign}1${v}`), 60) t.is(secs(`${sign}1 ${v}`), 60) } for (const v of ['hour', 'hours', 'hr', 'hrs', 'h']) { t.is(secs(`${sign}1${v}`), 3600) t.is(secs(`${sign}1 ${v}`), 3600) } for (const v of ['day', 'days', 'd']) { t.is(secs(`${sign}1${v}`), 86400) t.is(secs(`${sign}1 ${v}`), 86400) } for (const v of ['week', 'weeks', 'w']) { t.is(secs(`${sign}1${v}`), 604800) t.is(secs(`${sign}1 ${v}`), 604800) } for (const v of ['years', 'year', 'yrs', 'yr', 'y']) { t.is(secs(`${sign}1${v}`), 31557600) t.is(secs(`${sign}1 ${v}`), 31557600) } } for (const sign of ['-', '- ']) { for (const v of ['sec', 'secs', 'second', 'seconds', 's']) { t.is(secs(`${sign}1${v}`), -1) t.is(secs(`${sign}1 ${v}`), -1) } for (const v of ['minute', 'minutes', 'min', 'mins', 'm']) { t.is(secs(`${sign}1${v}`), -60) t.is(secs(`${sign}1 ${v}`), -60) } for (const v of ['hour', 'hours', 'hr', 'hrs', 'h']) { t.is(secs(`${sign}1${v}`), -3600) t.is(secs(`${sign}1 ${v}`), -3600) } for (const v of ['day', 'days', 'd']) { t.is(secs(`${sign}1${v}`), -86400) t.is(secs(`${sign}1 ${v}`), -86400) } for (const v of ['week', 'weeks', 'w']) { t.is(secs(`${sign}1${v}`), -604800) t.is(secs(`${sign}1 ${v}`), -604800) } for (const v of ['years', 'year', 'yrs', 'yr', 'y']) { t.is(secs(`${sign}1${v}`), -31557600) t.is(secs(`${sign}1 ${v}`), -31557600) } } for (const v of ['sec', 'secs', 'second', 'seconds', 's']) { t.is(secs(`1${v} ago`), -1) t.is(secs(`1${v} from now`), 1) t.is(secs(`1 ${v} ago`), -1) t.is(secs(`1 ${v} from now`), 1) } for (const v of ['minute', 'minutes', 'min', 'mins', 'm']) { t.is(secs(`1${v} ago`), -60) t.is(secs(`1${v} from now`), 60) t.is(secs(`1 ${v} ago`), -60) t.is(secs(`1 ${v} from now`), 60) } for (const v of ['hour', 'hours', 'hr', 'hrs', 'h']) { t.is(secs(`1${v} ago`), -3600) t.is(secs(`1${v} from now`), 3600) t.is(secs(`1 ${v} ago`), -3600) t.is(secs(`1 ${v} from now`), 3600) } for (const v of ['day', 'days', 'd']) { t.is(secs(`1${v} ago`), -86400) t.is(secs(`1${v} from now`), 86400) t.is(secs(`1 ${v} ago`), -86400) t.is(secs(`1 ${v} from now`), 86400) } for (const v of ['week', 'weeks', 'w']) { t.is(secs(`1${v} ago`), -604800) t.is(secs(`1${v} from now`), 604800) t.is(secs(`1 ${v} ago`), -604800) t.is(secs(`1 ${v} from now`), 604800) } for (const v of ['years', 'year', 'yrs', 'yr', 'y']) { t.is(secs(`1${v} ago`), -31557600) t.is(secs(`1${v} from now`), 31557600) t.is(secs(`1 ${v} ago`), -31557600) t.is(secs(`1 ${v} from now`), 31557600) } t.throws(() => secs('1 fortnight'), { instanceOf: TypeError, message: 'Invalid time period format', }) t.throws(() => secs('= 1 second'), { instanceOf: TypeError, message: 'Invalid time period format', }) t.throws(() => secs('- 1 second ago'), { instanceOf: TypeError, message: 'Invalid time period format', }) }) ================================================ FILE: test/util/decode_jwt.test.ts ================================================ import test from 'ava' import { decodeJwt, errors, base64url } from '../../src/index.js' test('invalid inputs', (t) => { const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' const parts = jwt.split('.') t.throws(() => decodeJwt(null as any), { instanceOf: errors.JWTInvalid, message: 'JWTs must use Compact JWS serialization, JWT must be a string', }) t.throws(() => decodeJwt('....'), { instanceOf: errors.JWTInvalid, message: 'Only JWTs using Compact JWS serialization can be decoded', }) t.throws(() => decodeJwt('.'), { instanceOf: errors.JWTInvalid, message: 'Invalid JWT', }) t.throws(() => decodeJwt([parts[0], '', parts[2]].join('.')), { instanceOf: errors.JWTInvalid, message: 'JWTs must contain a payload', }) t.throws(() => decodeJwt([parts[0], base64url.encode('null'), parts[2]].join('.')), { instanceOf: errors.JWTInvalid, message: 'Invalid JWT Claims Set', }) t.throws(() => decodeJwt([parts[0], base64url.encode('[]'), parts[2]].join('.')), { instanceOf: errors.JWTInvalid, message: 'Invalid JWT Claims Set', }) t.throws(() => decodeJwt([parts[0], base64url.encode('{"notajson'), parts[2]].join('.')), { instanceOf: errors.JWTInvalid, message: 'Failed to parse the decoded payload as JSON', }) t.deepEqual(decodeJwt([parts[0], base64url.encode('{}'), parts[2]].join('.')), {}) t.deepEqual(decodeJwt(jwt), { sub: '1234567890', name: 'John Doe', iat: 1516239022, }) }) ================================================ FILE: test/util/decode_protected_header.test.ts ================================================ import test from 'ava' import { decodeProtectedHeader } from '../../src/index.js' test('invalid inputs', (t) => { t.throws(() => decodeProtectedHeader(null), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('.'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('ew..'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('bnVsbA..'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('W10..'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('...'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('ew....'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('bnVsbA....'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('W10....'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader('.....'), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader({ protected: null }), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader({ protected: 'ew' }), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader({ protected: 'bnVsbA' }), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader({ protected: 'W10' }), { instanceOf: TypeError, message: 'Invalid Token or Protected Header formatting', }) t.throws(() => decodeProtectedHeader({}), { instanceOf: TypeError, message: 'Token does not contain a Protected Header', }) t.deepEqual(decodeProtectedHeader('eyJhbGciOiJIUzI1NiJ9..'), { alg: 'HS256' }) t.deepEqual(decodeProtectedHeader('eyJhbGciOiJIUzI1NiJ9....'), { alg: 'HS256' }) t.deepEqual(decodeProtectedHeader({ protected: 'eyJhbGciOiJIUzI1NiJ9' }), { alg: 'HS256' }) }) ================================================ FILE: tools/postbump.cjs ================================================ const { x } = require('tar') const { globSync } = require('glob') const { execSync } = require('child_process') const { readFileSync, writeFileSync } = require('fs') const { version } = require('../package.json') const readme = readFileSync('docs/README.md') const tagName = `v${version}` const opts = { stdio: 'inherit' } try { execSync('git rm -f docs/**/*.md', opts) } catch {} execSync('find docs -type d | grep "docs/" | xargs rm -rf', opts) execSync('npx patch-package', opts) execSync(`npm run docs:generate -- --gitRevision ${tagName}`, opts) globSync('docs/**/*.md').forEach((file) => { const content = readFileSync(file, 'utf-8') const updatedContent = content.replaceAll('\\<`ArrayBufferLike`\\>', '') writeFileSync(file, updatedContent, 'utf-8') }) writeFileSync('docs/README.md', readme) execSync('npm pack', opts) execSync('rm -rf dist', opts) x({ f: `jose-${version}.tgz`, strip: true, filter(loc) { return loc.startsWith('package/dist/') }, sync: true, }) execSync('npm run build:deno', opts) writeFileSync( 'dist/deno/README.md', readFileSync('docs/readme.md', { encoding: 'utf-8' }) .replace(/^`jose` is distributed.+$\n\n/m, '') .replace( /\*\*[\s\S]+```/gm, `**\`example\`** Deno import \`\`\`js import * as jose from 'https://deno.land/x/jose@${tagName}/index.ts' \`\`\``, ) .replace(/(\]\()(?!https)/gm, `](https://github.com/panva/jose/blob/${tagName}/docs/`), ) execSync('npm run build:bundle', opts) execSync('npm run build:bundle-min', opts) execSync('npm run build:umd', opts) execSync('git add docs/**/*.md', opts) const filesToUpdate = [ { path: './README.md', regex: /jose@v\d+\.\d+\.\d+/gm, replacement: `jose@v${version}` }, { path: './docs/README.md', regex: /jose@v\d+\.\d+\.\d+/gm, replacement: `jose@v${version}` }, { path: './jsr.json', regex: /"version": "\d+\.\d+\.\d+"/gm, replacement: `"version": "${version}"`, }, ] filesToUpdate.forEach(({ path, regex, replacement }) => { writeFileSync(path, readFileSync(path, { encoding: 'utf-8' }).replace(regex, replacement)) execSync(`git add ${path}`, { stdio: 'inherit' }) }) const ts = globSync('dist/**/**.ts') function filterExamples(file) { let inExample = false return file .split('\n') .filter((line) => { let remove = inExample if (line.includes('* @example')) { remove = inExample = true } else if (line.includes('```') && !line.includes('```js')) { inExample = false } else if (inExample) { remove = true } return remove === false }) .join('\n') } function trimExcessComment(file) { let previousWasEmpty = false return file .split('\n') .filter((line) => { let remove = false if (line.trim() === '*' && previousWasEmpty) { remove = true } else if (line.trim() === '*' || line.trim() === '/**') { previousWasEmpty = true } else { previousWasEmpty = false } return remove === false }) .join('\n') } for (const file of ts) { writeFileSync(file, trimExcessComment(filterExamples(readFileSync(file, { encoding: 'utf-8' })))) } for (const dir of ['types', 'deno', 'webapi']) { execSync(`git add dist/${dir} -f`, opts) } ================================================ FILE: tools/prebump.cjs ================================================ const { execSync } = require('child_process') const { readFileSync, writeFileSync } = require('fs') const dryRun = execSync('standard-version --dry-run', { encoding: 'utf-8' }) ;/tagging release v(\d+\.\d+\.\d+)/gm.test(dryRun) const version = RegExp.$1 const tagName = `v${version}` const path = './src/jwks/remote.ts' writeFileSync(path, readFileSync(path, { encoding: 'utf-8' }).replace(/v(\d+\.\d+\.\d+)/g, tagName)) execSync(`git add ${path}`, { stdio: 'inherit' }) ================================================ FILE: tools/publish.cjs ================================================ const { readFileSync, writeFileSync, unlinkSync } = require('fs') const pkg = JSON.parse(readFileSync('./package.json')) pkg.devDependencies = undefined pkg.scripts = undefined pkg.imports = undefined writeFileSync('./package.json', `${JSON.stringify(pkg, null, 2)}\n`) unlinkSync('./CHANGELOG.md') ================================================ FILE: tools/release-notes.cjs ================================================ const fs = require('fs') const { execSync } = require('child_process') execSync('git show HEAD -- CHANGELOG.md > CHANGELOG.diff') const tag = execSync('git tag --points-at HEAD').toString().trim() fs.writeFileSync( 'notes.md', fs .readFileSync('CHANGELOG.diff') .toString() .split('\n') .filter((line) => line.startsWith('+') && !line.startsWith('+++')) .map((line) => line.slice(1)) .slice(3) .join('\n'), ) execSync(`gh release create ${tag} -F notes.md --title ${tag} --discussion-category Releases`) ================================================ FILE: tsconfig/types.json ================================================ { "extends": "../tsconfig.json", "compilerOptions": { "target": "ESNext", "outDir": "../dist/types", "declaration": true, "emitDeclarationOnly": true, "removeComments": false } } ================================================ FILE: tsconfig.json ================================================ { "files": ["./src/index.ts"], "compilerOptions": { "outDir": "./dist/webapi", "target": "ES2022", "module": "ES2022", "lib": ["ES2022", "DOM", "DOM.Iterable"], "types": [], "strict": true, "forceConsistentCasingInFileNames": true, "noUnusedLocals": true, "noUnusedParameters": true, "sourceMap": false, "removeComments": true, "verbatimModuleSyntax": true, "erasableSyntaxOnly": true } } ================================================ FILE: typedoc.json ================================================ { "$schema": "https://typedoc.org/schema.json", "disableSources": true, "hideBreadcrumbs": true, "entryPoints": [ "src/types.d.ts", "src/jwe/**/*.ts", "src/jwk/*.ts", "src/jwks/*.ts", "src/jws/**/*.ts", "src/jwt/*.ts", "src/key/*.ts", "src/util/*.ts", ], "excludeExternals": true, "excludePrivate": true, "excludeProtected": true, "gitRevision": "main", "hideGenerator": true, "out": "docs", "plugin": ["typedoc-plugin-markdown", "typedoc-plugin-mdn-links"], "readme": "none", "sort": ["kind", "static-first", "required-first", "alphabetical"], "githubPages": false, "parametersFormat": "table", "pageTitleTemplates": { "member": "{keyword} {kind}: {name}\n\n## [💗 Help the project](https://github.com/sponsors/panva)\n\nSupport from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva)." }, "hidePageHeader": true }