Showing preview only (422K chars total). Download the full file or copy to clipboard to get everything.
Repository: lcobucci/jwt
Branch: 6.0.x
Commit: 9ffc631092e5
Files: 201
Total size: 378.5 KB
Directory structure:
gitextract_h82sgh5a/
├── .composer-require-checker.config.json
├── .gitattributes
├── .github/
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── SECURITY.md
│ └── workflows/
│ ├── backwards-compatibility.yml
│ ├── benchmarks.yml
│ ├── coding-standards.yml
│ ├── composer-json-lint.yml
│ ├── mutation-tests.yml
│ ├── phpunit.yml
│ ├── release-on-milestone-closed.yml
│ └── static-analysis.yml
├── .gitignore
├── .readthedocs.yaml
├── .roave-backward-compatibility-check.json
├── LICENSE
├── Makefile
├── README.md
├── composer.json
├── docs/
│ ├── configuration.md
│ ├── extending-the-library.md
│ ├── index.md
│ ├── installation.md
│ ├── issuing-tokens.md
│ ├── parsing-tokens.md
│ ├── quick-start.md
│ ├── rotating-keys.md
│ ├── supported-algorithms.md
│ ├── upgrading.md
│ └── validating-tokens.md
├── infection.json.dist
├── mkdocs.yml
├── phpbench.json
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── renovate.json
├── src/
│ ├── Builder.php
│ ├── ClaimsFormatter.php
│ ├── Configuration.php
│ ├── Decoder.php
│ ├── Encoder.php
│ ├── Encoding/
│ │ ├── CannotDecodeContent.php
│ │ ├── CannotEncodeContent.php
│ │ ├── ChainedFormatter.php
│ │ ├── JoseEncoder.php
│ │ ├── MicrosecondBasedDateConversion.php
│ │ ├── UnifyAudience.php
│ │ └── UnixTimestampDates.php
│ ├── Exception.php
│ ├── JwtFacade.php
│ ├── Parser.php
│ ├── Signer/
│ │ ├── Blake2b.php
│ │ ├── CannotSignPayload.php
│ │ ├── Ecdsa/
│ │ │ ├── ConversionFailed.php
│ │ │ ├── MultibyteStringConverter.php
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ ├── Sha512.php
│ │ │ └── SignatureConverter.php
│ │ ├── Ecdsa.php
│ │ ├── Eddsa.php
│ │ ├── Hmac/
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ └── Sha512.php
│ │ ├── Hmac.php
│ │ ├── InvalidKeyProvided.php
│ │ ├── Key/
│ │ │ ├── FileCouldNotBeRead.php
│ │ │ └── InMemory.php
│ │ ├── Key.php
│ │ ├── OpenSSL.php
│ │ ├── Rsa/
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ └── Sha512.php
│ │ └── Rsa.php
│ ├── Signer.php
│ ├── SodiumBase64Polyfill.php
│ ├── Token/
│ │ ├── Builder.php
│ │ ├── DataSet.php
│ │ ├── InvalidTokenStructure.php
│ │ ├── Parser.php
│ │ ├── Plain.php
│ │ ├── RegisteredClaimGiven.php
│ │ ├── RegisteredClaims.php
│ │ ├── Signature.php
│ │ └── UnsupportedHeaderFound.php
│ ├── Token.php
│ ├── UnencryptedToken.php
│ ├── Validation/
│ │ ├── Constraint/
│ │ │ ├── CannotValidateARegisteredClaim.php
│ │ │ ├── HasClaim.php
│ │ │ ├── HasClaimWithValue.php
│ │ │ ├── IdentifiedBy.php
│ │ │ ├── IssuedBy.php
│ │ │ ├── LeewayCannotBeNegative.php
│ │ │ ├── LooseValidAt.php
│ │ │ ├── PermittedFor.php
│ │ │ ├── RelatedTo.php
│ │ │ ├── SignedWith.php
│ │ │ ├── SignedWithOneInSet.php
│ │ │ ├── SignedWithUntilDate.php
│ │ │ └── StrictValidAt.php
│ │ ├── Constraint.php
│ │ ├── ConstraintViolation.php
│ │ ├── NoConstraintsGiven.php
│ │ ├── RequiredConstraintsViolated.php
│ │ ├── SignedWith.php
│ │ ├── ValidAt.php
│ │ └── Validator.php
│ └── Validator.php
└── tests/
├── Benchmark/
│ ├── AlgorithmsBench.php
│ ├── CreateSignatureBench.php
│ ├── Ecdsa/
│ │ ├── private-256.key
│ │ ├── private-384.key
│ │ ├── private-521.key
│ │ ├── public-256.key
│ │ ├── public-384.key
│ │ └── public-521.key
│ ├── IssueTokenBench.php
│ ├── ParseTokenBench.php
│ ├── Rsa/
│ │ ├── private.key
│ │ └── public.key
│ └── VerifySignatureBench.php
├── ConfigurationTest.php
├── ES512TokenTest.php
├── EcdsaTokenTest.php
├── EddsaTokenTest.php
├── Encoding/
│ ├── ChainedFormatterTest.php
│ ├── JoseEncoderTest.php
│ ├── MicrosecondBasedDateConversionTest.php
│ ├── UnifyAudienceTest.php
│ └── UnixTimestampDatesTest.php
├── HmacTokenTest.php
├── JwtFacadeTest.php
├── KeyDumpSigner.php
├── Keys.php
├── MaliciousTamperingPreventionTest.php
├── RFC6978VectorTest.php
├── RsaTokenTest.php
├── Signer/
│ ├── Blake2bTest.php
│ ├── Ecdsa/
│ │ ├── EcdsaTestCase.php
│ │ ├── MultibyteStringConverterTest.php
│ │ ├── Sha256Test.php
│ │ ├── Sha384Test.php
│ │ └── Sha512Test.php
│ ├── EddsaTest.php
│ ├── FakeSigner.php
│ ├── Hmac/
│ │ ├── HmacTestCase.php
│ │ ├── Sha256Test.php
│ │ ├── Sha384Test.php
│ │ └── Sha512Test.php
│ ├── Key/
│ │ ├── InMemoryTest.php
│ │ ├── empty.pem
│ │ └── test.pem
│ └── Rsa/
│ ├── KeyValidationSigner.php
│ ├── KeyValidationTest.php
│ ├── RsaTestCase.php
│ ├── Sha256Test.php
│ ├── Sha384Test.php
│ └── Sha512Test.php
├── SodiumBase64PolyfillTest.php
├── TimeFractionPrecisionTest.php
├── Token/
│ ├── BuilderTest.php
│ ├── DataSetTest.php
│ ├── ParserTest.php
│ ├── PlainTest.php
│ └── SignatureTest.php
├── UnsignedTokenTest.php
├── UnsupportedParser.php
├── Validation/
│ ├── Constraint/
│ │ ├── ConstraintTestCase.php
│ │ ├── HasClaimTest.php
│ │ ├── HasClaimWithValueTest.php
│ │ ├── IdentifiedByTest.php
│ │ ├── IssuedByTest.php
│ │ ├── LooseValidAtTest.php
│ │ ├── PermittedForTest.php
│ │ ├── RelatedToTest.php
│ │ ├── SignedWithOneInSetTest.php
│ │ ├── SignedWithTest.php
│ │ ├── SignedWithUntilDateTest.php
│ │ ├── StrictValidAtTest.php
│ │ └── ValidAtTestCase.php
│ ├── ConstraintViolationTest.php
│ ├── RequiredConstraintsViolatedTest.php
│ └── ValidatorTest.php
└── _keys/
├── ecdsa/
│ ├── private.key
│ ├── private2.key
│ ├── private_ec384.key
│ ├── private_ec512.key
│ ├── public1.key
│ ├── public2.key
│ ├── public2_ec512.key
│ ├── public3.key
│ ├── public_ec384.key
│ └── public_ec512.key
└── rsa/
├── encrypted-private.key
├── encrypted-public.key
├── private.key
├── private_512.key
├── public.key
└── public_512.key
================================================
FILE CONTENTS
================================================
================================================
FILE: .composer-require-checker.config.json
================================================
{
"symbol-whitelist" : [
"NoDiscard"
],
"php-core-extensions" : [
"Core",
"date",
"json",
"hash",
"SPL",
"standard"
]
}
================================================
FILE: .gitattributes
================================================
/docs export-ignore
/tests export-ignore
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/.composer-require-checker.config.json export-ignore
/*.yml export-ignore
/CONTRIBUTING.md export-ignore
/*.dist export-ignore
/phpbench.json export-ignore
/composer.lock export-ignore
/README.md export-ignore
/Makefile export-ignore
/.roave-backward-compatibility-check.json export-ignore
/.readthedocs.yaml export-ignore
/renovate.json export-ignore
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to lcobucci/jwt
First off, thanks for taking the time to contribute!
## Reporting issues
We accept bug and feature requests via issues created [here](https://github.com/lcobucci/jwt/issues).
### Prior to submitting a bug report
- **Always search the issue or pull request list first** - The odds are good that if you've found a problem, someone else has found it, too;
- **Always try the [master](https://github.com/lcobucci/jwt) branch** - to see if the reported bug has not already been fixed.
## Pull Requests
We accept contributions via pull requests [here](https://github.com/lcobucci/jwt/pulls).
- Follow [PSR-2 coding standards](http://www.php-fig.org/psr/psr-2);
- Follow [PSR-4 autoloading standards](http://www.php-fig.org/psr/psr-4);
- Follow [semver](http://semver.org);
- Add tests (everything MUST be well tested);
- Improve documentation (don't forget to update README.md);
- Create topic branches (don't send a PR from your master);
- One pull request per feature;
- Send coherent history by rebasing your work before submitting;
### Running tests locally
We provide a GNU-Make configuration that allows you to run the CI checks locally with a single command: `make`.
### Branches
- **5.0.x**: used to the next major release (new features that breaks BC)
- **4.4.x**: used to develop migration path for the next version
- **4.3.x**: used to fix bugs
- **4.2.x**: unmaintained
- **4.1.x**: unmaintained
- **4.0.x**: unmaintained
- **3.4.x**: security issues only
- **3.3**: unmaintained
- **3.2**: unmaintained
- **3.1**: unmaintained
- **3.0**: unmaintained
- **2.1**: unmaintained
**Thank you and happy coding!**
@lcobucci
================================================
FILE: .github/FUNDING.yml
================================================
github: lcobucci
patreon: lcobucci
================================================
FILE: .github/SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
| ----------- | ------------------ |
| 4.2.x-dev | :white_check_mark: |
| 4.1.x | :white_check_mark: |
| 4.0.x | :white_check_mark: |
| 3.4 | :white_check_mark: |
| < 3.4 | :x: |
## Reporting a Vulnerability
In case of a security vulnerability, please file an issue. It's shall be
prioritised and ported back/forth to all supported versions.
================================================
FILE: .github/workflows/backwards-compatibility.yml
================================================
name: "Backwards compatibility check"
on:
pull_request:
jobs:
bc-check:
name: "Backwards compatibility check"
runs-on: "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
with:
fetch-depth: 0
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
php-version: "8.3"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get composer cache directory
id: composer-cache
run: echo "composer_cache_dir=$(composer global config cache-files-dir)" >> $GITHUB_OUTPUT
- name: "Cache dependencies"
uses: "actions/cache@v5.0.4"
with:
path: ${{ steps.composer-cache.outputs.composer_cache_dir }}
key: "php-8.3-bc-break-check-${{ hashFiles('.github/workflows/backwards-compatibility.yml') }}"
restore-keys: "php-8.3-bc-break-check-"
- name: "Install dependencies"
run: composer global require roave/backward-compatibility-check
- name: "BC Check"
run: |
~/.composer/vendor/bin/roave-backward-compatibility-check --from=${{ github.event.pull_request.base.sha }} --format=github-actions
================================================
FILE: .github/workflows/benchmarks.yml
================================================
name: "Benchmarks"
on:
pull_request:
push:
jobs:
benchmarks:
name: "Run benchmarks"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "locked"
php-version:
- "8.3"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "PhpBench"
run: "make phpbench"
================================================
FILE: .github/workflows/coding-standards.yml
================================================
name: "Check Coding Standards"
on:
pull_request:
push:
jobs:
coding-standards:
name: "Check Coding Standards"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "locked"
php-version:
- "8.3"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "Coding Standard"
run: "make phpcs PHPCS_FLAGS='-q --report=checkstyle | cs2pr'"
================================================
FILE: .github/workflows/composer-json-lint.yml
================================================
name: "Lint composer.json"
on:
pull_request:
push:
jobs:
coding-standards:
name: "Lint composer.json"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "highest"
php-version:
- "8.3"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, composer-normalize, composer-require-checker, composer-unused
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "Validate composer.json"
run: "composer validate --strict"
- name: "Normalize composer.json"
run: "composer-normalize --dry-run"
- name: "Check composer.json explicit dependencies"
run: "composer-require-checker check --config-file=.composer-require-checker.config.json"
# - name: "Check composer.json unused dependencies"
# run: "composer-unused"
================================================
FILE: .github/workflows/mutation-tests.yml
================================================
name: "Mutation tests"
on:
pull_request:
push:
jobs:
mutation-tests:
name: "Mutation tests"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "locked"
php-version:
- "8.3"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "xdebug"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "Infection"
run: "make infection PHPUNIT_FLAGS=--coverage-clover=coverage.xml INFECTION_FLAGS=--logger-github"
- name: "Upload Code Coverage"
uses: "codecov/codecov-action@v5.5.3"
================================================
FILE: .github/workflows/phpunit.yml
================================================
name: "PHPUnit tests"
on:
pull_request:
push:
jobs:
phpunit:
name: "PHPUnit tests"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "lowest"
- "highest"
- "locked"
php-version:
- "8.3"
- "8.4"
- "8.5"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "Tests"
run: "make phpunit"
================================================
FILE: .github/workflows/release-on-milestone-closed.yml
================================================
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
name: "Automatic Releases"
on:
milestone:
types:
- "closed"
jobs:
release:
name: "GIT tag, release & create merge-up PR"
runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Release"
uses: "laminas/automatic-releases@1.26.2"
with:
command-name: "laminas:automatic-releases:release"
env:
"SHELL_VERBOSITY": "3"
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create Merge-Up Pull Request"
uses: "laminas/automatic-releases@1.26.2"
with:
command-name: "laminas:automatic-releases:create-merge-up-pull-request"
env:
"SHELL_VERBOSITY": "3"
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create and/or Switch to new Release Branch"
uses: "laminas/automatic-releases@1.26.2"
with:
command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor"
env:
"SHELL_VERBOSITY": "3"
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Bump Changelog Version On Originating Release Branch"
uses: "laminas/automatic-releases@1.26.2"
with:
command-name: "laminas:automatic-releases:bump-changelog"
env:
"SHELL_VERBOSITY": "3"
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
- name: "Create new milestones"
uses: "laminas/automatic-releases@1.26.2"
with:
command-name: "laminas:automatic-releases:create-milestones"
env:
"SHELL_VERBOSITY": "3"
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
================================================
FILE: .github/workflows/static-analysis.yml
================================================
name: "Static Analysis"
on:
pull_request:
push:
jobs:
static-analysis:
name: "Static Analysis"
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
dependencies:
- "locked"
php-version:
- "8.3"
operating-system:
- "ubuntu-latest"
steps:
- name: "Checkout"
uses: "actions/checkout@v6.0.2"
- name: "Install PHP"
uses: "shivammathur/setup-php@2.37.0"
with:
coverage: "none"
php-version: "${{ matrix.php-version }}"
ini-values: memory_limit=-1
tools: composer:v2, cs2pr
- name: "Install dependencies"
uses: "ramsey/composer-install@4.0.0"
with:
dependency-versions: "${{ matrix.dependencies }}"
- name: "PHPStan"
run: "make phpstan"
================================================
FILE: .gitignore
================================================
vendor
phpunit.xml
infection.txt
coverage
phpcs.xml
/.phpcs.cache
/.phpunit.result.cache
/.phpunit.cache
/build
================================================
FILE: .readthedocs.yaml
================================================
version: 2
build:
os: ubuntu-22.04
tools:
python: "3"
mkdocs:
configuration: mkdocs.yml
================================================
FILE: .roave-backward-compatibility-check.json
================================================
{
"baseline": [
"#\\[BC\\] SKIPPED: Unable to compile initializer in method Lcobucci\\\\.+#"
]
}
================================================
FILE: LICENSE
================================================
Copyright (c) 2014, Luís Cobucci
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: Makefile
================================================
PARALLELISM := $(shell nproc)
.PHONY: all
all: install phpcbf phpcs phpstan phpunit infection phpbench
.PHONY: install
install: vendor/composer/installed.json
vendor/composer/installed.json: composer.json composer.lock
@composer install $(INSTALL_FLAGS)
@touch -c composer.json composer.lock vendor/composer/installed.json
.PHONY: phpunit
phpunit:
@php -d assert.exception=1 -d zend.assertions=1 vendor/bin/phpunit $(PHPUNIT_FLAGS)
.PHONY: infection
infection:
@php -d assert.exception=1 -d zend.assertions=1 -d xdebug.mode=coverage vendor/bin/phpunit --coverage-xml=build/coverage-xml --log-junit=build/junit.xml $(PHPUNIT_FLAGS)
@php -d assert.exception=1 -d zend.assertions=1 vendor/bin/infection -v -s --threads=$(PARALLELISM) --coverage=build --skip-initial-tests $(INFECTION_FLAGS)
.PHONY: phpcbf
phpcbf:
@vendor/bin/phpcbf --parallel=$(PARALLELISM) || true
.PHONY: phpcs
phpcs:
@vendor/bin/phpcs --parallel=$(PARALLELISM) $(PHPCS_FLAGS)
.PHONY: phpstan
phpstan:
@php -d xdebug.mode=off vendor/bin/phpstan analyse --memory-limit=-1
ifndef PHPBENCH_REPORT
override PHPBENCH_REPORT = aggregate
endif
.PHONY: phpbench
phpbench:
@vendor/bin/phpbench run -l dots --retry-threshold=5 --report=$(PHPBENCH_REPORT) $(PHPBENCH_FLAGS)
================================================
FILE: README.md
================================================
# JWT
[![Gitter]](https://gitter.im/lcobucci/jwt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Total Downloads]](https://packagist.org/packages/lcobucci/jwt)
[![Latest Stable Version]](https://packagist.org/packages/lcobucci/jwt)
[![Unstable Version]](https://packagist.org/packages/lcobucci/jwt)
[![Build Status]](https://github.com/lcobucci/jwt/actions?query=workflow%3A%22PHPUnit%20Tests%22+branch%3A4.1.x)
[![Code Coverage]](https://codecov.io/gh/lcobucci/jwt)
A simple library to work with JSON Web Token and JSON Web Signature based on the [RFC 7519](https://tools.ietf.org/html/rfc7519).
## Installation
Package is available on [Packagist](https://packagist.org/packages/lcobucci/jwt),
you can install it using [Composer](https://getcomposer.org).
```shell
composer require lcobucci/jwt
```
## Documentation
The documentation is available at <https://lcobucci-jwt.readthedocs.io/en/latest/>.
[Gitter]: https://img.shields.io/badge/GITTER-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat-square
[Total Downloads]: https://img.shields.io/packagist/dt/lcobucci/jwt.svg?style=flat-square
[Latest Stable Version]: https://img.shields.io/packagist/v/lcobucci/jwt.svg?style=flat-square
[Unstable Version]: https://img.shields.io/packagist/vpre/lcobucci/jwt.svg?style=flat-square
[Build Status]: https://img.shields.io/github/actions/workflow/status/lcobucci/jwt/phpunit.yml?branch=5.1.x&style=flat-square
[Code Coverage]: https://codecov.io/gh/lcobucci/jwt/branch/5.1.x/graph/badge.svg
================================================
FILE: composer.json
================================================
{
"name": "lcobucci/jwt",
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"license": [
"BSD-3-Clause"
],
"type": "library",
"keywords": [
"JWT",
"JWS"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"require": {
"php": "~8.3.0 || ~8.4.0 || ~8.5.0",
"ext-openssl": "*",
"ext-sodium": "*",
"psr/clock": "^1.0"
},
"require-dev": {
"infection/infection": "^0.32.6",
"lcobucci/clock": "^3.5.0",
"lcobucci/coding-standard": "^11.2",
"phpbench/phpbench": "^1.4.3",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.1.40",
"phpstan/phpstan-deprecation-rules": "^2.0.4",
"phpstan/phpstan-phpunit": "^2.0.16",
"phpstan/phpstan-strict-rules": "^2.0.10",
"phpunit/phpunit": "^12.5.14 || ^13.0.5"
},
"suggest": {
"lcobucci/clock": ">= 3.2"
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Lcobucci\\JWT\\Tests\\": "tests"
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"infection/extension-installer": true,
"ocramius/package-versions": true,
"phpstan/extension-installer": true
},
"preferred-install": "dist",
"sort-packages": true
}
}
================================================
FILE: docs/configuration.md
================================================
# Configuration
In order to simplify the setup of the library, we provide the class `Lcobucci\JWT\Configuration`.
It's meant for:
* Configuring the default algorithm (signer) and key(s) to be used
* Configuring the default set of validation constraints
* Providing custom implementation for the [extension points](extending-the-library.md)
* Retrieving components (encoder, decoder, parser, validator, and builder)
## Initialisation
The `Lcobucci\JWT\Signer\Key\InMemory` object is used for symmetric/asymmetric signature.
To initialise it, you can pass the key content as a plain text:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$key = InMemory::plainText('my-key-as-plaintext');
```
Provide a base64 encoded string:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$key = InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=');
```
Or provide a file path:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
// this reads the file and keeps its contents in memory
$key = InMemory::file(__DIR__ . '/path-to-my-key-stored-in-a-file.pem');
```
### For symmetric algorithms
[Symmetric algorithms](supported-algorithms.md#symmetric-algorithms) use the same key for both signature creation and verification.
This means that it's really important that your key **remains secret**.
!!! Tip
It is recommended that you use a key with lots of entropy, preferably generated using a cryptographically secure pseudo-random number generator (CSPRNG).
You can use the [CryptoKey](https://github.com/AndrewCarterUK/CryptoKey) tool to do this for you.
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forSymmetricSigner(
new Signer\Hmac\Sha256(),
// replace the value below with a key of your own!
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
// You may also override the JOSE encoder/decoder if needed
// by providing extra arguments here
);
```
### For asymmetric algorithms
[Asymmetric algorithms](supported-algorithms.md#asymmetric-algorithms) use a **private key** for signature creation and a **public key** for verification.
This means that it's fine to distribute your **public key**. However, the **private key** should **remain secret**.
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forAsymmetricSigner(
new Signer\Rsa\Sha256(),
InMemory::file(__DIR__ . '/my-private-key.pem'),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
// You may also override the JOSE encoder/decoder if needed
// by providing extra arguments here
);
```
## Customisation
By using the setters of the `Lcobucci\JWT\Configuration` you may customise the setup of this library.
!!! Important
If you want to use a customised configuration, please make sure you call the setters before of invoking any getter.
Otherwise, the default implementations will be used.
### Builder factory
It configures how the token builder should be created.
It's useful when you want to provide a [custom Builder](extending-the-library.md#builder).
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forSymmetricSigner(
new Signer\Hmac\Sha256(),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
);
$configuration = $configuration->withBuilderFactory(
static function (ClaimsFormatter $formatter): Builder {
// This assumes `MyCustomBuilder` is an existing class
return new MyCustomBuilder(new JoseEncoder(), $formatter);
}
);
```
### Parser
It configures how the token parser should be created.
It's useful when you want to provide a [custom Parser](extending-the-library.md#parser).
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forSymmetricSigner(
new Signer\Hmac\Sha256(),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
);
// This assumes `MyParser` is an existing class
$configuration = $configuration->withParser(new MyParser());
```
### Validator
It configures how the token validator should be created.
It's useful when you want to provide a [custom Validator](extending-the-library.md#validator).
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forSymmetricSigner(
new Signer\Hmac\Sha256(),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
);
// This assumes `MyValidator` is an existing class
$configuration = $configuration->withValidator(new MyValidator());
```
### Validation constraints
It configures which are the base constraints to be used during validation.
```php
<?php
declare(strict_types=1);
use Lcobucci\Clock\SystemClock; // If you prefer, other PSR-20 implementations may also be used
// (https://packagist.org/providers/psr/clock-implementation)
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
require 'vendor/autoload.php';
$configuration = Configuration::forSymmetricSigner(
new Signer\Hmac\Sha256(),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
);
$configuration = $configuration->withValidationConstraints(
new SignedWith($configuration->signer(), $configuration->signingKey()),
new StrictValidAt(SystemClock::fromUTC()),
new IssuedBy('https://api.my-awesome-company.com')
);
```
## Retrieve components
Once you've made all the necessary configuration you can pass the configuration object around your code and use the getters to retrieve the components:
These are the available getters:
* `Lcobucci\JWT\Configuration#builder()`: retrieves the token builder (always creating a new instance)
* `Lcobucci\JWT\Configuration#parser()`: retrieves the token parser
* `Lcobucci\JWT\Configuration#signer()`: retrieves the signer
* `Lcobucci\JWT\Configuration#signingKey()`: retrieves the key for signature creation
* `Lcobucci\JWT\Configuration#verificationKey()`: retrieves the key for signature verification
* `Lcobucci\JWT\Configuration#validator()`: retrieves the token validator
* `Lcobucci\JWT\Configuration#validationConstraints()`: retrieves the default set of validation constraints
================================================
FILE: docs/extending-the-library.md
================================================
# Extending the library
!!! Note
The examples here fetch the configuration object from a hypothetical dependency injection container.
You can create it in the same script or require it from a different file. It basically depends on how your system is bootstrapped.
We've designed a few extension points in this library.
These should enable people to easily customise our core components if they want to.
## Builder
The token builder defines a fluent interface for plain token creation.
To create your own builder of it you must implement the `Lcobucci\JWT\Builder` interface:
```php
use Lcobucci\JWT\Builder;
final class MyCustomTokenBuilder implements Builder
{
// implement all methods
}
```
Then, register a custom factory in the [configuration object]:
```php
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Configuration;
$config = $container->get(Configuration::class);
assert($config instanceof Configuration);
$configuration = $configuration->withBuilderFactory(
static function (ClaimsFormatter $formatter): Builder {
return new MyCustomTokenBuilder($formatter);
}
);
```
## Claims formatter
By default, we provide formatters that:
- unify the audience claim, making sure we use strings when there's only one item in that claim
- format date based claims using microseconds (float)
You may customise and even create your own formatters:
```php
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Configuration;
use Serializable;
final class ClaimSerializer implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach ($claims as $claim => $claimValue) {
if ($claimValue instanceof Serializable) {
$claims[$claim] = $claimValue->serialize();
}
}
return $claims;
}
}
$config = $container->get(Configuration::class);
assert($config instanceof Configuration);
$builder = $config->builder(new ClaimSerializer());
```
The class `Lcobucci\JWT\Encoding\ChainedFormatter` allows for users to combine multiple formatters.
## Parser
The token parser defines how a JWT string should be converted into token objects.
To create your own parser of it you must implement the `Lcobucci\JWT\Parser` interface:
```php
use Lcobucci\JWT\Parser;
final class MyCustomTokenParser implements Parser
{
// implement all methods
}
```
Then register an instance in the [configuration object]:
```php
use Lcobucci\JWT\Configuration;
$config = $container->get(Configuration::class);
assert($config instanceof Configuration);
$configuration = $configuration->withParser(new MyCustomTokenParser());
```
## Signer
The signer defines how to create and verify signatures.
To create your own signer of it you must implement the `Lcobucci\JWT\Signer` interface:
```php
use Lcobucci\JWT\Signer;
final class SignerForAVeryCustomizedAlgorithm implements Signer
{
// implement all methods
}
```
Then pass an instance of it while creating an instance of the [configuration object], [issuing a token](issuing-tokens.md), or [validating a token].
## Key
The key object is passed down to signers and provide the necessary information to create and verify signatures.
To create your own signer of it you must implement the `Lcobucci\JWT\Signer\Key` interface:
```php
use Lcobucci\JWT\Signer\Key;
final class KeyWithSomeMagicalProperties implements Key
{
// implement all methods
}
```
## Validator
The token validator defines how to apply validation constraint to either validate or assert tokens.
To create your own validator of it you must implement the `Lcobucci\JWT\Validator` interface:
```php
use Lcobucci\JWT\Validator;
final class MyCustomTokenValidator implements Validator
{
// implement all methods
}
```
Then register an instance in the [configuration object]:
```php
use Lcobucci\JWT\Configuration;
$config = $container->get(Configuration::class);
assert($config instanceof Configuration);
$configuration = $configuration->withValidator(new MyCustomTokenValidator());
```
## Validation constraints
A validation constraint define how one or more claims/headers should be validated.
Custom validation constraints are handy to provide advanced rules for the registered claims or to validate private claims.
To create your own implementation of constraint you must implement the `Lcobucci\JWT\Validation\Constraint` interface:
```php
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class SubjectMustBeAValidUser implements Constraint
{
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw new ConstraintViolation('You should pass a plain token');
}
if (! $this->existsInDatabase($token->claims()->get('sub'))) {
throw new ConstraintViolation('Token related to an unknown user');
}
}
private function existsInDatabase(string $userId): bool
{
// ...
}
}
```
Then use it while [validating a token].
[configuration object]: configuration.md
[validating a token]: validating-tokens.md
================================================
FILE: docs/index.md
================================================
# Overview
`lcobucci/jwt` is a framework-agnostic PHP library that allows you to issue, parse, and validate JSON Web Tokens based on the [RFC 7519].
## Support
If you're having any issue to use the library, please [create a GH issue].
You can also reach us and other users of this library via our [Gitter channel].
## License
The project is licensed under the MIT license, see [LICENSE file].
[RFC 7519]: https://tools.ietf.org/html/rfc7519
[create a GH issue]: https://github.com/lcobucci/jwt/issues/new
[Gitter channel]: https://gitter.im/lcobucci/jwt
[LICENSE file]: https://github.com/lcobucci/jwt/blob/master/LICENSE
================================================
FILE: docs/installation.md
================================================
# Installation
This package is available on [Packagist] and you can install it using [Composer].
By running the following command you'll add `lcobucci/jwt` as a dependency to your project:
```sh
composer require lcobucci/jwt
```
## Autoloading
!!! Note
We'll be omitting the autoloader from the code samples to simplify the documentation.
In order to be able to use the classes provided by this library you're also required to include [Composer]'s autoloader in your application:
```php
require 'vendor/autoload.php';
```
!!! Tip
If you're not familiar with how [composer] works, we highly recommend you to take some time to read it's documentation - especially the [autoloading section].
[Packagist]: https://packagist.org/packages/lcobucci/jwt
[Composer]: https://getcomposer.org
[autoloading section]: https://getcomposer.org/doc/01-basic-usage.md#autoloading
================================================
FILE: docs/issuing-tokens.md
================================================
# Issuing tokens
To issue new tokens you must create a new token builder, customise it, and ask it to build the token:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Token\Builder;
require 'vendor/autoload.php';
$tokenBuilder = Builder::new(new JoseEncoder(), ChainedFormatter::default());
$algorithm = new Sha256();
$signingKey = InMemory::plainText(random_bytes(32));
$now = new DateTimeImmutable();
$token = $tokenBuilder
// Configures the issuer (iss claim)
->issuedBy('http://example.com')
// Configures the audience (aud claim)
->permittedFor('http://example.org')
// Configures the subject of the token (sub claim)
->relatedTo('component1')
// Configures the id (jti claim)
->identifiedBy('4f1g23a12aa')
// Configures the time that the token was issue (iat claim)
->issuedAt($now)
// Configures the time that the token can be used (nbf claim)
->canOnlyBeUsedAfter($now->modify('+1 minute'))
// Configures the expiration time of the token (exp claim)
->expiresAt($now->modify('+1 hour'))
// Configures a new claim, called "uid"
->withClaim('uid', 1)
// Configures a new header, called "foo"
->withHeader('foo', 'bar')
// Builds a new token
->getToken($algorithm, $signingKey);
echo $token->toString();
```
Once you've created a token, you're able to retrieve its data and convert it to its string representation:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Token\Builder;
require 'vendor/autoload.php';
$tokenBuilder = Builder::new(new JoseEncoder(), ChainedFormatter::default());
$algorithm = new Sha256();
$signingKey = InMemory::plainText(random_bytes(32));
$token = $tokenBuilder
->issuedBy('http://example.com')
->withClaim('uid', 1)
->withHeader('foo', 'bar')
->getToken($algorithm, $signingKey);
$token->headers(); // Retrieves the token headers
$token->claims(); // Retrieves the token claims
echo $token->headers()->get('foo'), PHP_EOL; // will print "bar"
echo $token->claims()->get('iss'), PHP_EOL; // will print "http://example.com"
echo $token->claims()->get('uid'), PHP_EOL; // will print "1"
echo $token->toString(), PHP_EOL; // The string representation of the object is a JWT string
```
!!! Note
Some systems make use of components to handle dependency injection.
If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library.
================================================
FILE: docs/parsing-tokens.md
================================================
# Parsing tokens
To parse a token you must create a new parser and ask it to parse a string:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
use Lcobucci\JWT\UnencryptedToken;
require 'vendor/autoload.php';
$parser = new Parser(new JoseEncoder());
try {
$token = $parser->parse(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.'
. 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.'
. '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q'
);
} catch (CannotDecodeContent | InvalidTokenStructure | UnsupportedHeaderFound $e) {
echo 'Oh no, an error: ' . $e->getMessage();
}
assert($token instanceof UnencryptedToken);
echo $token->claims()->get('sub'), PHP_EOL; // will print "1234567890"
```
!!! Note
Some systems make use of components to handle dependency injection.
If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library.
================================================
FILE: docs/quick-start.md
================================================
# Quick start
Once the library has been [installed](installation.md), you are able to issue and parse JWTs.
The class `Lcobucci\JWT\JwtFacade` is the quickest way to perform these operations.
Using that facade we also aim to make sure that every token is properly signed and has the recommended claims for date control.
## Issuing tokens
The method `Lcobucci\JWT\JwtFacade#issue()` is available for quickly creating tokens.
It uses the current time to generate the date claims (default expiration is **5 minutes**).
To issue a token, call the method passing: an algorithm, a key, and a customisation function:
```php
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use function var_dump;
$key = InMemory::base64Encoded(
'hiG8DlOKvtih6AxlZn5XKImZ06yu8I3mkOzaJrEuW8yAv8Jnkw330uMt8AEqQ5LB'
);
$token = (new JwtFacade())->issue(
new Sha256(),
$key,
static fn (
Builder $builder,
DateTimeImmutable $issuedAt
): Builder => $builder
->issuedBy('https://api.my-awesome-app.io')
->permittedFor('https://client-app.io')
->expiresAt($issuedAt->modify('+10 minutes'))
);
var_dump($token->claims()->all());
echo $token->toString();
```
### Creating tokens during tests
To reduce the chance of having flaky tests on your test suite, the facade supports the usage of a clock object.
That allows passing an implementation that always returns the same point in time.
You can achieve that by specifying the `clock` constructor parameter:
```php
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock; // If you prefer, other PSR-20 implementations may also be used
// (https://packagist.org/providers/psr/clock-implementation)
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token\RegisteredClaims;
$clock = new FrozenClock(new DateTimeImmutable('2022-06-24 22:51:10'));
$key = InMemory::base64Encoded(
'hiG8DlOKvtih6AxlZn5XKImZ06yu8I3mkOzaJrEuW8yAv8Jnkw330uMt8AEqQ5LB'
);
$token = (new JwtFacade(null, $clock))->issue(
new Sha256(),
$key,
static fn (
Builder $builder,
DateTimeImmutable $issuedAt
): Builder => $builder
);
echo $token->claims()->get(
RegisteredClaims::ISSUED_AT
)->format(DateTimeImmutable::RFC3339); // 2022-06-24 22:51:10
```
## Parsing tokens
The method `Lcobucci\JWT\JwtFacade#parse()` is the one for quickly parsing tokens.
It also verifies the signature and date claims, throwing an exception in case of tokens in unexpected state.
```php
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock; // If you prefer, other PSR-20 implementations may also be used
// (https://packagist.org/providers/psr/clock-implementation)
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint;
use function var_dump;
$jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NTg2OTYwNTIsIm5iZiI6MT'
. 'Y1ODY5NjA1MiwiZXhwIjoxNjU4Njk2NjUyLCJpc3MiOiJodHRwczovL2FwaS5teS1hd2Vzb'
. '21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwcC5pbyJ9.yzxpjyq8lXqMgaN'
. 'rMEOLUr7R0brvhwXx0gp56uWEIfc';
$key = InMemory::base64Encoded(
'hiG8DlOKvtih6AxlZn5XKImZ06yu8I3mkOzaJrEuW8yAv8Jnkw330uMt8AEqQ5LB'
);
$token = (new JwtFacade())->parse(
$jwt,
new Constraint\SignedWith(new Sha256(), $key),
new Constraint\StrictValidAt(
new FrozenClock(new DateTimeImmutable('2022-07-24 20:55:10+00:00'))
)
);
var_dump($token->claims()->all());
```
!!! Warning
The example above uses `FrozenClock` as clock implementation to make sure that code will always work.
Use `SystemClock` on the production code of your application, allowing the parser to correctly verify the date claims.
================================================
FILE: docs/rotating-keys.md
================================================
# Rotating Keys
Key rotation consists in retiring and replacing cryptographic keys with new ones.
Performing that operation on a regular basis is an industry standard.
## Why should I rotate my keys?
Rotating keys allows us to:
1. Limit the number of tokens signed with the same key, helping the prevention of attacks enabled by cryptanalysis
2. Adopt other algorithms or stronger keys
3. Limit the impact of eventual compromised keys
## The challenges
After rotating keys, apps will likely receive requests with tokens issued with the previous key.
If the key rotation of an app is done with a "hard cut", requests with non-expired tokens issued with the old key **will fail**!
Imagine if you were the user who logged in just before a key rotation on that kind of app, you'd probably have to log in again!
That's rather frustrating, right!?
## Preventing issues
It's possible to handle key rotation in a smoother way by leveraging the `SignedWithOneInSet` validation constraint!
Say your application uses the symmetric algorithm `HS256` with a not so secure key to issue tokens:
```php
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
// `FrozenClock` is used here to fix to a point in time that allows our validation to pass
$clock = new FrozenClock(new DateTimeImmutable('2023-11-04 21:06:01+00:00'));
$token = (new JwtFacade(clock: $clock))->issue(
new Signer\Hmac\Sha256(),
InMemory::plainText(
'a-very-long-and-secure-key-that-should-actually-be-something-else'
),
static fn (Builder $builder): Builder => $builder
->issuedBy('https://api.my-awesome-app.io')
->permittedFor('https://client-app.io')
);
```
!!! Sample
Here's a token issued with the code above, if you want to test the script locally:
<details>
<summary>Sample token</summary>
// line breaks added for readability
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJpYXQiOjE2OTkxMzE5NjEsIm5iZiI6MTY5OTEzMTk2MSwiZXhwIjoxNjk5MTMyMjYxLCJpc3MiOiJ
odHRwczovL2FwaS5teS1hd2Vzb21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwcC5pbyJ9
.IA9S0n8Q2O97lyR8KczVE8g-hxbbH6_TfJS-JWTQR4c
</details>
Your parsing logic (with validations) look like:
```php
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint;
// `FrozenClock` is used here to fix to a point in time that allows our
// validation to pass
$clock = new FrozenClock(new DateTimeImmutable('2023-11-04 21:06:35+00:00'))
$validationConstraints = [
new Constraint\SignedWith(
new Signer\Hmac\Sha256(),
InMemory::plainText(
'a-very-long-and-secure-key-that-should-actually-be-something-else'
),
),
new Constraint\StrictValidAt($clock),
];
$jwt = ''; // Fetched from, for example, a request header
$token = (new JwtFacade())->parse($jwt, ...$validationConstraints);
```
### Performing a backwards compatible rotation
Now Imagine that you want to adopt the new `BLAKE2B` symmetric algorithm.
These are the changes to your issuing logic:
```diff
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
// `FrozenClock` is used here to fix to a point in time that allows our validation to pass
$clock = new FrozenClock(new DateTimeImmutable('2023-11-04 21:06:01+00:00'));
$token = (new JwtFacade(clock: $clock))->issue(
- new Signer\Hmac\Sha256(),
+ new Signer\Blake2b(),
- InMemory::plainText(
- 'a-very-long-and-secure-key-that-should-actually-be-something-else'
+ InMemory::base64Encoded(
+ 'GOu4rLyVCBxmxP+sbniU68ojAja5PkRdvv7vNvBCqDQ='
),
static fn (Builder $builder): Builder => $builder
->issuedBy('https://api.my-awesome-app.io')
->permittedFor('https://client-app.io')
);
```
!!! Sample
Here's a token issued with the code above, if you want to test the script locally:
<details>
<summary>Sample token</summary>
// line breaks added for readability
eyJ0eXAiOiJKV1QiLCJhbGciOiJCTEFLRTJCIn0
.eyJpYXQiOjE2OTkxMzE5NjEsIm5iZiI6MTY5OTEzMTk2MSwiZXhwIjoxNjk5MTMyMjYxLCJpc3Mi
OiJodHRwczovL2FwaS5teS1hd2Vzb21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwc
C5pbyJ9.bD67s8IXpAJiBTIZn1et_M5WSS7kfmuNiacNRz5lArQ
</details>
So far, nothing different that a normal rotation.
Now check the changes on the parsing and validation logic:
```diff
<?php
declare(strict_types=1);
namespace MyApp;
require 'vendor/autoload.php';
use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint;
// `FrozenClock` is used here to fix to a point in time that allows our
// validation to pass
$clock = new FrozenClock(new DateTimeImmutable('2023-11-04 21:06:35+00:00'));
$validationConstraints = [
- new Constraint\SignedWith(
- new Signer\Hmac\Sha256(),
- InMemory::plainText(
- 'a-very-long-and-secure-key-that-should-actually-be-something-else'
- ),
- ),
+ new Constraint\SignedWithOneInSet(
+ new Constraint\SignedWithUntilDate(
+ new Signer\Blake2b(),
+ InMemory::base64Encoded(
+ 'GOu4rLyVCBxmxP+sbniU68ojAja5PkRdvv7vNvBCqDQ='
+ ),
+ new DateTimeImmutable('2025-12-31 23:59:59+00:00'),
+ $clock,
+ ),
+ new Constraint\SignedWithUntilDate(
+ new Signer\Hmac\Sha256(),
+ InMemory::plainText(
+ 'a-very-long-and-secure-key-that-should-actually-be-something-else'
+ ),
+ new DateTimeImmutable('2023-12-31 23:59:59+00:00'),
+ $clock,
+ ),
+ ),
new Constraint\StrictValidAt($clock),
];
$jwt = ''; // Fetched from, for example, a request header
$token = (new JwtFacade())->parse($jwt, ...$validationConstraints);
```
Now the application is able to accept non-expired tokens issued with either old and new keys!
In this case, the old key would automatically only be accepted until `2023-12-31 23:59:59+00:00`, even if engineers forget to remove it from the list.
!!! Important
The order of `SignedWithUntilDate` constraints given to `SignedWithOneInSet` does matter, and it's recommended to leave older keys at the end of the list.
================================================
FILE: docs/supported-algorithms.md
================================================
# Supported Algorithms
This library supports signing and verifying tokens with both symmetric and asymmetric algorithms.
Encryption is not yet supported.
Each algorithm will produce signature with different length.
If you have constraints regarding the length of the issued tokens, choose the algorithms that generate shorter output (`HS256`, `RS256`, `ES256`, and `BLAKE2B`).
## Symmetric algorithms
Symmetric algorithms perform signature creation and verification using the very same key/secret.
They're usually recommended for scenarios where these operations are handled by the very same component.
| Name | Description | Class | Key length req. |
|-----------|--------------------|------------------------------------|-----------------|
| `HS256` | HMAC using SHA-256 | `\Lcobucci\JWT\Signer\Hmac\Sha256` | `>= 256 bits` |
| `HS384` | HMAC using SHA-384 | `\Lcobucci\JWT\Signer\Hmac\Sha384` | `>= 384 bits` |
| `HS512` | HMAC using SHA-512 | `\Lcobucci\JWT\Signer\Hmac\Sha512` | `>= 512 bits` |
| `BLAKE2B` | Blake2b keyed Hash | `\Lcobucci\JWT\Signer\Blake2b` | `>= 256 bits` |
!!! Warning
Although `BLAKE2B` is fantastic due to its performance, it's not [JWT standard] and won't necessarily be offered by other libraries.
## Asymmetric algorithms
Asymmetric algorithms perform signature creation with private/secret keys and verification with public keys.
They're usually recommended for scenarios where creation is handled by a component and verification by many others.
| Name | Description | Class | Key length req. |
|---------|---------------------------------|-------------------------------------|-----------------|
| `ES256` | ECDSA using P-256 and SHA-256 | `\Lcobucci\JWT\Signer\Ecdsa\Sha256` | `== 256 bits` |
| `ES384` | ECDSA using P-384 and SHA-384 | `\Lcobucci\JWT\Signer\Ecdsa\Sha384` | `== 384 bits` |
| `ES512` | ECDSA using P-521 and SHA-512 | `\Lcobucci\JWT\Signer\Ecdsa\Sha512` | `== 521 bits` |
| `RS256` | RSASSA-PKCS1-v1_5 using SHA-256 | `\Lcobucci\JWT\Signer\Rsa\Sha256` | `>= 2048 bits` |
| `RS384` | RSASSA-PKCS1-v1_5 using SHA-384 | `\Lcobucci\JWT\Signer\Rsa\Sha384` | `>= 2048 bits` |
| `RS512` | RSASSA-PKCS1-v1_5 using SHA-512 | `\Lcobucci\JWT\Signer\Rsa\Sha512` | `>= 2048 bits` |
| `EdDSA` | EdDSA signature algorithms | `\Lcobucci\JWT\Signer\Eddsa` | `>= 256 bits` |
The following algorithms are implemented in a separate package `lcobucci/jwt-rsassa-pss` in order to keep dependencies low in the main package.
Please see the installation instructions in the [RSASSA-PSS readme].
| Name | Description | Class | Key length req. |
|---------|---------------------------------|--------------------------------------|-----------------|
| `PS256` | RSASSA-PSS using SHA-256 | `\Lcobucci\JWT\Signer\RsaPss\Sha256` | `>= 2048 bits` |
| `PS384` | RSASSA-PSS using SHA-384 | `\Lcobucci\JWT\Signer\RsaPss\Sha384` | `>= 2048 bits` |
| `PS512` | RSASSA-PSS using SHA-512 | `\Lcobucci\JWT\Signer\RsaPss\Sha512` | `>= 2048 bits` |
## `none` algorithm
The `none` algorithm as described by [JWT standard] is intentionally not implemented and not supported.
The risk of misusing it is too high, and even where other means guarantee the token validity a symmetric algorithm
shouldn't represent a computational bottleneck with modern hardware.
[JWT standard]: https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms
[RSASSA-PSS readme]: https://github.com/lcobucci/jwt-rsassa-pss
================================================
FILE: docs/upgrading.md
================================================
# Upgrading steps
Here we'll keep a list of all steps you need to take to make sure your code is compatible with newer versions.
## v4.x to v5.x
The release `v5.0.0` is a modernised version of the library, which requires PHP 8.1+ and drops all the deprecated components.
We're adding a few deprecation annotations on the version `v4.3.0`.
So, before going to `v5.0.0` please update to the latest 4.3.x version using composer:
```sh
composer require lcobucci/jwt ^4.3
```
Then run your tests and change all calls to deprecated methods, even if they are not triggering any notices.
Tools like [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules) can help finding them.
### Removal of `Ecdsa::create()`
To promote symmetry on the instantiation of all algorithms (signers), we're dropping the named constructor in favour of the constructor.
If you are using any variant of ECDSA, please change your code following this example:
```diff
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
$configuration = Configuration::forAsymmetricSigner(
- Signer\Ecdsa\Sha256::create(),
+ new Signer\Ecdsa\Sha256(),
InMemory::file(__DIR__ . '/my-private-key.pem'),
InMemory::base64Encoded('mBC5v1sOKVvbdEitdSBenu59nfNfhwkedkJVNabosTw=')
// You may also override the JOSE encoder/decoder if needed
// by providing extra arguments here
);
```
### Removal of `none` algorithm
To promote a more secure usage of the library and prevent misuse we decided to deviate from the RFC and drop `none`, which means that the following components are being removed:
* `Lcobucci\JWT\Configuration::forUnsecuredSigner()`
* `Lcobucci\JWT\Signer\Key\InMemory::empty()`
* `Lcobucci\JWT\Signer\None`
* `Lcobucci\JWT\Token\Signature::fromEmptyData()`
If you're relying on it and still want to have that on your system, please create your own implementation.
If you're using it because it's "fast", please look into adoption the non-standard Blake2b implementation:
```diff
<?php
declare(strict_types=1);
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
require 'vendor/autoload.php';
-$configuration = Configuration::forUnsecuredSigner();
+$configuration = Configuration::forSymmetricSigner(
+ new Signer\Blake2b(),
+ InMemory::base64Encoded('MpQd6dDPiqnzFSWmpUfLy4+Rdls90Ca4C8e0QD0IxqY=')
+);
```
### `Builder` API is now `@immutable`
`\Lcobucci\JWT\Builder` interface alongside with its default implementation `\Lcobucci\JWT\Token\Builder` are now marked `@immutable`.
If you are using it for example with the `JwtFacade` ensure now to use the returned new `Builder` instance:
```diff
$token = (new JwtFacade())->issue(
new Sha256(),
$key,
static function (
Builder $builder,
DateTimeImmutable $now
): Builder {
- $builder->issuedBy('https://api.my-awesome-app.io');
- $builder->permittedFor('https://client-app.io');
- $builder->expiresAt($now->modify('+10 minutes'));
+ $builder = $builder->issuedBy('https://api.my-awesome-app.io');
+ $builder = $builder->permittedFor('https://client-app.io');
+ $builder = $builder->expiresAt($now->modify('+10 minutes'));
return $builder;
}
);
```
Or:
```diff
$token = (new JwtFacade())->issue(
new Sha256(),
$key,
- static function (
+ static fn (
Builder $builder,
DateTimeImmutable $now
- ): Builder {
- $builder->issuedBy('https://api.my-awesome-app.io');
- $builder->permittedFor('https://client-app.io');
- $builder->expiresAt($now->modify('+10 minutes'));
-
- return $builder;
- }
+ ): Builder => $builder->issuedBy('https://api.my-awesome-app.io')
+ ->permittedFor('https://client-app.io')
+ ->expiresAt($now->modify('+10 minutes'))
);
```
### `lcobucci/clock` is not installed by default anymore
Thanks to [PSR-20](https://www.php-fig.org/psr/psr-20/), users can more easily plug-in other [clock implementations](https://packagist.org/providers/psr/clock-implementation) if they choose to do so.
If you like and were already using `lcobucci/clock` on your system, you're required to explicitly add it as a production dependency:
```sh
composer require lcobucci/clock
```
## v3.x to v4.x
The `v4.0.0` aggregates about 5 years of work and contains **several BC-breaks**.
We're building on the version `v3.4.0` a forward compatibility layer to help users to migrate to `v4.0.0`.
To help on the migration process, all deprecated components are being marked with `@deprecated` and deprecated behaviour will trigger a `E_USER_DEPRECATED` error.
However, you can also find here the instructions on how to make your code compatible with both versions.
### General migration strategy
Update your existing software to the latest 3.4.x version using composer:
```sh
composer require lcobucci/jwt ^3.4
```
Then run your tests and fix all deprecation notices.
Also change all calls to deprecated methods, even if they are not triggering any notices.
Tools like [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules) can help finding them.
Note that PHPUnit tests will only fail if you have the `E_USER_DEPRECATED` error level activated - it is a good practice to run tests using `E_ALL`.
Data providers that trigger deprecation messages will not fail tests at all, only print the message to the console.
Make sure you do not see any of them before you continue.
Now you can upgrade to the latest 4.x version:
```sh
composer require lcobucci/jwt ^4.0
```
Remember that some deprecation messages from the 3.4 version may have notified you that things still are different in 4.0, so you may find you need to adapt your own code once more at this stage.
While you are at it, consider using the new configuration object.
The details are listed below.
### Inject the configuration object instead of builder/parser/key
This object serves as a small service locator that centralises the JWT-related dependencies.
The main goal is to simplify the injection of our components into downstream code.
This step is quite important to at least have a single way to initialise the JWT components, even if the configuration object is thrown away.
Check an example of how to migrate the injection of builder+signer+key to configuration object below:
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
-use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Configuration;
-use Lcobucci\JWT\Signer;
-use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token;
use function bin2hex;
use function random_bytes;
final class JwtIssuer
{
- private Builder $builder;
- private Signer $signer;
- private Key $key;
-
- public function __construct(Builder $builder, Signer $signer, Key $key)
- {
- $this->builder = $builder;
- $this->signer = $signer;
- $this->key = $key;
- }
+ private Configuration $config;
+
+ public function __construct(Configuration $config)
+ {
+ $this->config = $config;
+ }
public function issueToken(): Token
{
- return $this->builder
+ return $this->config->builder()
->identifiedBy(bin2hex(random_bytes(16)))
- ->getToken($this->signer, $this->key);
+ ->getToken($this->config->signer(), $this->config->signingKey());
}
}
```
You can find more information on how to use the configuration object, [here](configuration.md).
### Use new `Key` objects
`Lcobucci\JWT\Signer\Key` has been converted to an interface in `v4.0`.
We provide `Lcobucci\JWT\Signer\Key\InMemory`, a drop-in replacement of the behaviour for `Lcobucci\JWT\Signer\Key` in `v3.x`.
You will need to pick the appropriated named constructor to migrate your code:
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
-use Lcobucci\JWT\Signer\Key;
+use Lcobucci\JWT\Signer\Key\InMemory;
-
-use function base64_decode;
// Key from plain text
-$key = new Key('a-very-secure-key');
+$key = InMemory::plainText('a-very-secure-key');
// Key from base64 encoded string
-$key = new Key(base64_decode('YS12ZXJ5LXNlY3VyZS1rZXk=', true));
+$key = InMemory::base64Encoded('YS12ZXJ5LXNlY3VyZS1rZXk=');
// Key from file contents
-$key = new Key('file:///var/secrets/my-private-key.pem');
+$key = InMemory::file('/var/secrets/my-private-key.pem');
```
### Use the new `Builder` API
There are 4 main differences on the new API:
1. Token configuration methods were renamed
1. Signature is created via `Builder#getToken()` (instead of `Builder#sign()`)
1. `DateTimeImmutable` objects are now used for the registered claims with dates, which are by default encoded as floats with microseconds as precision
1. Headers should be replicated manually - whenever necessary
Here's the migration:
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
+use DateTimeImmutable;
-use Lcobucci\JWT\Builder;
+use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
-
-use function time;
-$now = time();
+$now = new DateTimeImmutable();
+$config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText('testing'));
-$token = (new Builder())
+$token = $config->builder()
- ->setIssuer('http://example.com', true)
+ ->issuedBy('http://example.com')
+ ->withHeader('iss', 'http://example.com')
- ->setAudience('http://example.org')
+ ->permittedFor('http://example.org')
- ->setId('4f1g23a12aa')
+ ->identifiedBy('4f1g23a12aa')
- ->setSubject('user123')
+ ->relatedTo('user123')
- ->setIssuedAt($now)
+ ->issuedAt($now)
- ->setNotBefore($now + 60)
+ ->canOnlyBeUsedAfter($now->modify('+1 minute'))
- ->setExpiration($now + 3600)
+ ->expiresAt($now->modify('+1 hour'))
- ->set('uid', 1)
+ ->withClaim('uid', 1)
- ->sign(new Sha256(), 'testing')
- ->getToken();
+ ->getToken($config->signer(), $config->signingKey());
```
#### Date precision
If you want to continue using Unix timestamps, you can use the `withUnixTimestampDates()`-formatter:
```diff
<?php
-$builder = new Builder());
+$builder = $config->builder(ChainedFormatter::withUnixTimestampDates());
```
#### Support for multiple audiences
Even though we didn't officially support multiple audiences, it was technically possible to achieve that by manually setting the `aud` claim to an array with multiple strings.
If you parse a token with 3.4, and read its contents with `\Lcobucci\JWT\Token#getClaim()` or`\Lcobucci\JWT\Token#getClaims()`, you will only get the first element of such an array back.
If the audience claim does only contain a string, or only contains one string in the array, nothing changes.
Please [upgrade to the new Token API](#use-the-new-token-api) for accessing claims in order to get the full audience array again (e.g. call `Token#claims()->get('aud')`).
When creating a token, use the new method `Builder#permittedFor()` as detailed below.
##### Multiple calls to `Builder#permittedFor()`
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
-use Lcobucci\JWT\Builder;
+use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
+$config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText('testing'));
-$token = (new Builder())
+$token = $config->builder()
- ->withClaim('aud', ['one', 'two', 'three'])
+ ->permittedFor('one')
+ ->permittedFor('two')
+ ->permittedFor('three')
- ->sign(new Sha256(), 'testing')
- ->getToken();
+ ->getToken($config->signer(), $config->signingKey());
```
##### Single call to `Builder#permittedFor()` with multiple arguments
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
-use Lcobucci\JWT\Builder;
+use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
+$config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText('testing'));
-$token = (new Builder())
+$token = $config->builder()
- ->withClaim('aud', ['one', 'two', 'three'])
+ ->permittedFor('one', 'two', 'three')
- ->sign(new Sha256(), 'testing')
- ->getToken();
+ ->getToken($config->signer(), $config->signingKey());
```
##### Single call to `Builder#permittedFor()` with argument unpacking
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
-use Lcobucci\JWT\Builder;
+use Lcobucci\JWT\Configuration;
+use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Hmac\Sha256;
+$config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText('testing'));
-$token = (new Builder())
+$token = $config->builder()
- ->withClaim('aud', ['one', 'two', 'three'])
+ ->permittedFor(...['one', 'two', 'three'])
- ->sign(new Sha256(), 'testing')
- ->getToken();
+ ->getToken($config->signer(), $config->signingKey());
```
### Replace `Token#verify()` and `Token#validate()` with Validation API
These methods were quite limited and brings multiple responsibilities to the `Token` class.
On `v4.0` we provide another component to validate tokens, including their signature.
Here's an example of how to modify that logic (considering [constraints have been configured](configuration.md#customisation)):
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
use InvalidArgumentException;
+use Lcobucci\JWT\Configuration;
-use Lcobucci\JWT\Signer;
-use Lcobucci\JWT\Signer\Key;
-use Lcobucci\JWT\Parser;
-use Lcobucci\JWT\ValidationData;
final class AuthenticateJwt
{
- private Parser $parser;
- private Signer $signer;
- private Key $key;
+ private Configuration $config;
- public function __construct(Parser $parser, Signer $signer, Key $key)
+ public function __construct(Configuration $config)
{
- $this->parser = $parser;
- $this->signer = $signer;
- $this->key = $key;
+ $this->config = $config;
}
public function authenticate(string $jwt): void
{
- $token = $this->parser->parse($jwt);
+ $token = $this->config->parser()->parse($jwt);
- if (! $token->validate(new ValidationData()) || $token->verify($this->signer, $this->key)) {
+ if (! $this->config->validator()->validate($token, ...$this->config->validationConstraints())) {
throw new InvalidArgumentException('Invalid token provided');
}
}
}
```
Check [here](validating-tokens.md) for more information on how to validate tokens and what are the built-in constraints.
### Use the new `Token` API
There some important differences on this new API:
1. We no longer use the `Lcobucci\JWT\Claim` objects
1. Headers and claims are now represented as `Lcobucci\JWT\Token\DataSet`
1. Different methods should be used to retrieve a header/claim
1. No exception is thrown when accessing missing header/claim, the default argument is always used
1. Tokens should be explicitly casted to string via method
Your code should be adapted to manipulate tokens like this:
```diff
<?php
declare(strict_types=1);
namespace Me\MyApp\Authentication;
// we assume here that $token is a valid parsed/created token
-$token->getHeaders()
+$token->headers()->all()
-$token->hasHeader('typ')
+$token->headers()->has('typ')
-$token->getHeader('typ')
+$token->headers()->get('typ')
-$token->getClaims()
+$token->claims()->all()
-$token->hasClaim('iss')
+$token->claims()->has('iss')
-$token->getClaim('iss')
+$token->claims()->get('iss')
-echo (string) $token;
+echo $token->toString();
```
================================================
FILE: docs/validating-tokens.md
================================================
# Validating tokens
To validate a token you must create a new validator and assert or validate a token.
## Using `Lcobucci\JWT\Validator#assert()`
This method goes through every single constraint in the set, groups all the violations, and throws an exception with the grouped violations:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use Lcobucci\JWT\Validation\Validator;
require 'vendor/autoload.php';
$parser = new Parser(new JoseEncoder());
$token = $parser->parse(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.'
. 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.'
. '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q'
);
$validator = new Validator();
try {
$validator->assert($token, new RelatedTo('1234567891')); // doesn't throw an exception
$validator->assert($token, new RelatedTo('1234567890'));
} catch (RequiredConstraintsViolated $e) {
// list of constraints violation exceptions:
var_dump($e->violations());
}
```
## Using `Lcobucci\JWT\Validator#validate()`
The difference here is that we'll always get a `boolean` result and stop in the very first violation:
```php
<?php
declare(strict_types=1);
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Parser;
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
use Lcobucci\JWT\Validation\Validator;
require 'vendor/autoload.php';
$parser = new Parser(new JoseEncoder());
$token = $parser->parse(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.'
. 'eyJzdWIiOiIxMjM0NTY3ODkwIn0.'
. '2gSBz9EOsQRN9I-3iSxJoFt7NtgV6Rm0IL6a8CAwl3Q'
);
$validator = new Validator();
if (! $validator->validate($token, new RelatedTo('1234567891'))) {
echo 'Invalid token (1)!', PHP_EOL; // will print this
}
if (! $validator->validate($token, new RelatedTo('1234567890'))) {
echo 'Invalid token (2)!', PHP_EOL; // will not print this
}
```
!!! Note
Some systems make use of components to handle dependency injection.
If your application follows that practice, using a [configuration object](configuration.md) might simplify the wiring of this library.
## Available constraints
This library provides the following constraints:
* `Lcobucci\JWT\Validation\Constraint\IdentifiedBy`: verifies if the claim `jti` matches the expected value
* `Lcobucci\JWT\Validation\Constraint\IssuedBy`: verifies if the claim `iss` is listed as expected values
* `Lcobucci\JWT\Validation\Constraint\PermittedFor`: verifies if the claim `aud` contains the expected value
* `Lcobucci\JWT\Validation\Constraint\RelatedTo`: verifies if the claim `sub` matches the expected value
* `Lcobucci\JWT\Validation\Constraint\SignedWith`: verifies if the token was signed with the expected signer and key
* `Lcobucci\JWT\Validation\Constraint\SignedWithOneInSet`: verifies the token signature against multiple `SignedWithUntilDate` constraints
* `Lcobucci\JWT\Validation\Constraint\SignedWithUntilDate`: verifies if the token was signed with the expected signer and key (until a certain date)
* `Lcobucci\JWT\Validation\Constraint\StrictValidAt`: verifies presence and validity of the claims `iat`, `nbf`, and `exp` (supports leeway configuration)
* `Lcobucci\JWT\Validation\Constraint\LooseValidAt`: verifies the claims `iat`, `nbf`, and `exp`, when present (supports leeway configuration)
* `Lcobucci\JWT\Validation\Constraint\HasClaimWithValue`: verifies that a **custom claim** has the expected value (not recommended when comparing cryptographic hashes)
* `Lcobucci\JWT\Validation\Constraint\HasClaim`: verifies that a **custom claim** is present
You may also create your [own validation constraints](extending-the-library.md#validation-constraints).
================================================
FILE: infection.json.dist
================================================
{
"source": {
"directories": ["src"]
},
"timeout": 3,
"logs": {
"text": "infection.txt"
},
"mutators": {
"@default": true,
"@function_signature": true,
"CastInt": {
"ignore": [
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::octetLength",
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::readAsn1Integer"
]
},
"UnwrapSubstr": {
"ignore": [
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger"
]
},
"GreaterThan": {
"ignore": [
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::toAsn1",
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger",
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::retrievePositiveInteger"
]
},
"LessThanOrEqualTo": {
"ignore": [
"Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter::preparePositiveInteger"
]
},
"LogicalNot": {
"ignoreSourceCodeByRegex": [
"if \\(! function_exists\\('sodium_\\w+'\\)\\) \\{"
]
}
},
"minMsi": 100,
"minCoveredMsi": 100
}
================================================
FILE: mkdocs.yml
================================================
site_name: lcobucci/jwt
theme: readthedocs
nav:
- Intro:
- 'index.md'
- 'installation.md'
- 'quick-start.md'
- 'supported-algorithms.md'
- Usage:
- 'issuing-tokens.md'
- 'parsing-tokens.md'
- 'validating-tokens.md'
- 'configuration.md'
- Guides:
- 'extending-the-library.md'
- 'rotating-keys.md'
- 'upgrading.md'
markdown_extensions:
- admonition
- footnotes
- toc:
permalink: true
================================================
FILE: phpbench.json
================================================
{
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "tests/Benchmark"
}
================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<ruleset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd"
>
<arg name="basepath" value="." />
<arg name="extensions" value="php" />
<arg name="parallel" value="80" />
<arg name="colors" />
<arg name="cache" value=".phpcs.cache" />
<arg value="p" />
<file>src</file>
<file>tests</file>
<rule ref="Lcobucci" />
<rule ref="SlevomatCodingStandard.Functions.UnusedParameter">
<exclude-pattern>tests</exclude-pattern>
</rule>
</ruleset>
================================================
FILE: phpstan.neon.dist
================================================
parameters:
level: 8 # not yet ready for all the `mixed` checks
paths:
- src
- tests
================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutTestsThatDoNotTestAnything="true"
requireCoverageMetadata="true"
failOnAllIssues="true"
displayDetailsOnAllIssues="true"
cacheDirectory=".phpunit.cache"
>
<testsuites>
<testsuite name="tests">
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
================================================
FILE: renovate.json
================================================
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>lcobucci/.github:renovate-config"
]
}
================================================
FILE: src/Builder.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use DateTimeImmutable;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token\RegisteredClaimGiven;
use NoDiscard;
/** @immutable */
interface Builder
{
/**
* Appends new items to audience
*
* @param non-empty-string ...$audiences
*/
#[NoDiscard]
public function permittedFor(string ...$audiences): Builder;
/**
* Configures the expiration time
*/
#[NoDiscard]
public function expiresAt(DateTimeImmutable $expiration): Builder;
/**
* Configures the token id
*
* @param non-empty-string $id
*/
#[NoDiscard]
public function identifiedBy(string $id): Builder;
/**
* Configures the time that the token was issued
*/
#[NoDiscard]
public function issuedAt(DateTimeImmutable $issuedAt): Builder;
/**
* Configures the issuer
*
* @param non-empty-string $issuer
*/
#[NoDiscard]
public function issuedBy(string $issuer): Builder;
/**
* Configures the time before which the token cannot be accepted
*/
#[NoDiscard]
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): Builder;
/**
* Configures the subject
*
* @param non-empty-string $subject
*/
#[NoDiscard]
public function relatedTo(string $subject): Builder;
/**
* Configures a header item
*
* @param non-empty-string $name
*/
#[NoDiscard]
public function withHeader(string $name, mixed $value): Builder;
/**
* Configures a claim item
*
* @param non-empty-string $name
*
* @throws RegisteredClaimGiven When trying to set a registered claim.
*/
#[NoDiscard]
public function withClaim(string $name, mixed $value): Builder;
/**
* Returns a signed token to be used
*
* @throws CannotEncodeContent When data cannot be converted to JSON.
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
#[NoDiscard]
public function getToken(Signer $signer, Key $key): UnencryptedToken;
}
================================================
FILE: src/ClaimsFormatter.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use NoDiscard;
interface ClaimsFormatter
{
/**
* @param array<non-empty-string, mixed> $claims
*
* @return array<non-empty-string, mixed>
*/
#[NoDiscard]
public function formatClaims(array $claims): array;
}
================================================
FILE: src/Configuration.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
use NoDiscard;
/**
* Configuration container for the JWT Builder and Parser
*
* Serves like a small DI container to simplify the creation and usage
* of the objects.
*/
final readonly class Configuration
{
private Parser $parser;
private Validator $validator;
/** @var Closure(ClaimsFormatter $claimFormatter): Builder */
private Closure $builderFactory;
/** @var Constraint[] */
private array $validationConstraints;
/** @param Closure(ClaimsFormatter $claimFormatter): Builder|null $builderFactory */
private function __construct(
private Signer $signer,
private Key $signingKey,
private Key $verificationKey,
private Encoder $encoder,
private Decoder $decoder,
?Parser $parser,
?Validator $validator,
?Closure $builderFactory,
Constraint ...$validationConstraints,
) {
$this->parser = $parser ?? new Token\Parser($decoder);
$this->validator = $validator ?? new Validation\Validator();
$this->builderFactory = $builderFactory
?? static function (ClaimsFormatter $claimFormatter) use ($encoder): Builder {
return Token\Builder::new($encoder, $claimFormatter);
};
$this->validationConstraints = $validationConstraints;
}
#[NoDiscard]
public static function forAsymmetricSigner(
Signer $signer,
Key $signingKey,
Key $verificationKey,
Encoder $encoder = new JoseEncoder(),
Decoder $decoder = new JoseEncoder(),
): self {
return new self(
$signer,
$signingKey,
$verificationKey,
$encoder,
$decoder,
null,
null,
null,
);
}
#[NoDiscard]
public static function forSymmetricSigner(
Signer $signer,
Key $key,
Encoder $encoder = new JoseEncoder(),
Decoder $decoder = new JoseEncoder(),
): self {
return new self(
$signer,
$key,
$key,
$encoder,
$decoder,
null,
null,
null,
);
}
/** @param callable(ClaimsFormatter): Builder $builderFactory */
#[NoDiscard]
public function withBuilderFactory(callable $builderFactory): self
{
return new self(
$this->signer,
$this->signingKey,
$this->verificationKey,
$this->encoder,
$this->decoder,
$this->parser,
$this->validator,
$builderFactory(...),
...$this->validationConstraints,
);
}
public function builder(?ClaimsFormatter $claimFormatter = null): Builder
{
return ($this->builderFactory)($claimFormatter ?? ChainedFormatter::default());
}
public function parser(): Parser
{
return $this->parser;
}
#[NoDiscard]
public function withParser(Parser $parser): self
{
return new self(
$this->signer,
$this->signingKey,
$this->verificationKey,
$this->encoder,
$this->decoder,
$parser,
$this->validator,
$this->builderFactory,
...$this->validationConstraints,
);
}
public function signer(): Signer
{
return $this->signer;
}
public function signingKey(): Key
{
return $this->signingKey;
}
public function verificationKey(): Key
{
return $this->verificationKey;
}
public function validator(): Validator
{
return $this->validator;
}
#[NoDiscard]
public function withValidator(Validator $validator): self
{
return new self(
$this->signer,
$this->signingKey,
$this->verificationKey,
$this->encoder,
$this->decoder,
$this->parser,
$validator,
$this->builderFactory,
...$this->validationConstraints,
);
}
/** @return Constraint[] */
public function validationConstraints(): array
{
return $this->validationConstraints;
}
#[NoDiscard]
public function withValidationConstraints(Constraint ...$validationConstraints): self
{
return new self(
$this->signer,
$this->signingKey,
$this->verificationKey,
$this->encoder,
$this->decoder,
$this->parser,
$this->validator,
$this->builderFactory,
...$validationConstraints,
);
}
}
================================================
FILE: src/Decoder.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use NoDiscard;
interface Decoder
{
/**
* Decodes from JSON, validating the errors
*
* @param non-empty-string $json
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
#[NoDiscard]
public function jsonDecode(string $json): mixed;
/**
* Decodes from Base64URL
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*
* @return ($data is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent When something goes wrong while decoding.
*/
#[NoDiscard]
public function base64UrlDecode(string $data): string;
}
================================================
FILE: src/Encoder.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use NoDiscard;
interface Encoder
{
/**
* Encodes to JSON, validating the errors
*
* @return non-empty-string
*
* @throws CannotEncodeContent When something goes wrong while encoding.
*/
#[NoDiscard]
public function jsonEncode(mixed $data): string;
/**
* Encodes to base64url
*
* @link http://tools.ietf.org/html/rfc4648#section-5
*
* @return ($data is non-empty-string ? non-empty-string : string)
*/
#[NoDiscard]
public function base64UrlEncode(string $data): string;
}
================================================
FILE: src/Encoding/CannotDecodeContent.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotDecodeContent extends RuntimeException implements Exception
{
public static function jsonIssues(JsonException $previous): self
{
return new self(message: 'Error while decoding from JSON', previous: $previous);
}
public static function invalidBase64String(): self
{
return new self('Error while decoding from Base64Url, invalid base64 characters detected');
}
}
================================================
FILE: src/Encoding/CannotEncodeContent.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class CannotEncodeContent extends RuntimeException implements Exception
{
public static function jsonIssues(JsonException $previous): self
{
return new self(message: 'Error while encoding to JSON', previous: $previous);
}
}
================================================
FILE: src/Encoding/ChainedFormatter.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
final readonly class ChainedFormatter implements ClaimsFormatter
{
/** @var array<ClaimsFormatter> */
private array $formatters;
public function __construct(ClaimsFormatter ...$formatters)
{
$this->formatters = $formatters;
}
public static function default(): self
{
return new self(new UnifyAudience(), new MicrosecondBasedDateConversion());
}
public static function withUnixTimestampDates(): self
{
return new self(new UnifyAudience(), new UnixTimestampDates());
}
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach ($this->formatters as $formatter) {
$claims = $formatter->formatClaims($claims);
}
return $claims;
}
}
================================================
FILE: src/Encoding/JoseEncoder.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use JsonException;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\SodiumBase64Polyfill;
use function json_decode;
use function json_encode;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;
/**
* A utilitarian class that encodes and decodes data according to JOSE specifications
*/
final readonly class JoseEncoder implements Encoder, Decoder
{
public function jsonEncode(mixed $data): string
{
try {
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotEncodeContent::jsonIssues($exception);
}
}
public function jsonDecode(string $json): mixed
{
try {
return json_decode(json: $json, associative: true, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw CannotDecodeContent::jsonIssues($exception);
}
}
public function base64UrlEncode(string $data): string
{
return SodiumBase64Polyfill::bin2base64(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}
public function base64UrlDecode(string $data): string
{
return SodiumBase64Polyfill::base642bin(
$data,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING,
);
}
}
================================================
FILE: src/Encoding/MicrosecondBasedDateConversion.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final readonly class MicrosecondBasedDateConversion implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
private function convertDate(DateTimeImmutable $date): int|float
{
if ($date->format('u') === '000000') {
return (int) $date->format('U');
}
return (float) $date->format('U.u');
}
}
================================================
FILE: src/Encoding/UnifyAudience.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
use function count;
use function current;
final readonly class UnifyAudience implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
if (
! array_key_exists(RegisteredClaims::AUDIENCE, $claims)
|| count($claims[RegisteredClaims::AUDIENCE]) !== 1
) {
return $claims;
}
$claims[RegisteredClaims::AUDIENCE] = current($claims[RegisteredClaims::AUDIENCE]);
return $claims;
}
}
================================================
FILE: src/Encoding/UnixTimestampDates.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Encoding;
use DateTimeImmutable;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;
use function array_key_exists;
final readonly class UnixTimestampDates implements ClaimsFormatter
{
/** @inheritdoc */
public function formatClaims(array $claims): array
{
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
private function convertDate(DateTimeImmutable $date): int
{
return $date->getTimestamp();
}
}
================================================
FILE: src/Exception.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Throwable;
interface Exception extends Throwable
{
}
================================================
FILE: src/JwtFacade.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Closure;
use DateTimeImmutable;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\SignedWith;
use Lcobucci\JWT\Validation\ValidAt;
use Lcobucci\JWT\Validation\Validator;
use NoDiscard;
use Psr\Clock\ClockInterface as Clock;
use function assert;
final readonly class JwtFacade
{
private Clock $clock;
public function __construct(
private Parser $parser = new Token\Parser(new JoseEncoder()),
?Clock $clock = null,
) {
$this->clock = $clock ?? new class implements Clock {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
};
}
/** @param Closure(Builder, DateTimeImmutable):Builder $customiseBuilder */
#[NoDiscard]
public function issue(
Signer $signer,
Key $signingKey,
Closure $customiseBuilder,
): UnencryptedToken {
$builder = Token\Builder::new(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates());
$now = $this->clock->now();
$builder = $builder
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($now->modify('+5 minutes'));
return $customiseBuilder($builder, $now)->getToken($signer, $signingKey);
}
/** @param non-empty-string $jwt */
#[NoDiscard]
public function parse(
string $jwt,
SignedWith $signedWith,
ValidAt $validAt,
Constraint ...$constraints,
): UnencryptedToken {
$token = $this->parser->parse($jwt);
assert($token instanceof UnencryptedToken);
(new Validator())->assert(
$token,
$signedWith,
$validAt,
...$constraints,
);
return $token;
}
}
================================================
FILE: src/Parser.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use Lcobucci\JWT\Token\InvalidTokenStructure;
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
use NoDiscard;
interface Parser
{
/**
* Parses the JWT and returns a token
*
* @param non-empty-string $jwt
*
* @throws CannotDecodeContent When something goes wrong while decoding.
* @throws InvalidTokenStructure When token string structure is invalid.
* @throws UnsupportedHeaderFound When parsed token has an unsupported header.
*/
#[NoDiscard]
public function parse(string $jwt): Token;
}
================================================
FILE: src/Signer/Blake2b.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use function hash_equals;
use function sodium_crypto_generichash;
use function strlen;
final readonly class Blake2b implements Signer
{
private const int MINIMUM_KEY_LENGTH_IN_BITS = 256;
public function algorithmId(): string
{
return 'BLAKE2B';
}
public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());
if ($actualKeyLength < self::MINIMUM_KEY_LENGTH_IN_BITS) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH_IN_BITS, $actualKeyLength);
}
return sodium_crypto_generichash($payload, $key->contents());
}
public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
}
================================================
FILE: src/Signer/CannotSignPayload.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotSignPayload extends InvalidArgumentException implements Exception
{
public static function errorHappened(string $error): self
{
return new self('There was an error while creating the signature:' . $error);
}
}
================================================
FILE: src/Signer/Ecdsa/ConversionFailed.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class ConversionFailed extends InvalidArgumentException implements Exception
{
public static function invalidLength(): self
{
return new self('Invalid signature length.');
}
public static function incorrectStartSequence(): self
{
return new self('Invalid data. Should start with a sequence.');
}
public static function integerExpected(): self
{
return new self('Invalid data. Should contain an integer.');
}
}
================================================
FILE: src/Signer/Ecdsa/MultibyteStringConverter.php
================================================
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2018 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*
* @link https://github.com/web-token/jwt-framework/blob/v1.2/src/Component/Core/Util/ECSignature.php
*/
namespace Lcobucci\JWT\Signer\Ecdsa;
use function assert;
use function bin2hex;
use function dechex;
use function hex2bin;
use function hexdec;
use function is_string;
use function str_pad;
use function strlen;
use function substr;
use const STR_PAD_LEFT;
/**
* ECDSA signature converter using ext-mbstring
*
* @internal
*/
final readonly class MultibyteStringConverter implements SignatureConverter
{
private const string ASN1_SEQUENCE = '30';
private const string ASN1_INTEGER = '02';
private const int ASN1_MAX_SINGLE_BYTE = 128;
private const string ASN1_LENGTH_2BYTES = '81';
private const string ASN1_BIG_INTEGER_LIMIT = '7f';
private const string ASN1_NEGATIVE_INTEGER = '00';
private const int BYTE_SIZE = 2;
public function toAsn1(string $points, int $length): string
{
$points = bin2hex($points);
if (self::octetLength($points) !== $length) {
throw ConversionFailed::invalidLength();
}
$pointR = self::preparePositiveInteger(substr($points, 0, $length));
$pointS = self::preparePositiveInteger(substr($points, $length, null));
$lengthR = self::octetLength($pointR);
$lengthS = self::octetLength($pointS);
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
$asn1 = hex2bin(
self::ASN1_SEQUENCE
. $lengthPrefix . dechex($totalLength)
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
. self::ASN1_INTEGER . dechex($lengthS) . $pointS,
);
assert(is_string($asn1));
assert($asn1 !== '');
return $asn1;
}
private static function octetLength(string $data): int
{
return (int) (strlen($data) / self::BYTE_SIZE);
}
private static function preparePositiveInteger(string $data): string
{
if (substr($data, 0, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT) {
return self::ASN1_NEGATIVE_INTEGER . $data;
}
while (
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
&& substr($data, 2, self::BYTE_SIZE) <= self::ASN1_BIG_INTEGER_LIMIT
) {
$data = substr($data, 2, null);
}
return $data;
}
public function fromAsn1(string $signature, int $length): string
{
$message = bin2hex($signature);
$position = 0;
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_SEQUENCE) {
throw ConversionFailed::incorrectStartSequence();
}
// @phpstan-ignore-next-line
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
$position += self::BYTE_SIZE;
}
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
$points = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT) . str_pad($pointS, $length, '0', STR_PAD_LEFT));
assert(is_string($points));
assert($points !== '');
return $points;
}
private static function readAsn1Content(string $message, int &$position, int $length): string
{
$content = substr($message, $position, $length);
$position += $length;
return $content;
}
private static function readAsn1Integer(string $message, int &$position): string
{
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
throw ConversionFailed::integerExpected();
}
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
}
private static function retrievePositiveInteger(string $data): string
{
while (
substr($data, 0, self::BYTE_SIZE) === self::ASN1_NEGATIVE_INTEGER
&& substr($data, 2, self::BYTE_SIZE) > self::ASN1_BIG_INTEGER_LIMIT
) {
$data = substr($data, 2, null);
}
return $data;
}
}
================================================
FILE: src/Signer/Ecdsa/Sha256.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA256;
final readonly class Sha256 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
public function pointLength(): int
{
return 64;
}
public function expectedKeyLength(): int
{
return 256;
}
}
================================================
FILE: src/Signer/Ecdsa/Sha384.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA384;
final readonly class Sha384 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
public function pointLength(): int
{
return 96;
}
public function expectedKeyLength(): int
{
return 384;
}
}
================================================
FILE: src/Signer/Ecdsa/Sha512.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa;
use const OPENSSL_ALGO_SHA512;
final readonly class Sha512 extends Ecdsa
{
public function algorithmId(): string
{
return 'ES512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
public function pointLength(): int
{
return 132;
}
public function expectedKeyLength(): int
{
// ES512 means ECDSA using P-521 and SHA-512.
// The key size is indeed 521 bits.
return 521;
}
}
================================================
FILE: src/Signer/Ecdsa/SignatureConverter.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Ecdsa;
/**
* Manipulates the result of a ECDSA signature (points R and S) according to the
* JWA specs.
*
* OpenSSL creates a signature using the ASN.1 format and, according the JWA specs,
* the signature for JWTs must be the concatenated values of points R and S (in
* big-endian octet order).
*
* @internal
*
* @see https://tools.ietf.org/html/rfc7518#page-9
* @see https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
*/
interface SignatureConverter
{
/**
* Converts the signature generated by OpenSSL into what JWA defines
*
* @return non-empty-string
*
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function fromAsn1(string $signature, int $length): string;
/**
* Converts the JWA signature into something OpenSSL understands
*
* @param non-empty-string $points
*
* @return non-empty-string
*
* @throws ConversionFailed When there was an issue during the format conversion.
*/
public function toAsn1(string $points, int $length): string;
}
================================================
FILE: src/Signer/Ecdsa.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Ecdsa\MultibyteStringConverter;
use Lcobucci\JWT\Signer\Ecdsa\SignatureConverter;
use const OPENSSL_KEYTYPE_EC;
abstract readonly class Ecdsa extends OpenSSL
{
public function __construct(
private SignatureConverter $converter = new MultibyteStringConverter(),
) {
}
final public function sign(string $payload, Key $key): string
{
return $this->converter->fromAsn1(
$this->createSignature($key, $payload),
$this->pointLength(),
);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature(
$this->converter->toAsn1($expected, $this->pointLength()),
$payload,
$key,
);
}
/** {@inheritDoc} */
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_EC) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_EC],
self::KEY_TYPE_MAP[$type] ?? 'unknown',
);
}
$expectedKeyLength = $this->expectedKeyLength();
if ($lengthInBits !== $expectedKeyLength) {
throw InvalidKeyProvided::incompatibleKeyLength($expectedKeyLength, $lengthInBits);
}
}
/**
* @internal
*
* @return positive-int
*/
abstract public function expectedKeyLength(): int;
/**
* Returns the length of each point in the signature, so that we can calculate and verify R and S points properly
*
* @internal
*
* @return positive-int
*/
abstract public function pointLength(): int;
}
================================================
FILE: src/Signer/Eddsa.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use SodiumException;
use function sodium_crypto_sign_detached;
use function sodium_crypto_sign_verify_detached;
final readonly class Eddsa implements Signer
{
public function algorithmId(): string
{
return 'EdDSA';
}
public function sign(string $payload, Key $key): string
{
try {
return sodium_crypto_sign_detached($payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
public function verify(string $expected, string $payload, Key $key): bool
{
try {
return sodium_crypto_sign_verify_detached($expected, $payload, $key->contents());
} catch (SodiumException $sodiumException) {
throw new InvalidKeyProvided($sodiumException->getMessage(), 0, $sodiumException);
}
}
}
================================================
FILE: src/Signer/Hmac/Sha256.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final readonly class Sha256 extends Hmac
{
public function algorithmId(): string
{
return 'HS256';
}
public function algorithm(): string
{
return 'sha256';
}
public function minimumBitsLengthForKey(): int
{
return 256;
}
}
================================================
FILE: src/Signer/Hmac/Sha384.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final readonly class Sha384 extends Hmac
{
public function algorithmId(): string
{
return 'HS384';
}
public function algorithm(): string
{
return 'sha384';
}
public function minimumBitsLengthForKey(): int
{
return 384;
}
}
================================================
FILE: src/Signer/Hmac/Sha512.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Hmac;
use Lcobucci\JWT\Signer\Hmac;
final readonly class Sha512 extends Hmac
{
public function algorithmId(): string
{
return 'HS512';
}
public function algorithm(): string
{
return 'sha512';
}
public function minimumBitsLengthForKey(): int
{
return 512;
}
}
================================================
FILE: src/Signer/Hmac.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use function hash_equals;
use function hash_hmac;
use function strlen;
abstract readonly class Hmac implements Signer
{
final public function sign(string $payload, Key $key): string
{
$actualKeyLength = 8 * strlen($key->contents());
$expectedKeyLength = $this->minimumBitsLengthForKey();
if ($actualKeyLength < $expectedKeyLength) {
throw InvalidKeyProvided::tooShort($expectedKeyLength, $actualKeyLength);
}
return hash_hmac($this->algorithm(), $payload, $key->contents(), true);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return hash_equals($expected, $this->sign($payload, $key));
}
/**
* @internal
*
* @return non-empty-string
*/
abstract public function algorithm(): string;
/**
* @internal
*
* @return positive-int
*/
abstract public function minimumBitsLengthForKey(): int;
}
================================================
FILE: src/Signer/InvalidKeyProvided.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidKeyProvided extends InvalidArgumentException implements Exception
{
public static function cannotBeParsed(string $details): self
{
return new self('It was not possible to parse your key, reason:' . $details);
}
/**
* @param non-empty-string $expectedType
* @param non-empty-string $actualType
*/
public static function incompatibleKeyType(string $expectedType, string $actualType): self
{
return new self(
'The type of the provided key is not "' . $expectedType
. '", "' . $actualType . '" provided',
);
}
/** @param positive-int $expectedLength */
public static function incompatibleKeyLength(int $expectedLength, int $actualLength): self
{
return new self(
'The length of the provided key is different than ' . $expectedLength . ' bits, '
. $actualLength . ' bits provided',
);
}
public static function cannotBeEmpty(): self
{
return new self('Key cannot be empty');
}
public static function tooShort(int $expectedLength, int $actualLength): self
{
return new self('Key provided is shorter than ' . $expectedLength . ' bits,'
. ' only ' . $actualLength . ' bits provided');
}
}
================================================
FILE: src/Signer/Key/FileCouldNotBeRead.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
use Throwable;
final class FileCouldNotBeRead extends InvalidArgumentException implements Exception
{
/** @param non-empty-string $path */
public static function onPath(string $path, ?Throwable $cause = null): self
{
return new self(
message: 'The path "' . $path . '" does not contain a valid key file',
previous: $cause,
);
}
}
================================================
FILE: src/Signer/Key/InMemory.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\SodiumBase64Polyfill;
use SensitiveParameter;
use SplFileObject;
use Throwable;
use function assert;
use function is_string;
final readonly class InMemory implements Key
{
/** @param non-empty-string $contents */
private function __construct(
#[SensitiveParameter]
public string $contents,
#[SensitiveParameter]
public string $passphrase,
) {
}
/** @param non-empty-string $contents */
public static function plainText(
#[SensitiveParameter]
string $contents,
#[SensitiveParameter]
string $passphrase = '',
): self {
self::guardAgainstEmptyKey($contents); // @phpstan-ignore staticMethod.alreadyNarrowedType
return new self($contents, $passphrase);
}
/** @param non-empty-string $contents */
public static function base64Encoded(
#[SensitiveParameter]
string $contents,
#[SensitiveParameter]
string $passphrase = '',
): self {
$decoded = SodiumBase64Polyfill::base642bin(
$contents,
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_ORIGINAL,
);
self::guardAgainstEmptyKey($decoded); // @phpstan-ignore staticMethod.alreadyNarrowedType
return new self($decoded, $passphrase);
}
/**
* @param non-empty-string $path
*
* @throws FileCouldNotBeRead
*/
public static function file(
string $path,
#[SensitiveParameter]
string $passphrase = '',
): self {
try {
$file = new SplFileObject($path);
} catch (Throwable $exception) {
throw FileCouldNotBeRead::onPath($path, $exception);
}
$fileSize = $file->getSize();
$contents = $fileSize > 0 ? $file->fread($file->getSize()) : '';
assert(is_string($contents));
self::guardAgainstEmptyKey($contents);
return new self($contents, $passphrase);
}
/** @phpstan-assert non-empty-string $contents */
private static function guardAgainstEmptyKey(string $contents): void
{
if ($contents === '') {
throw InvalidKeyProvided::cannotBeEmpty();
}
}
public function contents(): string
{
return $this->contents;
}
public function passphrase(): string
{
return $this->passphrase;
}
}
================================================
FILE: src/Signer/Key.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
interface Key
{
/** @return non-empty-string */
public function contents(): string;
public function passphrase(): string;
}
================================================
FILE: src/Signer/OpenSSL.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer;
use OpenSSLAsymmetricKey;
use function array_key_exists;
use function assert;
use function is_array;
use function is_bool;
use function is_int;
use function openssl_error_string;
use function openssl_pkey_get_details;
use function openssl_pkey_get_private;
use function openssl_pkey_get_public;
use function openssl_sign;
use function openssl_verify;
use const OPENSSL_KEYTYPE_DH;
use const OPENSSL_KEYTYPE_DSA;
use const OPENSSL_KEYTYPE_EC;
use const OPENSSL_KEYTYPE_RSA;
use const PHP_EOL;
abstract readonly class OpenSSL implements Signer
{
protected const array KEY_TYPE_MAP = [
OPENSSL_KEYTYPE_RSA => 'RSA',
OPENSSL_KEYTYPE_DSA => 'DSA',
OPENSSL_KEYTYPE_DH => 'DH',
OPENSSL_KEYTYPE_EC => 'EC',
];
/**
* @return non-empty-string
*
* @throws CannotSignPayload
* @throws InvalidKeyProvided
*/
final protected function createSignature(
Key $key,
string $payload,
): string {
$opensslKey = $this->getPrivateKey($key);
$signature = '';
if (! openssl_sign($payload, $signature, $opensslKey, $this->algorithm())) {
throw CannotSignPayload::errorHappened($this->fullOpenSSLErrorString());
}
return $signature;
}
/** @throws CannotSignPayload */
private function getPrivateKey(
Key $key,
): OpenSSLAsymmetricKey {
return $this->validateKey(openssl_pkey_get_private($key->contents(), $key->passphrase()));
}
/** @throws InvalidKeyProvided */
final protected function verifySignature(
string $expected,
string $payload,
Key $key,
): bool {
$opensslKey = $this->getPublicKey($key);
$result = openssl_verify($payload, $expected, $opensslKey, $this->algorithm());
return $result === 1;
}
/** @throws InvalidKeyProvided */
private function getPublicKey(Key $key): OpenSSLAsymmetricKey
{
return $this->validateKey(openssl_pkey_get_public($key->contents()));
}
/**
* Raises an exception when the key type is not the expected type
*
* @throws InvalidKeyProvided
*/
private function validateKey(OpenSSLAsymmetricKey|bool $key): OpenSSLAsymmetricKey
{
if (is_bool($key)) {
throw InvalidKeyProvided::cannotBeParsed($this->fullOpenSSLErrorString());
}
$details = openssl_pkey_get_details($key);
assert(is_array($details));
assert(array_key_exists('bits', $details));
assert(is_int($details['bits']));
assert(array_key_exists('type', $details));
assert(is_int($details['type']));
$this->guardAgainstIncompatibleKey($details['type'], $details['bits']);
return $key;
}
private function fullOpenSSLErrorString(): string
{
$error = '';
while ($msg = openssl_error_string()) {
$error .= PHP_EOL . '* ' . $msg;
}
return $error;
}
/** @throws InvalidKeyProvided */
abstract protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void;
/**
* Returns which algorithm to be used to create/verify the signature (using OpenSSL constants)
*
* @internal
*/
abstract public function algorithm(): int;
}
================================================
FILE: src/Signer/Rsa/Sha256.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA256;
final readonly class Sha256 extends Rsa
{
public function algorithmId(): string
{
return 'RS256';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA256;
}
}
================================================
FILE: src/Signer/Rsa/Sha384.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA384;
final readonly class Sha384 extends Rsa
{
public function algorithmId(): string
{
return 'RS384';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA384;
}
}
================================================
FILE: src/Signer/Rsa/Sha512.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer\Rsa;
use Lcobucci\JWT\Signer\Rsa;
use const OPENSSL_ALGO_SHA512;
final readonly class Sha512 extends Rsa
{
public function algorithmId(): string
{
return 'RS512';
}
public function algorithm(): int
{
return OPENSSL_ALGO_SHA512;
}
}
================================================
FILE: src/Signer/Rsa.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Signer;
use const OPENSSL_KEYTYPE_RSA;
abstract readonly class Rsa extends OpenSSL
{
private const int MINIMUM_KEY_LENGTH = 2048;
final public function sign(string $payload, Key $key): string
{
return $this->createSignature($key, $payload);
}
final public function verify(string $expected, string $payload, Key $key): bool
{
return $this->verifySignature($expected, $payload, $key);
}
final protected function guardAgainstIncompatibleKey(int $type, int $lengthInBits): void
{
if ($type !== OPENSSL_KEYTYPE_RSA) {
throw InvalidKeyProvided::incompatibleKeyType(
self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_RSA],
self::KEY_TYPE_MAP[$type] ?? 'unknown',
);
}
if ($lengthInBits < self::MINIMUM_KEY_LENGTH) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH, $lengthInBits);
}
}
}
================================================
FILE: src/Signer.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Signer\CannotSignPayload;
use Lcobucci\JWT\Signer\Ecdsa\ConversionFailed;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key;
use NoDiscard;
/** @immutable */
interface Signer
{
/**
* Returns the algorithm id
*
* @return non-empty-string
*/
public function algorithmId(): string;
/**
* Creates a hash for the given payload
*
* @param non-empty-string $payload
*
* @return non-empty-string
*
* @throws CannotSignPayload When payload signing fails.
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
#[NoDiscard]
public function sign(string $payload, Key $key): string;
/**
* Returns if the expected hash matches with the data and key
*
* @param non-empty-string $expected
* @param non-empty-string $payload
*
* @throws InvalidKeyProvided When issue key is invalid/incompatible.
* @throws ConversionFailed When signature could not be converted.
*/
#[NoDiscard]
public function verify(string $expected, string $payload, Key $key): bool;
}
================================================
FILE: src/SodiumBase64Polyfill.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Encoding\CannotDecodeContent;
use SodiumException;
use function base64_decode;
use function base64_encode;
use function function_exists;
use function is_string;
use function rtrim;
use function sodium_base642bin;
use function sodium_bin2base64;
use function strtr;
/** @internal */
final readonly class SodiumBase64Polyfill
{
public const int SODIUM_BASE64_VARIANT_ORIGINAL = 1;
public const int SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
public const int SODIUM_BASE64_VARIANT_URLSAFE = 5;
public const int SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
public static function bin2base64(string $decoded, int $variant): string
{
if (! function_exists('sodium_bin2base64')) {
return self::bin2base64Fallback($decoded, $variant); // @codeCoverageIgnore
}
return sodium_bin2base64($decoded, $variant);
}
/** @return ($decoded is non-empty-string ? non-empty-string : string) */
public static function bin2base64Fallback(string $decoded, int $variant): string
{
$encoded = base64_encode($decoded);
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '+/', '-_');
}
if (
$variant === self::SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = rtrim($encoded, '=');
}
return $encoded;
}
/**
* @return ($encoded is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent
*/
public static function base642bin(string $encoded, int $variant): string
{
if (! function_exists('sodium_base642bin')) {
return self::base642binFallback($encoded, $variant); // @codeCoverageIgnore
}
try {
return sodium_base642bin($encoded, $variant, '');
} catch (SodiumException) {
throw CannotDecodeContent::invalidBase64String();
}
}
/**
* @return ($encoded is non-empty-string ? non-empty-string : string)
*
* @throws CannotDecodeContent
*/
public static function base642binFallback(string $encoded, int $variant): string
{
if (
$variant === self::SODIUM_BASE64_VARIANT_URLSAFE
|| $variant === self::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING
) {
$encoded = strtr($encoded, '-_', '+/');
}
$decoded = base64_decode($encoded, true);
if (! is_string($decoded)) {
throw CannotDecodeContent::invalidBase64String();
}
return $decoded;
}
}
================================================
FILE: src/Token/Builder.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Builder as BuilderInterface;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\Encoding\CannotEncodeContent;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\UnencryptedToken;
use NoDiscard;
use function array_diff;
use function array_merge;
use function in_array;
/** @immutable */
final readonly class Builder implements BuilderInterface
{
/**
* @param array<non-empty-string, mixed> $headers
* @param array<non-empty-string, mixed> $claims
*/
private function __construct(
private Encoder $encoder,
private ClaimsFormatter $claimFormatter,
private array $headers = ['typ' => 'JWT', 'alg' => null],
private array $claims = [],
) {
}
#[NoDiscard]
public static function new(Encoder $encoder, ClaimsFormatter $claimFormatter): self
{
return new self($encoder, $claimFormatter);
}
public function permittedFor(string ...$audiences): BuilderInterface
{
$configured = $this->claims[RegisteredClaims::AUDIENCE] ?? [];
$toAppend = array_diff($audiences, $configured);
return $this->newWithClaim(RegisteredClaims::AUDIENCE, array_merge($configured, $toAppend));
}
public function expiresAt(DateTimeImmutable $expiration): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::EXPIRATION_TIME, $expiration);
}
public function identifiedBy(string $id): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::ID, $id);
}
public function issuedAt(DateTimeImmutable $issuedAt): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::ISSUED_AT, $issuedAt);
}
public function issuedBy(string $issuer): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::ISSUER, $issuer);
}
public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::NOT_BEFORE, $notBefore);
}
public function relatedTo(string $subject): BuilderInterface
{
return $this->newWithClaim(RegisteredClaims::SUBJECT, $subject);
}
public function withHeader(string $name, mixed $value): BuilderInterface
{
$headers = $this->headers;
$headers[$name] = $value;
return new self(
$this->encoder,
$this->claimFormatter,
$headers,
$this->claims,
);
}
public function withClaim(string $name, mixed $value): BuilderInterface
{
if (in_array($name, RegisteredClaims::ALL, true)) {
throw RegisteredClaimGiven::forClaim($name);
}
return $this->newWithClaim($name, $value);
}
/** @param non-empty-string $name */
private function newWithClaim(string $name, mixed $value): BuilderInterface
{
$claims = $this->claims;
$claims[$name] = $value;
return new self(
$this->encoder,
$this->claimFormatter,
$this->headers,
$claims,
);
}
/**
* @param array<non-empty-string, mixed> $items
*
* @throws CannotEncodeContent When data cannot be converted to JSON.
*/
private function encode(array $items): string
{
return $this->encoder->base64UrlEncode(
$this->encoder->jsonEncode($items),
);
}
public function getToken(Signer $signer, Key $key): UnencryptedToken
{
$headers = $this->headers;
$headers['alg'] = $signer->algorithmId();
$encodedHeaders = $this->encode($headers);
$encodedClaims = $this->encode($this->claimFormatter->formatClaims($this->claims));
$signature = $signer->sign($encodedHeaders . '.' . $encodedClaims, $key);
$encodedSignature = $this->encoder->base64UrlEncode($signature);
return new Plain(
new DataSet($headers, $encodedHeaders),
new DataSet($this->claims, $encodedClaims),
new Signature($signature, $encodedSignature),
);
}
}
================================================
FILE: src/Token/DataSet.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use function array_key_exists;
final readonly class DataSet
{
/** @param array<non-empty-string, mixed> $data */
public function __construct(private array $data, private string $encoded)
{
}
/** @param non-empty-string $name */
public function get(string $name, mixed $default = null): mixed
{
return $this->data[$name] ?? $default;
}
/** @param non-empty-string $name */
public function has(string $name): bool
{
return array_key_exists($name, $this->data);
}
/** @return array<non-empty-string, mixed> */
public function all(): array
{
return $this->data;
}
public function toString(): string
{
return $this->encoded;
}
}
================================================
FILE: src/Token/InvalidTokenStructure.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class InvalidTokenStructure extends InvalidArgumentException implements Exception
{
public static function missingOrNotEnoughSeparators(): self
{
return new self('The JWT string must have two dots');
}
public static function missingHeaderPart(): self
{
return new self('The JWT string is missing the Header part');
}
public static function missingClaimsPart(): self
{
return new self('The JWT string is missing the Claim part');
}
public static function missingSignaturePart(): self
{
return new self('The JWT string is missing the Signature part');
}
/** @param non-empty-string $part */
public static function arrayExpected(string $part): self
{
return new self($part . ' must be an array with non-empty-string keys');
}
public static function dateIsNotParseable(string $value): self
{
return new self('Value is not in the allowed date format: ' . $value);
}
}
================================================
FILE: src/Token/Parser.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeImmutable;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Parser as ParserInterface;
use Lcobucci\JWT\Token as TokenInterface;
use function array_key_exists;
use function count;
use function explode;
use function is_array;
use function is_numeric;
use function number_format;
final readonly class Parser implements ParserInterface
{
private const int MICROSECOND_PRECISION = 6;
public function __construct(private Decoder $decoder)
{
}
public function parse(string $jwt): TokenInterface
{
[$encodedHeaders, $encodedClaims, $encodedSignature] = $this->splitJwt($jwt);
if ($encodedHeaders === '') {
throw InvalidTokenStructure::missingHeaderPart();
}
if ($encodedClaims === '') {
throw InvalidTokenStructure::missingClaimsPart();
}
if ($encodedSignature === '') {
throw InvalidTokenStructure::missingSignaturePart();
}
$header = $this->parseHeader($encodedHeaders);
return new Plain(
new DataSet($header, $encodedHeaders),
new DataSet($this->parseClaims($encodedClaims), $encodedClaims),
$this->parseSignature($encodedSignature),
);
}
/**
* Splits the JWT string into an array
*
* @param non-empty-string $jwt
*
* @return string[]
*
* @throws InvalidTokenStructure When JWT doesn't have all parts.
*/
private function splitJwt(string $jwt): array
{
$data = explode('.', $jwt);
if (count($data) !== 3) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
return $data;
}
/**
* Parses the header from a string
*
* @param non-empty-string $data
*
* @return array<non-empty-string, mixed>
*
* @throws UnsupportedHeaderFound When an invalid header is informed.
* @throws InvalidTokenStructure When parsed content isn't an array.
*/
private function parseHeader(string $data): array
{
$header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($header)) {
throw InvalidTokenStructure::arrayExpected('headers');
}
$this->guardAgainstEmptyStringKeys($header, 'headers');
if (array_key_exists('enc', $header)) {
throw UnsupportedHeaderFound::encryption();
}
if (! array_key_exists('typ', $header)) {
$header['typ'] = 'JWT';
}
return $header;
}
/**
* Parses the claim set from a string
*
* @param non-empty-string $data
*
* @return array<non-empty-string, mixed>
*
* @throws InvalidTokenStructure When parsed content isn't an array or contains non-parseable dates.
*/
private function parseClaims(string $data): array
{
$claims = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
if (! is_array($claims)) {
throw InvalidTokenStructure::arrayExpected('claims');
}
$this->guardAgainstEmptyStringKeys($claims, 'claims');
if (array_key_exists(RegisteredClaims::AUDIENCE, $claims)) {
$claims[RegisteredClaims::AUDIENCE] = (array) $claims[RegisteredClaims::AUDIENCE];
}
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
if (! array_key_exists($claim, $claims)) {
continue;
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
/**
* @param array<string, mixed> $array
* @param non-empty-string $part
*
* @phpstan-assert array<non-empty-string, mixed> $array
*/
private function guardAgainstEmptyStringKeys(array $array, string $part): void
{
foreach ($array as $key => $value) {
if ($key === '') {
throw InvalidTokenStructure::arrayExpected($part);
}
}
}
/** @throws InvalidTokenStructure */
private function convertDate(int|float|string $timestamp): DateTimeImmutable
{
if (! is_numeric($timestamp)) {
throw InvalidTokenStructure::dateIsNotParseable($timestamp);
}
$normalizedTimestamp = number_format((float) $timestamp, self::MICROSECOND_PRECISION, '.', '');
$date = DateTimeImmutable::createFromFormat('U.u', $normalizedTimestamp);
if ($date === false) {
throw InvalidTokenStructure::dateIsNotParseable($normalizedTimestamp);
}
return $date;
}
/**
* Returns the signature from given data
*
* @param non-empty-string $data
*/
private function parseSignature(string $data): Signature
{
$hash = $this->decoder->base64UrlDecode($data);
return new Signature($hash, $data);
}
}
================================================
FILE: src/Token/Plain.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use DateTimeInterface;
use Lcobucci\JWT\UnencryptedToken;
use function in_array;
final readonly class Plain implements UnencryptedToken
{
public function __construct(
private DataSet $headers,
private DataSet $claims,
private Signature $signature,
) {
}
public function headers(): DataSet
{
return $this->headers;
}
public function claims(): DataSet
{
return $this->claims;
}
public function signature(): Signature
{
return $this->signature;
}
public function payload(): string
{
return $this->headers->toString() . '.' . $this->claims->toString();
}
public function isPermittedFor(string $audience): bool
{
return in_array($audience, $this->claims->get(RegisteredClaims::AUDIENCE, []), true);
}
public function isIdentifiedBy(string $id): bool
{
return $this->claims->get(RegisteredClaims::ID) === $id;
}
public function isRelatedTo(string $subject): bool
{
return $this->claims->get(RegisteredClaims::SUBJECT) === $subject;
}
public function hasBeenIssuedBy(string ...$issuers): bool
{
return in_array($this->claims->get(RegisteredClaims::ISSUER), $issuers, true);
}
public function hasBeenIssuedBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::ISSUED_AT);
}
public function isMinimumTimeBefore(DateTimeInterface $now): bool
{
return $now >= $this->claims->get(RegisteredClaims::NOT_BEFORE);
}
public function isExpired(DateTimeInterface $now): bool
{
if (! $this->claims->has(RegisteredClaims::EXPIRATION_TIME)) {
return false;
}
return $now >= $this->claims->get(RegisteredClaims::EXPIRATION_TIME);
}
public function toString(): string
{
return $this->headers->toString() . '.'
. $this->claims->toString() . '.'
. $this->signature->toString();
}
}
================================================
FILE: src/Token/RegisteredClaimGiven.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
use function sprintf;
final class RegisteredClaimGiven extends InvalidArgumentException implements Exception
{
private const DEFAULT_MESSAGE = 'Builder#withClaim() is meant to be used for non-registered claims, '
. 'check the documentation on how to set claim "%s"';
/** @param non-empty-string $name */
public static function forClaim(string $name): self
{
return new self(sprintf(self::DEFAULT_MESSAGE, $name));
}
}
================================================
FILE: src/Token/RegisteredClaims.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
/**
* Defines the list of claims that are registered in the IANA "JSON Web Token Claims" registry
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1
*/
interface RegisteredClaims
{
public const array ALL = [
self::AUDIENCE,
self::EXPIRATION_TIME,
self::ID,
self::ISSUED_AT,
self::ISSUER,
self::NOT_BEFORE,
self::SUBJECT,
];
public const array DATE_CLAIMS = [
self::ISSUED_AT,
self::NOT_BEFORE,
self::EXPIRATION_TIME,
];
/**
* Identifies the recipients that the JWT is intended for
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
public const string AUDIENCE = 'aud';
/**
* Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.4
*/
public const string EXPIRATION_TIME = 'exp';
/**
* Provides a unique identifier for the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.7
*/
public const string ID = 'jti';
/**
* Identifies the time at which the JWT was issued
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.6
*/
public const string ISSUED_AT = 'iat';
/**
* Identifies the principal that issued the JWT
*
* @see https://tools.ietf.org/html/rfc7519#section-4.1.1
*/
public const string ISSUER = 'iss';
/**
* Identifies the time before which the JWT MUST NOT be accepted for processing
*
* https://tools.ietf.org/html/rfc7519#section-4.1.5
*/
public const string NOT_BEFORE = 'nbf';
/**
* Identifies the principal that is the subject of the JWT.
*
* https://tools.ietf.org/html/rfc7519#section-4.1.2
*/
public const string SUBJECT = 'sub';
}
================================================
FILE: src/Token/Signature.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
final readonly class Signature
{
/**
* @param non-empty-string $hash
* @param non-empty-string $encoded
*/
public function __construct(private string $hash, private string $encoded)
{
}
/** @return non-empty-string */
public function hash(): string
{
return $this->hash;
}
/**
* Returns the encoded version of the signature
*
* @return non-empty-string
*/
public function toString(): string
{
return $this->encoded;
}
}
================================================
FILE: src/Token/UnsupportedHeaderFound.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Token;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class UnsupportedHeaderFound extends InvalidArgumentException implements Exception
{
public static function encryption(): self
{
return new self('Encryption is not supported yet');
}
}
================================================
FILE: src/Token.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use DateTimeInterface;
use Lcobucci\JWT\Token\DataSet;
use NoDiscard;
/** @immutable */
interface Token
{
/**
* Returns the token headers
*/
public function headers(): DataSet;
/**
* Returns if the token is allowed to be used by the audience
*
* @param non-empty-string $audience
*/
public function isPermittedFor(string $audience): bool;
/**
* Returns if the token has the given id
*
* @param non-empty-string $id
*/
public function isIdentifiedBy(string $id): bool;
/**
* Returns if the token has the given subject
*
* @param non-empty-string $subject
*/
public function isRelatedTo(string $subject): bool;
/**
* Returns if the token was issued by any of given issuers
*
* @param non-empty-string ...$issuers
*/
public function hasBeenIssuedBy(string ...$issuers): bool;
/**
* Returns if the token was issued before of given time
*/
public function hasBeenIssuedBefore(DateTimeInterface $now): bool;
/**
* Returns if the token minimum time is before than given time
*/
public function isMinimumTimeBefore(DateTimeInterface $now): bool;
/**
* Returns if the token is expired
*/
public function isExpired(DateTimeInterface $now): bool;
/**
* Returns an encoded representation of the token
*
* @return non-empty-string
*/
#[NoDiscard]
public function toString(): string;
}
================================================
FILE: src/UnencryptedToken.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Token\DataSet;
use Lcobucci\JWT\Token\Signature;
interface UnencryptedToken extends Token
{
/**
* Returns the token claims
*/
public function claims(): DataSet;
/**
* Returns the token signature
*/
public function signature(): Signature;
/**
* Returns the token payload
*
* @return non-empty-string
*/
public function payload(): string;
}
================================================
FILE: src/Validation/Constraint/CannotValidateARegisteredClaim.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
{
/** @param non-empty-string $claim */
public static function create(string $claim): self
{
return new self(
'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value',
);
}
}
================================================
FILE: src/Validation/Constraint/HasClaim.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
use function in_array;
final readonly class HasClaim implements Constraint
{
/** @param non-empty-string $claim */
public function __construct(private string $claim)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$claims = $token->claims();
if (! $claims->has($this->claim)) {
throw ConstraintViolation::error('The token does not have the claim "' . $this->claim . '"', $this);
}
}
}
================================================
FILE: src/Validation/Constraint/HasClaimWithValue.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
use function in_array;
final readonly class HasClaimWithValue implements Constraint
{
/** @param non-empty-string $claim */
public function __construct(private string $claim, private mixed $expectedValue)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$claims = $token->claims();
if (! $claims->has($this->claim)) {
throw ConstraintViolation::error('The token does not have the claim "' . $this->claim . '"', $this);
}
if ($claims->get($this->claim) !== $this->expectedValue) {
throw ConstraintViolation::error(
'The claim "' . $this->claim . '" does not have the expected value',
$this,
);
}
}
}
================================================
FILE: src/Validation/Constraint/IdentifiedBy.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final readonly class IdentifiedBy implements Constraint
{
/** @param non-empty-string $id */
public function __construct(private string $id)
{
}
public function assert(Token $token): void
{
if (! $token->isIdentifiedBy($this->id)) {
throw ConstraintViolation::error(
'The token is not identified with the expected ID',
$this,
);
}
}
}
================================================
FILE: src/Validation/Constraint/IssuedBy.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final readonly class IssuedBy implements Constraint
{
/** @var non-empty-string[] */
private array $issuers;
/** @param non-empty-string ...$issuers */
public function __construct(string ...$issuers)
{
$this->issuers = $issuers;
}
public function assert(Token $token): void
{
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
throw ConstraintViolation::error(
'The token was not issued by the given issuers',
$this,
);
}
}
}
================================================
FILE: src/Validation/Constraint/LeewayCannotBeNegative.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class LeewayCannotBeNegative extends InvalidArgumentException implements Exception
{
public static function create(): self
{
return new self('Leeway cannot be negative');
}
}
================================================
FILE: src/Validation/Constraint/LooseValidAt.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final readonly class LooseValidAt implements ValidAtInterface
{
private DateInterval $leeway;
public function __construct(private Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(Token $token, DateTimeInterface $now): void
{
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
{
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(Token $token, DateTimeInterface $now): void
{
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}
================================================
FILE: src/Validation/Constraint/PermittedFor.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final readonly class PermittedFor implements Constraint
{
/** @param non-empty-string $audience */
public function __construct(private string $audience)
{
}
public function assert(Token $token): void
{
if (! $token->isPermittedFor($this->audience)) {
throw ConstraintViolation::error(
'The token is not allowed to be used by this audience',
$this,
);
}
}
}
================================================
FILE: src/Validation/Constraint/RelatedTo.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final readonly class RelatedTo implements Constraint
{
/** @param non-empty-string $subject */
public function __construct(private string $subject)
{
}
public function assert(Token $token): void
{
if (! $token->isRelatedTo($this->subject)) {
throw ConstraintViolation::error(
'The token is not related to the expected subject',
$this,
);
}
}
}
================================================
FILE: src/Validation/Constraint/SignedWith.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
final readonly class SignedWith implements SignedWithInterface
{
public function __construct(private Signer $signer, private Signer\Key $key)
{
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
if ($token->headers()->get('alg') !== $this->signer->algorithmId()) {
throw ConstraintViolation::error('Token signer mismatch', $this);
}
if (! $this->signer->verify($token->signature()->hash(), $token->payload(), $this->key)) {
throw ConstraintViolation::error('Token signature mismatch', $this);
}
}
}
================================================
FILE: src/Validation/Constraint/SignedWithOneInSet.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use const PHP_EOL;
final readonly class SignedWithOneInSet implements SignedWithInterface
{
/** @var array<SignedWithUntilDate> */
private array $constraints;
public function __construct(SignedWithUntilDate ...$constraints)
{
$this->constraints = $constraints;
}
public function assert(Token $token): void
{
$errorMessage = 'It was not possible to verify the signature of the token, reasons:';
foreach ($this->constraints as $constraint) {
try {
$constraint->assert($token);
return;
} catch (ConstraintViolation $violation) {
$errorMessage .= PHP_EOL . '- ' . $violation->getMessage();
}
}
throw ConstraintViolation::error($errorMessage, $this);
}
}
================================================
FILE: src/Validation/Constraint/SignedWithUntilDate.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateTimeImmutable;
use DateTimeInterface;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use Psr\Clock\ClockInterface;
final readonly class SignedWithUntilDate implements SignedWithInterface
{
private SignedWith $verifySignature;
private ClockInterface $clock;
public function __construct(
Signer $signer,
Signer\Key $key,
private DateTimeImmutable $validUntil,
?ClockInterface $clock = null,
) {
$this->verifySignature = new SignedWith($signer, $key);
$this->clock = $clock ?? new class () implements ClockInterface {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
};
}
public function assert(Token $token): void
{
if ($this->validUntil < $this->clock->now()) {
throw ConstraintViolation::error(
'This constraint was only usable until '
. $this->validUntil->format(DateTimeInterface::RFC3339),
$this,
);
}
$this->verifySignature->assert($token);
}
}
================================================
FILE: src/Validation/Constraint/StrictValidAt.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final readonly class StrictValidAt implements ValidAtInterface
{
private DateInterval $leeway;
public function __construct(private Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) {
throw ConstraintViolation::error('"Expiration Time" claim missing', $this);
}
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
throw ConstraintViolation::error('"Not Before" claim missing', $this);
}
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
throw ConstraintViolation::error('"Issued At" claim missing', $this);
}
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}
================================================
FILE: src/Validation/Constraint.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
interface Constraint
{
/** @throws ConstraintViolation */
public function assert(Token $token): void;
}
================================================
FILE: src/Validation/ConstraintViolation.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class ConstraintViolation extends RuntimeException implements Exception
{
/** @param class-string<Constraint>|null $constraint */
public function __construct(
string $message = '',
public readonly ?string $constraint = null,
) {
parent::__construct($message);
}
/** @param non-empty-string $message */
public static function error(string $message, Constraint $constraint): self
{
return new self(message: $message, constraint: $constraint::class);
}
}
================================================
FILE: src/Validation/NoConstraintsGiven.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class NoConstraintsGiven extends RuntimeException implements Exception
{
}
================================================
FILE: src/Validation/RequiredConstraintsViolated.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
use function array_map;
use function implode;
final class RequiredConstraintsViolated extends RuntimeException implements Exception
{
/** @param ConstraintViolation[] $violations */
public function __construct(
string $message = '',
public readonly array $violations = [],
) {
parent::__construct($message);
}
public static function fromViolations(ConstraintViolation ...$violations): self
{
return new self(message: self::buildMessage($violations), violations: $violations);
}
/** @param ConstraintViolation[] $violations */
private static function buildMessage(array $violations): string
{
$violations = array_map(
static function (ConstraintViolation $violation): string {
return '- ' . $violation->getMessage();
},
$violations,
);
$message = "The token violates some mandatory constraints, details:\n";
$message .= implode("\n", $violations);
return $message;
}
/** @return ConstraintViolation[] */
public function violations(): array
{
return $this->violations;
}
}
================================================
FILE: src/Validation/SignedWith.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface SignedWith extends Constraint
{
}
================================================
FILE: src/Validation/ValidAt.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface ValidAt extends Constraint
{
}
================================================
FILE: src/Validation/Validator.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
final readonly class Validator implements \Lcobucci\JWT\Validator
{
public function assert(Token $token, Constraint ...$constraints): void
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
$violations = [];
foreach ($constraints as $constraint) {
$this->checkConstraint($constraint, $token, $violations);
}
if ($violations !== []) {
throw RequiredConstraintsViolated::fromViolations(...$violations);
}
}
/** @param ConstraintViolation[] $violations */
private function checkConstraint(
Constraint $constraint,
Token $token,
array &$violations,
): void {
try {
$constraint->assert($token);
} catch (ConstraintViolation $e) {
$violations[] = $e;
}
}
public function validate(Token $token, Constraint ...$constraints): bool
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
try {
foreach ($constraints as $constraint) {
$constraint->assert($token);
}
return true;
} catch (ConstraintViolation) {
return false;
}
}
}
================================================
FILE: src/Validator.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\NoConstraintsGiven;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use NoDiscard;
interface Validator
{
/**
* @throws RequiredConstraintsViolated
* @throws NoConstraintsGiven
*/
public function assert(Token $token, Constraint ...$constraints): void;
/** @throws NoConstraintsGiven */
#[NoDiscard]
public function validate(Token $token, Constraint ...$constraints): bool;
}
================================================
FILE: tests/Benchmark/AlgorithmsBench.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests\Benchmark;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Key\InMemory;
use PhpBench\Attributes as Bench;
use RuntimeException;
#[Bench\Iterations(5)]
#[Bench\Revs(100)]
#[Bench\Warmup(3)]
abstract class AlgorithmsBench
{
private const array SUPPORTED_ALGORITHMS = [
'hmac' => ['HS256', 'HS384', 'HS512'],
'rsa' => ['RS256', 'RS384', 'RS512'],
'ecdsa' => ['ES256', 'ES384', 'ES512'],
'eddsa' => ['EdDSA'],
'blake2b' => ['BLAKE2B'],
];
protected const string PAYLOAD = "It\xe2\x80\x99s a dangerous business, Frodo, going out your door. You step"
. " onto the road, and if you don't keep your feet, there\xe2\x80\x99s no knowing where you might be swept"
. ' off to.';
#[Bench\Subject]
#[Bench\ParamProviders('hmacAlgorithms')]
#[Bench\Groups(['hmac', 'symmetric'])]
public function hmac(): void
{
$this->runBenchmark();
}
/** @return iterable<string, array{algorithm: string}> */
public function hmacAlgorithms(): iterable
{
yield from $this->iterateAlgorithms('hmac');
}
#[Bench\Subject]
#[Bench\ParamProviders('rsaAlgorithms')]
#[Bench\Groups(['rsa', 'asymmetric'])]
public function rsa(): void
{
$this->runBenchmark();
}
/** @return iterable<string, array{algorithm: string}> */
public function rsaAlgorithms(): iterable
{
yield from $this->iterateAlgorithms('rsa');
}
#[Bench\Subject]
#[Bench\ParamProviders('ecdsaAlgorithms')]
#[Bench\Groups(['ecdsa', 'asymmetric'])]
public function ecdsa(): void
{
$this->runBenchmark();
}
/** @return iterable<string, array{algorithm: string}> */
public function ecdsaAlgorithms(): iterable
{
yield from $this->iterateAlgorithms('ecdsa');
}
#[Bench\Subject]
#[Bench\ParamProviders('eddsaAlgorithms')]
#[Bench\Groups(['eddsa', 'asymmetric'])]
public function eddsa(): void
{
$this->runBenchmark();
}
/** @return iterable<string, array{algorithm: string}> */
public function eddsaAlgorithms(): iterable
{
yield from $this->iterateAlgorithms('eddsa');
}
#[Bench\Subject]
#[Bench\ParamProviders('blake2bAlgorithms')]
#[Bench\Groups(['blake2b', 'symmetric'])]
public function blake2b(): void
{
$this->runBenchmark();
}
/** @return iterable<string, array{algorithm: string}> */
public function blake2bAlgorithms(): iterable
{
yield from $this->iterateAlgorithms('blake2b');
}
abstract protected function runBenchmark(): void;
protected function resolveAlgorithm(string $name): Signer
{
return match ($name) {
'HS256' => new Signer\Hmac\Sha256(),
'HS384' => new Signer\Hmac\Sha384(),
'HS512' => new Signer\Hmac\Sha512(),
'RS256' => new Signer\Rsa\Sha256(),
'RS384' => new Signer\Rsa\Sha384(),
'RS512' => new Signer\Rsa\Sha512(),
'ES256' => new Signer\Ecdsa\Sha256(),
'ES384' => new Signer\Ecdsa\Sha384(),
'ES512' => new Signer\Ecdsa\Sha512(),
'EdDSA' => new Signer\Eddsa(),
'BLAKE2B' => new Signer\Blake2b(),
default => throw new RuntimeException('Unknown algorithm'),
};
}
protected function resolveSigningKey(string $name): Key
{
return match ($name) {
'HS256' => InMemory::base64Encoded('n5p7sBK+dvBmSKNlQIFrsuB1cnmnwsxGyWXPgRSZtWY='),
'HS384' => InMemory::base64Encoded('kNUb8KvJC+fvhPzIuimwWHleES3AAnUjI+UIWZyor5HT33st9KIjfPkgtfu60UL2'),
'HS512' => InMemory::base64Encoded(
'OgXKIs+aZCQgXnDfi8mAFnWVo+Xn3JTR7BvT/j1Q1zP9oRx9xGg4jmpq00RsPPDclYi8+jRl664pu4d0zan2ow==',
),
'RS256', 'RS384', 'RS512' => InMemory::file(__DIR__ . '/Rsa/private.key'),
'ES256' => InMemory::file(__DIR__ . '/Ecdsa/private-256.key'),
'ES384' => InMemory::file(__DIR__ . '/Ecdsa/private-384.key'),
'ES512' => InMemory::file(__DIR__ . '/Ecdsa/private-521.key'),
'EdDSA' => InMemory::base64Encoded(
'K3NWT0XqaH+4jgi42gQmHnFE+HTPVhFYi3u4DFJ3OpRHRMt/aGRBoKD/Pt5H/iYgGCla7Q04CdjOUpLSrjZhtg==',
),
'BLAKE2B' => InMemory::base64Encoded('b6DNRcX2SFapbICe6lXWYoOZA+JXL/dvkfWiv2hJv3Y='),
default => throw new RuntimeException('Unknown algorithm'),
};
}
protected function resolveVerificationKey(string $name): Key
{
return match ($name) {
'HS256', 'HS384', 'HS512', 'BLAKE2B' => $this->resolveSigningKey($name),
'RS256', 'RS384', 'RS512' => InMemory::file(__DIR__ . '/Rsa/public.key'),
'ES256' => InMemory::file(__DIR__ . '/Ecdsa/public-256.key'),
'ES384' => InMemory::file(__DIR__ . '/Ecdsa/public-384.key'),
'ES512' => InMemory::file(__DIR__ . '/Ecdsa/public-521.key'),
'EdDSA' => InMemory::base64Encoded('R0TLf2hkQaCg/z7eR/4mIBgpWu0NOAnYzlKS0q42YbY='),
default => throw new RuntimeException('Unknown algorithm'),
};
}
/** @return iterable<string, array{algorithm: string}> */
private function iterateAlgorithms(string $family): iterable
{
foreach (self::SUPPORTED_ALGORITHMS[$family] ?? [] as $algorithm) {
yield $algorithm => ['algorithm' => $algorithm];
}
}
}
================================================
FILE: tests/Benchmark/CreateSignatureBench.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests\Benchmark;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use PhpBench\Attributes as Bench;
#[Bench\BeforeMethods('initialize')]
final class CreateSignatureBench extends AlgorithmsBench
{
private Signer $algorithm;
private Key $key;
/** @param array{algorithm: string} $params */
public function initialize(array $params): void
{
$this->algorithm = $this->resolveAlgorithm($params['algorithm']);
$this->key = $this->resolveSigningKey($params['algorithm']);
}
protected function runBenchmark(): void
{
$void = $this->algorithm->sign(self::PAYLOAD, $this->key);
}
}
================================================
FILE: tests/Benchmark/Ecdsa/private-256.key
================================================
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIK/5B8mfmtOq5sTN8hEivOK9aLUoPmkHFUrZEYQPogjPoAoGCCqGSM49
AwEHoUQDQgAEZe2loSV3wrroKUN/4zhwGhCqo3Xhu1td4QjeQ5wIVR0eUu11cBFj
9/nkDd+fNBs9ybqGCvfgynyn6e7NAITRnA==
-----END EC PRIVATE KEY-----
================================================
FILE: tests/Benchmark/Ecdsa/private-384.key
================================================
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDDZi2q9Sdoiu0XNsrdD/PLBtgAr48L4MWy7XMiND8riSeJkTYnhPlra
xMSRKkd2OhSgBwYFK4EEACKhZANiAAR+oJdVSn/ZrdLRzsad6Dv7bOVLdPkc0GZu
n5//VFLgobl2lxEhRvWxW0tBRkJKj8tnVRWaWbe0L1C7QCtGN6NnSxpn6Y4FhtE2
prdfXjnjWAQI0+TdPeCvQtOFQjrLWrw=
-----END EC PRIVATE KEY-----
================================================
FILE: tests/Benchmark/Ecdsa/private-521.key
================================================
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBCwZmxfodGCjbu5tgb4al9Qwv36dS9lCYk4Hjq6VMMneMH2tlDaS1
kEid5mVnJrznhLJFn5IO3mB+FC/V1q2RKQigBwYFK4EEACOhgYkDgYYABAGjhQzd
+mwlkFVzY2Ak1fW+DMrqPxszQO6SR8cqpcAhb9BSR9whqghljOU1X9cJe/A6/2WF
WqTRpj6RaRkzot6KbwEC0jo08XIXdyWkp4AsNbLKPDaO2DZH/8LMkeouNHxJJC/8
/nU+5kOPBeoZV9qodJYOhnkxiNjHJjrFL8YYRXgUTw==
-----END EC PRIVATE KEY-----
================================================
FILE: tests/Benchmark/Ecdsa/public-256.key
================================================
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZe2loSV3wrroKUN/4zhwGhCqo3Xh
u1td4QjeQ5wIVR0eUu11cBFj9/nkDd+fNBs9ybqGCvfgynyn6e7NAITRnA==
-----END PUBLIC KEY-----
================================================
FILE: tests/Benchmark/Ecdsa/public-384.key
================================================
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfqCXVUp/2a3S0c7Gneg7+2zlS3T5HNBm
bp+f/1RS4KG5dpcRIUb1sVtLQUZCSo/LZ1UVmlm3tC9Qu0ArRjejZ0saZ+mOBYbR
Nqa3X14541gECNPk3T3gr0LThUI6y1q8
-----END PUBLIC KEY-----
================================================
FILE: tests/Benchmark/Ecdsa/public-521.key
================================================
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBo4UM3fpsJZBVc2NgJNX1vgzK6j8b
M0DukkfHKqXAIW/QUkfcIaoIZYzlNV/XCXvwOv9lhVqk0aY+kWkZM6Leim8BAtI6
NPFyF3clpKeALDWyyjw2jtg2R//CzJHqLjR8SSQv/P51PuZDjwXqGVfaqHSWDoZ5
MYjYxyY6xS/GGEV4FE8=
-----END PUBLIC KEY-----
================================================
FILE: tests/Benchmark/IssueTokenBench.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests\Benchmark;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use PhpBench\Attributes as Bench;
#[Bench\BeforeMethods('initialize')]
final class IssueTokenBench extends AlgorithmsBench
{
private Signer $algorithm;
private Key $key;
/** @param array{algorithm: string} $params */
public function initialize(array $params): void
{
$this->algorithm = $this->resolveAlgorithm($params['algorithm']);
$this->key = $this->resolveSigningKey($params['algorithm']);
}
protected function runBenchmark(): void
{
$void = (new JwtFacade())->issue(
$this->algorithm,
$this->key,
static fn (Builder $builder): Builder => $builder
->identifiedBy('token-1')
->issuedBy('lcobucci.jwt.benchmarks')
->relatedTo('user-1')
->permittedFor('lcobucci.jwt'),
);
}
}
================================================
FILE: tests/Benchmark/ParseTokenBench.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests\Benchmark;
use Lcobucci\Clock\SystemClock;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Validation\Constraint;
use PhpBench\Attributes as Bench;
#[Bench\BeforeMethods('initialize')]
final class ParseTokenBench extends AlgorithmsBench
{
private Signer $algorithm;
private Key $key;
/** @var non-empty-string */
private string $jwt;
/** @param array{algorithm: string} $params */
public function initialize(array $params): void
{
$this->algorithm = $this->resolveAlgorithm($params['algorithm']);
$this->key = $this->resolveVerificationKey($params['algorithm']);
$this->jwt = (new JwtFacade())->issue(
$this->algorithm,
$this->resolveSigningKey($params['algorithm']),
static fn (Builder $builder): Builder => $builder
->identifiedBy('token-1')
->issuedBy('lcobucci.jwt.benchmarks')
->relatedTo('user-1')
->permittedFor('lcobucci.jwt'),
)->toString();
}
protected function runBenchmark(): void
{
$void = (new JwtFacade())->parse(
$this->jwt,
new Constraint\SignedWith($this->algorithm, $this->key),
new Constraint\StrictValidAt(SystemClock::fromSystemTimezone()),
new Constraint\IssuedBy('lcobucci.jwt.benchmarks'),
new Constraint\RelatedTo('user-1'),
new Constraint\PermittedFor('lcobucci.jwt'),
new Constraint\IdentifiedBy('token-1'),
);
}
}
================================================
FILE: tests/Benchmark/Rsa/private.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfgQ+0A4Jz0CWR
5Ac/MdK2ABuCzttNkvBQFl1Hz8q4o8Qct3isdVN5P475dXaNGiN02HElZMO813ue
pDRUSJlAfP8AmZIKkxokxEFIUqspvbCpXAZT82xg5gv5C2JY3aVvNwR7pcLR0Cmv
nJ1AuseqQceKDdEGit1pnoCP6gEeoUQdik97tOl7459V8d3UTpxLozUVlwPU00tg
PmUUek8j1tPAmWx17e6EaoLRkK4QeDyWHPA4eu0hBtLQVVtv2Tf61VNTh+D/cv++
eJQUArC4IuoqdLYFjB2r+bNKdstjuH+qLGhHuOKDf/+RGG5rHBSRHPmJqJCSqBzm
Ad2s0/nPAgMBAAECggEAbWUC9B+EFRIo8kpGfh0ZuyGPvMNKvYWNtB/ikiH9k20e
T+O1q/I78eiZkpXxXQ0UTEs2LsNRS+8uJbvQ+A1irkwMSMkK1J3XTGgdrhCku9gR
ldY7sNA/AKZGh+Q661/42rINLRCe8W+nZ34ui/qOfkLnK9QWDDqpaIsA+bMwWWSD
Fu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l+DqEiWxqg82s
Xt2h+LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5P
lyl21hsFf4L/mHCuoFau7gdsPfHPxxjVOcOpBrQzwQKBgQDdKXGD8PBNclxvrT3l
GhfKBAIBnlGcC9mWejXKEe2dR7H9+nsBn/2dFo7sdf/5IV8ZB661qjZMOMMBZThW
6mTyvD0lHQDNnQ3Z++4gCav9YKyYal42pCd6/VPsjISyeHxQy36fkJp+GSKTOESy
uad0fovE6u9EmWw+npm/xtSrSQKBgQC4oTZ2H5xN/oREXiTh7+PLvwZ89hQhpTKh
JIm4HOncK5uTc4KqzqCtPxtH9y7QObUxnBaa12oPIj3ketR6rcw/Xm8ww43yUdN5
m7aWYq/CpbtqdXlTOEzWJnvPjIyS5TAVagG/Jjz3wRe9EP6F2pHEeVKoBnX3bMHe
lUUnSzukVwKBgAfD1b15Lya49i/hmEO798va+isOYPUmoVwcLFlM6dfU1ZYCPmFf
OatTSG9a8ULQ/iLF10d/k2p3r7kT0beTgTnYjBkKfKW7duoJY2HylPxPcZ/kVCx8
9PnnfRPYFyyg+FRp4Kc/j30P6tvaZOcVh6CadNPUH9R7woYsUV+fXoYpAoGACLDm
DGduhylc9o7r84rEUVn7pzQ6PF83Y+iBZx5NT+TpnOZKF1pErAMVeKzFEl41DlHH
qqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi/7k/vJgGHwHxgPaX2PnvP
+zyEkDERuf+ry4c/Z11Cq9AqC2yeL6kdKT1cYF8CgYEA3PiqvXQN0zwMeE+sBvZg
i289XP9XCQF3VWqPzMKnIgQp7/Tugo6+NZBKCQsMf3HaEGBjTVJs/jcK8+TRXvaK
e+7ZMaQj8VfBdYkssbu0NKDDhjJ+GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h
6ZEpMF6xmujs4qMpPz8aaI4=
-----END RSA PRIVATE KEY-----
================================================
FILE: tests/Benchmark/Rsa/public.key
================================================
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn4EPtAOCc9AlkeQHPzHS
tgAbgs7bTZLwUBZdR8/KuKPEHLd4rHVTeT+O+XV2jRojdNhxJWTDvNd7nqQ0VEiZ
QHz/AJmSCpMaJMRBSFKrKb2wqVwGU/NsYOYL+QtiWN2lbzcEe6XC0dApr5ydQLrH
qkHHig3RBordaZ6Aj+oBHqFEHYpPe7Tpe+OfVfHd1E6cS6M1FZcD1NNLYD5lFHpP
I9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3+tVTU4fg/3L/vniUFAKw
uCLqKnS2BYwdq/mzSnbLY7h/qixoR7jig3//kRhuaxwUkRz5iaiQkqgc5gHdrNP5
zwIDAQAB
-----END PUBLIC KEY-----
================================================
FILE: tests/Benchmark/VerifySignatureBench.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests\Benchmark;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key;
use PhpBench\Attributes as Bench;
#[Bench\BeforeMethods('initialize')]
final class VerifySignatureBench extends AlgorithmsBench
{
private Signer $algorithm;
private Key $key;
/** @var non-empty-string */
private string $signature;
/** @param array{algorithm: string} $params */
public function initialize(array $params): void
{
$this->algorithm = $this->resolveAlgorithm($params['algorithm']);
$this->key = $this->resolveVerificationKey($params['algorithm']);
$this->signature = $this->algorithm->sign(
self::PAYLOAD,
$this->resolveSigningKey($params['algorithm']),
);
}
protected function runBenchmark(): void
{
$void = $this->algorithm->verify($this->signature, self::PAYLOAD, $this->key);
}
}
================================================
FILE: tests/ConfigurationTest.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Decoder;
use Lcobucci\JWT\Encoder;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token\Builder as BuilderImpl;
use Lcobucci\JWT\Token\Parser as ParserImpl;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validator;
use PHPUnit\Framework\Attributes as PHPUnit;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
#[PHPUnit\CoversClass(Configuration::class)]
#[PHPUnit\UsesClass(ChainedFormatter::class)]
#[PHPUnit\UsesClass(InMemory::class)]
#[PHPUnit\UsesClass(BuilderImpl::class)]
#[PHPUnit\UsesClass(ParserImpl::class)]
#[PHPUnit\UsesClass(\Lcobucci\JWT\Validation\Validator::class)]
final class ConfigurationTest extends TestCase
{
private Parser&Stub $parser;
private Signer&Stub $signer;
private Encoder&Stub $encoder;
private Decoder&Stub $decoder;
private Validator&Stub $validator;
private Constraint&Stub $validationConstraints;
#[PHPUnit\Before]
public function createDependencies(): void
{
$this->signer = self::createStub(Signer::class);
$this->encoder = self::createStub(Encoder::class);
$this->decoder = self::createStub(Decoder::class);
$this->parser = self::createStub(Parser::class);
$this->validator = self::createStub(Validator::class);
$this->validationConstraints = self::createStub(Constraint::class);
}
#[PHPUnit\Test]
public function forAsymmetricSignerShouldConfigureSignerAndBothKeys(): void
{
$signingKey = InMemory::plainText('private');
$verificationKey = InMemory::plainText('public');
$config = Configuration::forAsymmetricSigner($this->signer, $signingKey, $verificationKey);
self::assertSame($this->signer, $config->signer());
self::assertSame($signingKey, $config->signingKey());
self::assertSame($verificationKey, $config->verificationKey());
}
#[PHPUnit\Test]
public function forSymmetricSignerShouldConfigureSignerAndBothKeys(): void
{
$key = InMemory::plainText('private');
$config = Configuration::forSymmetricSigner($this->signer, $key);
self::assertSame($this->signer, $config->signer());
self::assertSame($key, $config->signingKey());
self::assertSame($key, $config->verificationKey());
}
#[PHPUnit\Test]
public function builderShouldCreateABuilderWithDefaultEncoderAndClaimFactory(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$builder = $config->builder();
self::assertInstanceOf(BuilderImpl::class, $builder);
self::assertNotEquals(BuilderImpl::new($this->encoder, ChainedFormatter::default()), $builder);
self::assertEquals(BuilderImpl::new(new JoseEncoder(), ChainedFormatter::default()), $builder);
}
#[PHPUnit\Test]
public function builderShouldCreateABuilderWithCustomizedEncoderAndClaimFactory(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
$this->encoder,
);
$builder = $config->builder();
self::assertInstanceOf(BuilderImpl::class, $builder);
self::assertEquals(BuilderImpl::new($this->encoder, ChainedFormatter::default()), $builder);
}
#[PHPUnit\Test]
public function builderShouldUseBuilderFactoryWhenThatIsConfigured(): void
{
$builder = self::createStub(Builder::class);
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$newConfig = $config->withBuilderFactory(
static function () use ($builder): Builder {
return $builder;
},
);
self::assertNotSame($builder, $config->builder());
self::assertSame($builder, $newConfig->builder());
}
#[PHPUnit\Test]
public function parserShouldReturnAParserWithDefaultDecoder(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$parser = $config->parser();
self::assertNotEquals(new ParserImpl($this->decoder), $parser);
}
#[PHPUnit\Test]
public function parserShouldReturnAParserWithCustomizedDecoder(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
decoder: $this->decoder,
);
$parser = $config->parser();
self::assertEquals(new ParserImpl($this->decoder), $parser);
}
#[PHPUnit\Test]
public function parserShouldNotCreateAnInstanceIfItWasConfigured(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$newConfig = $config->withParser($this->parser);
self::assertNotSame($this->parser, $config->parser());
self::assertSame($this->parser, $newConfig->parser());
}
#[PHPUnit\Test]
public function validatorShouldReturnTheDefaultWhenItWasNotConfigured(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$validator = $config->validator();
self::assertNotSame($this->validator, $validator);
}
#[PHPUnit\Test]
public function validatorShouldReturnTheConfiguredValidator(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$newConfig = $config->withValidator($this->validator);
self::assertNotSame($this->validator, $config->validator());
self::assertSame($this->validator, $newConfig->validator());
}
#[PHPUnit\Test]
public function validationConstraintsShouldReturnAnEmptyArrayWhenItWasNotConfigured(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
self::assertSame([], $config->validationConstraints());
}
#[PHPUnit\Test]
public function validationConstraintsShouldReturnTheConfiguredValidator(): void
{
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
$newConfig = $config->withValidationConstraints($this->validationConstraints);
self::assertNotSame([$this->validationConstraints], $config->validationConstraints());
self::assertSame([$this->validationConstraints], $newConfig->validationConstraints());
}
#[PHPUnit\Test]
public function customClaimFormatterCanBeUsed(): void
{
$formatter = self::createStub(ClaimsFormatter::class);
$config = Configuration::forSymmetricSigner(
new KeyDumpSigner(),
InMemory::plainText('private'),
);
self::assertEquals(BuilderImpl::new(new JoseEncoder(), $formatter), $config->builder($formatter));
}
}
================================================
FILE: tests/ES512TokenTest.php
================================================
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Tests;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Encoding\MicrosecondBasedDateConversion;
use Lcobucci\JWT\Encoding\UnifyAudience;
use Lcobucci\JWT\Signer\Ecdsa;
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
use Lcobucci\JWT\Signer\Ecdsa\Sha512;
use Lcobucci\JWT\Signer\InvalidKeyProvided;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\OpenSSL;
use Lcobucci\JWT\SodiumBase64Polyfill;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
use Lcobucci\JWT\Validation\Validator;
use PHPUnit\Framework\Attributes as PHPUnit;
use PHPUnit\Framework\TestCase;
use function assert;
#[PHPUnit\CoversClass(Configuration::class)]
#[PHPUnit\CoversClass(JoseEncoder::class)]
#[PHPUnit\CoversClass(ChainedFormatter::class)]
#[PHPUnit\CoversClass(MicrosecondBasedDateConversion::class)]
#[PHPUnit\CoversClass(UnifyAudience::class)]
#[PHPUnit\CoversClass(Token\Builder::class)]
#[PHPUnit\CoversClass(Token\Parser::class)]
#[PHPUnit\CoversClass(Token\Plain::class)]
#[PHPUnit\CoversClass(Token\DataSet::class)]
#[PHPUnit\CoversClass(Token\Signature::class)]
#[PHPUnit\CoversClass(Ecdsa::class)]
#[PHPUnit\CoversClass(Ecdsa\MultibyteStringConverter::class)]
#[PHPUnit\CoversClass(Sha256::class)]
#[PHPUnit\CoversClass(Sha512::class)]
#[PHPUnit\CoversClass(InMemory::class)]
#[PHPUnit\CoversClass(InvalidKeyProvided::class)]
#[PHPUnit\CoversClass(OpenSSL::class)]
#[PHPUnit\CoversClass(SodiumBase64Polyfill::class)]
#[PHPUnit\CoversClass(Validator::class)]
#[PHPUnit\CoversClass(ConstraintViolation::class)]
#[PHPUnit\CoversClass(SignedWith::class)]
#[PHPUnit\CoversClass(RequiredConstraintsViolated::class)]
class ES512TokenTest extends TestCase
{
use Keys;
private Configuration $config;
#[PHPUnit\Before]
public function createConfiguration(): void
{
$this->config = Configuration::forAsymmetricSigner(
new Sha512(),
static::$ecdsaKeys['private_ec512'],
static::$ecdsaKeys['public_ec512'],
);
}
#[PHPUnit\Test]
public function builderShouldRaiseExceptionWhenKeyIsInvalid(): void
{
$builder = $this->config->builder()
->identifiedBy('1')
->permittedFor('https://client.abc.com')
->issuedBy('https://api.abc.com')
->withClaim('user', ['name' => 'testing', 'email' => 'testing@abc.com']);
$this->expectException(InvalidKeyProvided::class);
$this->expectExceptionMessage('It was not possible to parse your key, reason:');
$void = $builder->getToken($this->config->signer(), InMemory::plainText('testing'));
}
#[PHPUnit\Test]
public function builderShouldRaiseExceptionWhenKeyIsNotEcdsaCompatible(): void
{
$builder = $this->config->builder()
->identifiedBy('1')
->permittedFor('https://client.abc.com')
->issuedBy('https://api.abc.com')
->withClaim('user', ['name' => 'testing', 'email' => 'testing@abc.com']);
$this->expectException(InvalidKeyProvided::class);
$this->expectExceptionMessage('The type of the provided key is not "EC", "RSA" provided');
$void = $builder->getToken($this->config->signer(), static::$rsaKeys['private']);
}
#[PHPUnit\Test]
public function builderCanGenerateAToken(): Token
{
$user = ['name' => 'testing', 'email' => 'testing@abc.com'];
$builder = $this->config->builder();
$token = $builder->identifiedBy('1')
gitextract_h82sgh5a/
├── .composer-require-checker.config.json
├── .gitattributes
├── .github/
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── SECURITY.md
│ └── workflows/
│ ├── backwards-compatibility.yml
│ ├── benchmarks.yml
│ ├── coding-standards.yml
│ ├── composer-json-lint.yml
│ ├── mutation-tests.yml
│ ├── phpunit.yml
│ ├── release-on-milestone-closed.yml
│ └── static-analysis.yml
├── .gitignore
├── .readthedocs.yaml
├── .roave-backward-compatibility-check.json
├── LICENSE
├── Makefile
├── README.md
├── composer.json
├── docs/
│ ├── configuration.md
│ ├── extending-the-library.md
│ ├── index.md
│ ├── installation.md
│ ├── issuing-tokens.md
│ ├── parsing-tokens.md
│ ├── quick-start.md
│ ├── rotating-keys.md
│ ├── supported-algorithms.md
│ ├── upgrading.md
│ └── validating-tokens.md
├── infection.json.dist
├── mkdocs.yml
├── phpbench.json
├── phpcs.xml.dist
├── phpstan.neon.dist
├── phpunit.xml.dist
├── renovate.json
├── src/
│ ├── Builder.php
│ ├── ClaimsFormatter.php
│ ├── Configuration.php
│ ├── Decoder.php
│ ├── Encoder.php
│ ├── Encoding/
│ │ ├── CannotDecodeContent.php
│ │ ├── CannotEncodeContent.php
│ │ ├── ChainedFormatter.php
│ │ ├── JoseEncoder.php
│ │ ├── MicrosecondBasedDateConversion.php
│ │ ├── UnifyAudience.php
│ │ └── UnixTimestampDates.php
│ ├── Exception.php
│ ├── JwtFacade.php
│ ├── Parser.php
│ ├── Signer/
│ │ ├── Blake2b.php
│ │ ├── CannotSignPayload.php
│ │ ├── Ecdsa/
│ │ │ ├── ConversionFailed.php
│ │ │ ├── MultibyteStringConverter.php
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ ├── Sha512.php
│ │ │ └── SignatureConverter.php
│ │ ├── Ecdsa.php
│ │ ├── Eddsa.php
│ │ ├── Hmac/
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ └── Sha512.php
│ │ ├── Hmac.php
│ │ ├── InvalidKeyProvided.php
│ │ ├── Key/
│ │ │ ├── FileCouldNotBeRead.php
│ │ │ └── InMemory.php
│ │ ├── Key.php
│ │ ├── OpenSSL.php
│ │ ├── Rsa/
│ │ │ ├── Sha256.php
│ │ │ ├── Sha384.php
│ │ │ └── Sha512.php
│ │ └── Rsa.php
│ ├── Signer.php
│ ├── SodiumBase64Polyfill.php
│ ├── Token/
│ │ ├── Builder.php
│ │ ├── DataSet.php
│ │ ├── InvalidTokenStructure.php
│ │ ├── Parser.php
│ │ ├── Plain.php
│ │ ├── RegisteredClaimGiven.php
│ │ ├── RegisteredClaims.php
│ │ ├── Signature.php
│ │ └── UnsupportedHeaderFound.php
│ ├── Token.php
│ ├── UnencryptedToken.php
│ ├── Validation/
│ │ ├── Constraint/
│ │ │ ├── CannotValidateARegisteredClaim.php
│ │ │ ├── HasClaim.php
│ │ │ ├── HasClaimWithValue.php
│ │ │ ├── IdentifiedBy.php
│ │ │ ├── IssuedBy.php
│ │ │ ├── LeewayCannotBeNegative.php
│ │ │ ├── LooseValidAt.php
│ │ │ ├── PermittedFor.php
│ │ │ ├── RelatedTo.php
│ │ │ ├── SignedWith.php
│ │ │ ├── SignedWithOneInSet.php
│ │ │ ├── SignedWithUntilDate.php
│ │ │ └── StrictValidAt.php
│ │ ├── Constraint.php
│ │ ├── ConstraintViolation.php
│ │ ├── NoConstraintsGiven.php
│ │ ├── RequiredConstraintsViolated.php
│ │ ├── SignedWith.php
│ │ ├── ValidAt.php
│ │ └── Validator.php
│ └── Validator.php
└── tests/
├── Benchmark/
│ ├── AlgorithmsBench.php
│ ├── CreateSignatureBench.php
│ ├── Ecdsa/
│ │ ├── private-256.key
│ │ ├── private-384.key
│ │ ├── private-521.key
│ │ ├── public-256.key
│ │ ├── public-384.key
│ │ └── public-521.key
│ ├── IssueTokenBench.php
│ ├── ParseTokenBench.php
│ ├── Rsa/
│ │ ├── private.key
│ │ └── public.key
│ └── VerifySignatureBench.php
├── ConfigurationTest.php
├── ES512TokenTest.php
├── EcdsaTokenTest.php
├── EddsaTokenTest.php
├── Encoding/
│ ├── ChainedFormatterTest.php
│ ├── JoseEncoderTest.php
│ ├── MicrosecondBasedDateConversionTest.php
│ ├── UnifyAudienceTest.php
│ └── UnixTimestampDatesTest.php
├── HmacTokenTest.php
├── JwtFacadeTest.php
├── KeyDumpSigner.php
├── Keys.php
├── MaliciousTamperingPreventionTest.php
├── RFC6978VectorTest.php
├── RsaTokenTest.php
├── Signer/
│ ├── Blake2bTest.php
│ ├── Ecdsa/
│ │ ├── EcdsaTestCase.php
│ │ ├── MultibyteStringConverterTest.php
│ │ ├── Sha256Test.php
│ │ ├── Sha384Test.php
│ │ └── Sha512Test.php
│ ├── EddsaTest.php
│ ├── FakeSigner.php
│ ├── Hmac/
│ │ ├── HmacTestCase.php
│ │ ├── Sha256Test.php
│ │ ├── Sha384Test.php
│ │ └── Sha512Test.php
│ ├── Key/
│ │ ├── InMemoryTest.php
│ │ ├── empty.pem
│ │ └── test.pem
│ └── Rsa/
│ ├── KeyValidationSigner.php
│ ├── KeyValidationTest.php
│ ├── RsaTestCase.php
│ ├── Sha256Test.php
│ ├── Sha384Test.php
│ └── Sha512Test.php
├── SodiumBase64PolyfillTest.php
├── TimeFractionPrecisionTest.php
├── Token/
│ ├── BuilderTest.php
│ ├── DataSetTest.php
│ ├── ParserTest.php
│ ├── PlainTest.php
│ └── SignatureTest.php
├── UnsignedTokenTest.php
├── UnsupportedParser.php
├── Validation/
│ ├── Constraint/
│ │ ├── ConstraintTestCase.php
│ │ ├── HasClaimTest.php
│ │ ├── HasClaimWithValueTest.php
│ │ ├── IdentifiedByTest.php
│ │ ├── IssuedByTest.php
│ │ ├── LooseValidAtTest.php
│ │ ├── PermittedForTest.php
│ │ ├── RelatedToTest.php
│ │ ├── SignedWithOneInSetTest.php
│ │ ├── SignedWithTest.php
│ │ ├── SignedWithUntilDateTest.php
│ │ ├── StrictValidAtTest.php
│ │ └── ValidAtTestCase.php
│ ├── ConstraintViolationTest.php
│ ├── RequiredConstraintsViolatedTest.php
│ └── ValidatorTest.php
└── _keys/
├── ecdsa/
│ ├── private.key
│ ├── private2.key
│ ├── private_ec384.key
│ ├── private_ec512.key
│ ├── public1.key
│ ├── public2.key
│ ├── public2_ec512.key
│ ├── public3.key
│ ├── public_ec384.key
│ └── public_ec512.key
└── rsa/
├── encrypted-private.key
├── encrypted-public.key
├── private.key
├── private_512.key
├── public.key
└── public_512.key
SYMBOL INDEX (784 symbols across 137 files)
FILE: src/Builder.php
type Builder (line 16) | interface Builder
method permittedFor (line 23) | #[NoDiscard]
method expiresAt (line 29) | #[NoDiscard]
method identifiedBy (line 37) | #[NoDiscard]
method issuedAt (line 43) | #[NoDiscard]
method issuedBy (line 51) | #[NoDiscard]
method canOnlyBeUsedAfter (line 57) | #[NoDiscard]
method relatedTo (line 65) | #[NoDiscard]
method withHeader (line 73) | #[NoDiscard]
method withClaim (line 83) | #[NoDiscard]
method getToken (line 94) | #[NoDiscard]
FILE: src/ClaimsFormatter.php
type ClaimsFormatter (line 8) | interface ClaimsFormatter
method formatClaims (line 15) | #[NoDiscard]
FILE: src/Configuration.php
class Configuration (line 19) | final readonly class Configuration
method __construct (line 31) | private function __construct(
method forAsymmetricSigner (line 53) | #[NoDiscard]
method forSymmetricSigner (line 73) | #[NoDiscard]
method withBuilderFactory (line 93) | #[NoDiscard]
method builder (line 109) | public function builder(?ClaimsFormatter $claimFormatter = null): Builder
method parser (line 114) | public function parser(): Parser
method withParser (line 119) | #[NoDiscard]
method signer (line 135) | public function signer(): Signer
method signingKey (line 140) | public function signingKey(): Key
method verificationKey (line 145) | public function verificationKey(): Key
method validator (line 150) | public function validator(): Validator
method withValidator (line 155) | #[NoDiscard]
method validationConstraints (line 172) | public function validationConstraints(): array
method withValidationConstraints (line 177) | #[NoDiscard]
FILE: src/Decoder.php
type Decoder (line 9) | interface Decoder
method jsonDecode (line 18) | #[NoDiscard]
method base64UrlDecode (line 30) | #[NoDiscard]
FILE: src/Encoder.php
type Encoder (line 9) | interface Encoder
method jsonEncode (line 18) | #[NoDiscard]
method base64UrlEncode (line 28) | #[NoDiscard]
FILE: src/Encoding/CannotDecodeContent.php
class CannotDecodeContent (line 10) | final class CannotDecodeContent extends RuntimeException implements Exce...
method jsonIssues (line 12) | public static function jsonIssues(JsonException $previous): self
method invalidBase64String (line 17) | public static function invalidBase64String(): self
FILE: src/Encoding/CannotEncodeContent.php
class CannotEncodeContent (line 10) | final class CannotEncodeContent extends RuntimeException implements Exce...
method jsonIssues (line 12) | public static function jsonIssues(JsonException $previous): self
FILE: src/Encoding/ChainedFormatter.php
class ChainedFormatter (line 8) | final readonly class ChainedFormatter implements ClaimsFormatter
method __construct (line 13) | public function __construct(ClaimsFormatter ...$formatters)
method default (line 18) | public static function default(): self
method withUnixTimestampDates (line 23) | public static function withUnixTimestampDates(): self
method formatClaims (line 29) | public function formatClaims(array $claims): array
FILE: src/Encoding/JoseEncoder.php
class JoseEncoder (line 21) | final readonly class JoseEncoder implements Encoder, Decoder
method jsonEncode (line 23) | public function jsonEncode(mixed $data): string
method jsonDecode (line 32) | public function jsonDecode(string $json): mixed
method base64UrlEncode (line 41) | public function base64UrlEncode(string $data): string
method base64UrlDecode (line 49) | public function base64UrlDecode(string $data): string
FILE: src/Encoding/MicrosecondBasedDateConversion.php
class MicrosecondBasedDateConversion (line 12) | final readonly class MicrosecondBasedDateConversion implements ClaimsFor...
method formatClaims (line 15) | public function formatClaims(array $claims): array
method convertDate (line 28) | private function convertDate(DateTimeImmutable $date): int|float
FILE: src/Encoding/UnifyAudience.php
class UnifyAudience (line 13) | final readonly class UnifyAudience implements ClaimsFormatter
method formatClaims (line 16) | public function formatClaims(array $claims): array
FILE: src/Encoding/UnixTimestampDates.php
class UnixTimestampDates (line 12) | final readonly class UnixTimestampDates implements ClaimsFormatter
method formatClaims (line 15) | public function formatClaims(array $claims): array
method convertDate (line 28) | private function convertDate(DateTimeImmutable $date): int
FILE: src/Exception.php
type Exception (line 8) | interface Exception extends Throwable
FILE: src/JwtFacade.php
class JwtFacade (line 20) | final readonly class JwtFacade
method __construct (line 24) | public function __construct(
method issue (line 37) | #[NoDiscard]
method parse (line 55) | #[NoDiscard]
FILE: src/Parser.php
type Parser (line 11) | interface Parser
method parse (line 22) | #[NoDiscard]
FILE: src/Signer.php
type Signer (line 13) | interface Signer
method algorithmId (line 20) | public function algorithmId(): string;
method sign (line 33) | #[NoDiscard]
method verify (line 45) | #[NoDiscard]
FILE: src/Signer/Blake2b.php
class Blake2b (line 12) | final readonly class Blake2b implements Signer
method algorithmId (line 16) | public function algorithmId(): string
method sign (line 21) | public function sign(string $payload, Key $key): string
method verify (line 32) | public function verify(string $expected, string $payload, Key $key): bool
FILE: src/Signer/CannotSignPayload.php
class CannotSignPayload (line 9) | final class CannotSignPayload extends InvalidArgumentException implement...
method errorHappened (line 11) | public static function errorHappened(string $error): self
FILE: src/Signer/Ecdsa.php
class Ecdsa (line 11) | abstract readonly class Ecdsa extends OpenSSL
method __construct (line 13) | public function __construct(
method sign (line 18) | final public function sign(string $payload, Key $key): string
method verify (line 26) | final public function verify(string $expected, string $payload, Key $k...
method guardAgainstIncompatibleKey (line 36) | final protected function guardAgainstIncompatibleKey(int $type, int $l...
method expectedKeyLength (line 57) | abstract public function expectedKeyLength(): int;
method pointLength (line 66) | abstract public function pointLength(): int;
FILE: src/Signer/Ecdsa/ConversionFailed.php
class ConversionFailed (line 9) | final class ConversionFailed extends InvalidArgumentException implements...
method invalidLength (line 11) | public static function invalidLength(): self
method incorrectStartSequence (line 16) | public static function incorrectStartSequence(): self
method integerExpected (line 21) | public static function integerExpected(): self
FILE: src/Signer/Ecdsa/MultibyteStringConverter.php
class MultibyteStringConverter (line 34) | final readonly class MultibyteStringConverter implements SignatureConverter
method toAsn1 (line 44) | public function toAsn1(string $points, int $length): string
method octetLength (line 73) | private static function octetLength(string $data): int
method preparePositiveInteger (line 78) | private static function preparePositiveInteger(string $data): string
method fromAsn1 (line 94) | public function fromAsn1(string $signature, int $length): string
method readAsn1Content (line 118) | private static function readAsn1Content(string $message, int &$positio...
method readAsn1Integer (line 126) | private static function readAsn1Integer(string $message, int &$positio...
method retrievePositiveInteger (line 137) | private static function retrievePositiveInteger(string $data): string
FILE: src/Signer/Ecdsa/Sha256.php
class Sha256 (line 10) | final readonly class Sha256 extends Ecdsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
method pointLength (line 22) | public function pointLength(): int
method expectedKeyLength (line 27) | public function expectedKeyLength(): int
FILE: src/Signer/Ecdsa/Sha384.php
class Sha384 (line 10) | final readonly class Sha384 extends Ecdsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
method pointLength (line 22) | public function pointLength(): int
method expectedKeyLength (line 27) | public function expectedKeyLength(): int
FILE: src/Signer/Ecdsa/Sha512.php
class Sha512 (line 10) | final readonly class Sha512 extends Ecdsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
method pointLength (line 22) | public function pointLength(): int
method expectedKeyLength (line 27) | public function expectedKeyLength(): int
FILE: src/Signer/Ecdsa/SignatureConverter.php
type SignatureConverter (line 19) | interface SignatureConverter
method fromAsn1 (line 28) | public function fromAsn1(string $signature, int $length): string;
method toAsn1 (line 39) | public function toAsn1(string $points, int $length): string;
FILE: src/Signer/Eddsa.php
class Eddsa (line 12) | final readonly class Eddsa implements Signer
method algorithmId (line 14) | public function algorithmId(): string
method sign (line 19) | public function sign(string $payload, Key $key): string
method verify (line 28) | public function verify(string $expected, string $payload, Key $key): bool
FILE: src/Signer/Hmac.php
class Hmac (line 12) | abstract readonly class Hmac implements Signer
method sign (line 14) | final public function sign(string $payload, Key $key): string
method verify (line 26) | final public function verify(string $expected, string $payload, Key $k...
method algorithm (line 36) | abstract public function algorithm(): string;
method minimumBitsLengthForKey (line 43) | abstract public function minimumBitsLengthForKey(): int;
FILE: src/Signer/Hmac/Sha256.php
class Sha256 (line 8) | final readonly class Sha256 extends Hmac
method algorithmId (line 10) | public function algorithmId(): string
method algorithm (line 15) | public function algorithm(): string
method minimumBitsLengthForKey (line 20) | public function minimumBitsLengthForKey(): int
FILE: src/Signer/Hmac/Sha384.php
class Sha384 (line 8) | final readonly class Sha384 extends Hmac
method algorithmId (line 10) | public function algorithmId(): string
method algorithm (line 15) | public function algorithm(): string
method minimumBitsLengthForKey (line 20) | public function minimumBitsLengthForKey(): int
FILE: src/Signer/Hmac/Sha512.php
class Sha512 (line 8) | final readonly class Sha512 extends Hmac
method algorithmId (line 10) | public function algorithmId(): string
method algorithm (line 15) | public function algorithm(): string
method minimumBitsLengthForKey (line 20) | public function minimumBitsLengthForKey(): int
FILE: src/Signer/InvalidKeyProvided.php
class InvalidKeyProvided (line 9) | final class InvalidKeyProvided extends InvalidArgumentException implemen...
method cannotBeParsed (line 11) | public static function cannotBeParsed(string $details): self
method incompatibleKeyType (line 20) | public static function incompatibleKeyType(string $expectedType, strin...
method incompatibleKeyLength (line 29) | public static function incompatibleKeyLength(int $expectedLength, int ...
method cannotBeEmpty (line 37) | public static function cannotBeEmpty(): self
method tooShort (line 42) | public static function tooShort(int $expectedLength, int $actualLength...
FILE: src/Signer/Key.php
type Key (line 6) | interface Key
method contents (line 9) | public function contents(): string;
method passphrase (line 11) | public function passphrase(): string;
FILE: src/Signer/Key/FileCouldNotBeRead.php
class FileCouldNotBeRead (line 10) | final class FileCouldNotBeRead extends InvalidArgumentException implemen...
method onPath (line 13) | public static function onPath(string $path, ?Throwable $cause = null):...
FILE: src/Signer/Key/InMemory.php
class InMemory (line 16) | final readonly class InMemory implements Key
method __construct (line 19) | private function __construct(
method plainText (line 28) | public static function plainText(
method base64Encoded (line 40) | public static function base64Encoded(
method file (line 61) | public static function file(
method guardAgainstEmptyKey (line 82) | private static function guardAgainstEmptyKey(string $contents): void
method contents (line 89) | public function contents(): string
method passphrase (line 94) | public function passphrase(): string
FILE: src/Signer/OpenSSL.php
class OpenSSL (line 27) | abstract readonly class OpenSSL implements Signer
method createSignature (line 42) | final protected function createSignature(
method getPrivateKey (line 58) | private function getPrivateKey(
method verifySignature (line 65) | final protected function verifySignature(
method getPublicKey (line 77) | private function getPublicKey(Key $key): OpenSSLAsymmetricKey
method validateKey (line 87) | private function validateKey(OpenSSLAsymmetricKey|bool $key): OpenSSLA...
method fullOpenSSLErrorString (line 106) | private function fullOpenSSLErrorString(): string
method guardAgainstIncompatibleKey (line 118) | abstract protected function guardAgainstIncompatibleKey(int $type, int...
method algorithm (line 125) | abstract public function algorithm(): int;
FILE: src/Signer/Rsa.php
class Rsa (line 8) | abstract readonly class Rsa extends OpenSSL
method sign (line 12) | final public function sign(string $payload, Key $key): string
method verify (line 17) | final public function verify(string $expected, string $payload, Key $k...
method guardAgainstIncompatibleKey (line 22) | final protected function guardAgainstIncompatibleKey(int $type, int $l...
FILE: src/Signer/Rsa/Sha256.php
class Sha256 (line 10) | final readonly class Sha256 extends Rsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
FILE: src/Signer/Rsa/Sha384.php
class Sha384 (line 10) | final readonly class Sha384 extends Rsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
FILE: src/Signer/Rsa/Sha512.php
class Sha512 (line 10) | final readonly class Sha512 extends Rsa
method algorithmId (line 12) | public function algorithmId(): string
method algorithm (line 17) | public function algorithm(): int
FILE: src/SodiumBase64Polyfill.php
class SodiumBase64Polyfill (line 19) | final readonly class SodiumBase64Polyfill
method bin2base64 (line 27) | public static function bin2base64(string $decoded, int $variant): string
method bin2base64Fallback (line 37) | public static function bin2base64Fallback(string $decoded, int $varian...
method base642bin (line 63) | public static function base642bin(string $encoded, int $variant): string
method base642binFallback (line 81) | public static function base642binFallback(string $encoded, int $varian...
FILE: src/Token.php
type Token (line 11) | interface Token
method headers (line 16) | public function headers(): DataSet;
method isPermittedFor (line 23) | public function isPermittedFor(string $audience): bool;
method isIdentifiedBy (line 30) | public function isIdentifiedBy(string $id): bool;
method isRelatedTo (line 37) | public function isRelatedTo(string $subject): bool;
method hasBeenIssuedBy (line 44) | public function hasBeenIssuedBy(string ...$issuers): bool;
method hasBeenIssuedBefore (line 49) | public function hasBeenIssuedBefore(DateTimeInterface $now): bool;
method isMinimumTimeBefore (line 54) | public function isMinimumTimeBefore(DateTimeInterface $now): bool;
method isExpired (line 59) | public function isExpired(DateTimeInterface $now): bool;
method toString (line 66) | #[NoDiscard]
FILE: src/Token/Builder.php
class Builder (line 21) | final readonly class Builder implements BuilderInterface
method __construct (line 27) | private function __construct(
method new (line 35) | #[NoDiscard]
method permittedFor (line 41) | public function permittedFor(string ...$audiences): BuilderInterface
method expiresAt (line 49) | public function expiresAt(DateTimeImmutable $expiration): BuilderInter...
method identifiedBy (line 54) | public function identifiedBy(string $id): BuilderInterface
method issuedAt (line 59) | public function issuedAt(DateTimeImmutable $issuedAt): BuilderInterface
method issuedBy (line 64) | public function issuedBy(string $issuer): BuilderInterface
method canOnlyBeUsedAfter (line 69) | public function canOnlyBeUsedAfter(DateTimeImmutable $notBefore): Buil...
method relatedTo (line 74) | public function relatedTo(string $subject): BuilderInterface
method withHeader (line 79) | public function withHeader(string $name, mixed $value): BuilderInterface
method withClaim (line 92) | public function withClaim(string $name, mixed $value): BuilderInterface
method newWithClaim (line 102) | private function newWithClaim(string $name, mixed $value): BuilderInte...
method encode (line 120) | private function encode(array $items): string
method getToken (line 127) | public function getToken(Signer $signer, Key $key): UnencryptedToken
FILE: src/Token/DataSet.php
class DataSet (line 8) | final readonly class DataSet
method __construct (line 11) | public function __construct(private array $data, private string $encoded)
method get (line 16) | public function get(string $name, mixed $default = null): mixed
method has (line 22) | public function has(string $name): bool
method all (line 28) | public function all(): array
method toString (line 33) | public function toString(): string
FILE: src/Token/InvalidTokenStructure.php
class InvalidTokenStructure (line 9) | final class InvalidTokenStructure extends InvalidArgumentException imple...
method missingOrNotEnoughSeparators (line 11) | public static function missingOrNotEnoughSeparators(): self
method missingHeaderPart (line 16) | public static function missingHeaderPart(): self
method missingClaimsPart (line 21) | public static function missingClaimsPart(): self
method missingSignaturePart (line 26) | public static function missingSignaturePart(): self
method arrayExpected (line 32) | public static function arrayExpected(string $part): self
method dateIsNotParseable (line 37) | public static function dateIsNotParseable(string $value): self
FILE: src/Token/Parser.php
class Parser (line 18) | final readonly class Parser implements ParserInterface
method __construct (line 22) | public function __construct(private Decoder $decoder)
method parse (line 26) | public function parse(string $jwt): TokenInterface
method splitJwt (line 60) | private function splitJwt(string $jwt): array
method parseHeader (line 81) | private function parseHeader(string $data): array
method parseClaims (line 111) | private function parseClaims(string $data): array
method guardAgainstEmptyStringKeys (line 142) | private function guardAgainstEmptyStringKeys(array $array, string $par...
method convertDate (line 152) | private function convertDate(int|float|string $timestamp): DateTimeImm...
method parseSignature (line 174) | private function parseSignature(string $data): Signature
FILE: src/Token/Plain.php
class Plain (line 11) | final readonly class Plain implements UnencryptedToken
method __construct (line 13) | public function __construct(
method headers (line 20) | public function headers(): DataSet
method claims (line 25) | public function claims(): DataSet
method signature (line 30) | public function signature(): Signature
method payload (line 35) | public function payload(): string
method isPermittedFor (line 40) | public function isPermittedFor(string $audience): bool
method isIdentifiedBy (line 45) | public function isIdentifiedBy(string $id): bool
method isRelatedTo (line 50) | public function isRelatedTo(string $subject): bool
method hasBeenIssuedBy (line 55) | public function hasBeenIssuedBy(string ...$issuers): bool
method hasBeenIssuedBefore (line 60) | public function hasBeenIssuedBefore(DateTimeInterface $now): bool
method isMinimumTimeBefore (line 65) | public function isMinimumTimeBefore(DateTimeInterface $now): bool
method isExpired (line 70) | public function isExpired(DateTimeInterface $now): bool
method toString (line 79) | public function toString(): string
FILE: src/Token/RegisteredClaimGiven.php
class RegisteredClaimGiven (line 11) | final class RegisteredClaimGiven extends InvalidArgumentException implem...
method forClaim (line 17) | public static function forClaim(string $name): self
FILE: src/Token/RegisteredClaims.php
type RegisteredClaims (line 11) | interface RegisteredClaims
FILE: src/Token/Signature.php
class Signature (line 6) | final readonly class Signature
method __construct (line 12) | public function __construct(private string $hash, private string $enco...
method hash (line 17) | public function hash(): string
method toString (line 27) | public function toString(): string
FILE: src/Token/UnsupportedHeaderFound.php
class UnsupportedHeaderFound (line 9) | final class UnsupportedHeaderFound extends InvalidArgumentException impl...
method encryption (line 11) | public static function encryption(): self
FILE: src/UnencryptedToken.php
type UnencryptedToken (line 9) | interface UnencryptedToken extends Token
method claims (line 14) | public function claims(): DataSet;
method signature (line 19) | public function signature(): Signature;
method payload (line 26) | public function payload(): string;
FILE: src/Validation/Constraint.php
type Constraint (line 8) | interface Constraint
method assert (line 11) | public function assert(Token $token): void;
FILE: src/Validation/Constraint/CannotValidateARegisteredClaim.php
class CannotValidateARegisteredClaim (line 9) | final class CannotValidateARegisteredClaim extends InvalidArgumentExcept...
method create (line 12) | public static function create(string $claim): self
FILE: src/Validation/Constraint/HasClaim.php
class HasClaim (line 13) | final readonly class HasClaim implements Constraint
method __construct (line 16) | public function __construct(private string $claim)
method assert (line 23) | public function assert(Token $token): void
FILE: src/Validation/Constraint/HasClaimWithValue.php
class HasClaimWithValue (line 13) | final readonly class HasClaimWithValue implements Constraint
method __construct (line 16) | public function __construct(private string $claim, private mixed $expe...
method assert (line 23) | public function assert(Token $token): void
FILE: src/Validation/Constraint/IdentifiedBy.php
class IdentifiedBy (line 10) | final readonly class IdentifiedBy implements Constraint
method __construct (line 13) | public function __construct(private string $id)
method assert (line 17) | public function assert(Token $token): void
FILE: src/Validation/Constraint/IssuedBy.php
class IssuedBy (line 10) | final readonly class IssuedBy implements Constraint
method __construct (line 16) | public function __construct(string ...$issuers)
method assert (line 21) | public function assert(Token $token): void
FILE: src/Validation/Constraint/LeewayCannotBeNegative.php
class LeewayCannotBeNegative (line 9) | final class LeewayCannotBeNegative extends InvalidArgumentException impl...
method create (line 11) | public static function create(): self
FILE: src/Validation/Constraint/LooseValidAt.php
class LooseValidAt (line 13) | final readonly class LooseValidAt implements ValidAtInterface
method __construct (line 17) | public function __construct(private Clock $clock, ?DateInterval $leewa...
method guardLeeway (line 22) | private function guardLeeway(?DateInterval $leeway): DateInterval
method assert (line 35) | public function assert(Token $token): void
method assertExpiration (line 45) | private function assertExpiration(Token $token, DateTimeInterface $now...
method assertMinimumTime (line 53) | private function assertMinimumTime(Token $token, DateTimeInterface $no...
method assertIssueTime (line 61) | private function assertIssueTime(Token $token, DateTimeInterface $now)...
FILE: src/Validation/Constraint/PermittedFor.php
class PermittedFor (line 10) | final readonly class PermittedFor implements Constraint
method __construct (line 13) | public function __construct(private string $audience)
method assert (line 17) | public function assert(Token $token): void
FILE: src/Validation/Constraint/RelatedTo.php
class RelatedTo (line 10) | final readonly class RelatedTo implements Constraint
method __construct (line 13) | public function __construct(private string $subject)
method assert (line 17) | public function assert(Token $token): void
FILE: src/Validation/Constraint/SignedWith.php
class SignedWith (line 12) | final readonly class SignedWith implements SignedWithInterface
method __construct (line 14) | public function __construct(private Signer $signer, private Signer\Key...
method assert (line 18) | public function assert(Token $token): void
FILE: src/Validation/Constraint/SignedWithOneInSet.php
class SignedWithOneInSet (line 12) | final readonly class SignedWithOneInSet implements SignedWithInterface
method __construct (line 17) | public function __construct(SignedWithUntilDate ...$constraints)
method assert (line 22) | public function assert(Token $token): void
FILE: src/Validation/Constraint/SignedWithUntilDate.php
class SignedWithUntilDate (line 14) | final readonly class SignedWithUntilDate implements SignedWithInterface
method __construct (line 19) | public function __construct(
method assert (line 35) | public function assert(Token $token): void
FILE: src/Validation/Constraint/StrictValidAt.php
class StrictValidAt (line 14) | final readonly class StrictValidAt implements ValidAtInterface
method __construct (line 18) | public function __construct(private Clock $clock, ?DateInterval $leewa...
method guardLeeway (line 23) | private function guardLeeway(?DateInterval $leeway): DateInterval
method assert (line 36) | public function assert(Token $token): void
method assertExpiration (line 50) | private function assertExpiration(UnencryptedToken $token, DateTimeInt...
method assertMinimumTime (line 62) | private function assertMinimumTime(UnencryptedToken $token, DateTimeIn...
method assertIssueTime (line 74) | private function assertIssueTime(UnencryptedToken $token, DateTimeInte...
FILE: src/Validation/ConstraintViolation.php
class ConstraintViolation (line 9) | final class ConstraintViolation extends RuntimeException implements Exce...
method __construct (line 12) | public function __construct(
method error (line 20) | public static function error(string $message, Constraint $constraint):...
FILE: src/Validation/NoConstraintsGiven.php
class NoConstraintsGiven (line 9) | final class NoConstraintsGiven extends RuntimeException implements Excep...
FILE: src/Validation/RequiredConstraintsViolated.php
class RequiredConstraintsViolated (line 12) | final class RequiredConstraintsViolated extends RuntimeException impleme...
method __construct (line 15) | public function __construct(
method fromViolations (line 22) | public static function fromViolations(ConstraintViolation ...$violatio...
method buildMessage (line 28) | private static function buildMessage(array $violations): string
method violations (line 44) | public function violations(): array
FILE: src/Validation/SignedWith.php
type SignedWith (line 6) | interface SignedWith extends Constraint
FILE: src/Validation/ValidAt.php
type ValidAt (line 6) | interface ValidAt extends Constraint
FILE: src/Validation/Validator.php
class Validator (line 8) | final readonly class Validator implements \Lcobucci\JWT\Validator
method assert (line 10) | public function assert(Token $token, Constraint ...$constraints): void
method checkConstraint (line 28) | private function checkConstraint(
method validate (line 40) | public function validate(Token $token, Constraint ...$constraints): bool
FILE: src/Validator.php
type Validator (line 11) | interface Validator
method assert (line 17) | public function assert(Token $token, Constraint ...$constraints): void;
method validate (line 20) | #[NoDiscard]
FILE: tests/Benchmark/AlgorithmsBench.php
class AlgorithmsBench (line 12) | #[Bench\Iterations(5)]
method hmac (line 29) | #[Bench\Subject]
method hmacAlgorithms (line 38) | public function hmacAlgorithms(): iterable
method rsa (line 43) | #[Bench\Subject]
method rsaAlgorithms (line 52) | public function rsaAlgorithms(): iterable
method ecdsa (line 57) | #[Bench\Subject]
method ecdsaAlgorithms (line 66) | public function ecdsaAlgorithms(): iterable
method eddsa (line 71) | #[Bench\Subject]
method eddsaAlgorithms (line 80) | public function eddsaAlgorithms(): iterable
method blake2b (line 85) | #[Bench\Subject]
method blake2bAlgorithms (line 94) | public function blake2bAlgorithms(): iterable
method runBenchmark (line 99) | abstract protected function runBenchmark(): void;
method resolveAlgorithm (line 101) | protected function resolveAlgorithm(string $name): Signer
method resolveSigningKey (line 119) | protected function resolveSigningKey(string $name): Key
method resolveVerificationKey (line 139) | protected function resolveVerificationKey(string $name): Key
method iterateAlgorithms (line 153) | private function iterateAlgorithms(string $family): iterable
FILE: tests/Benchmark/CreateSignatureBench.php
class CreateSignatureBench (line 10) | #[Bench\BeforeMethods('initialize')]
method initialize (line 17) | public function initialize(array $params): void
method runBenchmark (line 23) | protected function runBenchmark(): void
FILE: tests/Benchmark/IssueTokenBench.php
class IssueTokenBench (line 12) | #[Bench\BeforeMethods('initialize')]
method initialize (line 19) | public function initialize(array $params): void
method runBenchmark (line 25) | protected function runBenchmark(): void
FILE: tests/Benchmark/ParseTokenBench.php
class ParseTokenBench (line 14) | #[Bench\BeforeMethods('initialize')]
method initialize (line 23) | public function initialize(array $params): void
method runBenchmark (line 39) | protected function runBenchmark(): void
FILE: tests/Benchmark/VerifySignatureBench.php
class VerifySignatureBench (line 10) | #[Bench\BeforeMethods('initialize')]
method initialize (line 19) | public function initialize(array $params): void
method runBenchmark (line 30) | protected function runBenchmark(): void
FILE: tests/ConfigurationTest.php
class ConfigurationTest (line 24) | #[PHPUnit\CoversClass(Configuration::class)]
method createDependencies (line 39) | #[PHPUnit\Before]
method forAsymmetricSignerShouldConfigureSignerAndBothKeys (line 50) | #[PHPUnit\Test]
method forSymmetricSignerShouldConfigureSignerAndBothKeys (line 63) | #[PHPUnit\Test]
method builderShouldCreateABuilderWithDefaultEncoderAndClaimFactory (line 74) | #[PHPUnit\Test]
method builderShouldCreateABuilderWithCustomizedEncoderAndClaimFactory (line 88) | #[PHPUnit\Test]
method builderShouldUseBuilderFactoryWhenThatIsConfigured (line 102) | #[PHPUnit\Test]
method parserShouldReturnAParserWithDefaultDecoder (line 120) | #[PHPUnit\Test]
method parserShouldReturnAParserWithCustomizedDecoder (line 132) | #[PHPUnit\Test]
method parserShouldNotCreateAnInstanceIfItWasConfigured (line 145) | #[PHPUnit\Test]
method validatorShouldReturnTheDefaultWhenItWasNotConfigured (line 158) | #[PHPUnit\Test]
method validatorShouldReturnTheConfiguredValidator (line 170) | #[PHPUnit\Test]
method validationConstraintsShouldReturnAnEmptyArrayWhenItWasNotConfigured (line 183) | #[PHPUnit\Test]
method validationConstraintsShouldReturnTheConfiguredValidator (line 194) | #[PHPUnit\Test]
method customClaimFormatterCanBeUsed (line 207) | #[PHPUnit\Test]
FILE: tests/ES512TokenTest.php
class ES512TokenTest (line 28) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 56) | #[PHPUnit\Before]
method builderShouldRaiseExceptionWhenKeyIsInvalid (line 66) | #[PHPUnit\Test]
method builderShouldRaiseExceptionWhenKeyIsNotEcdsaCompatible (line 81) | #[PHPUnit\Test]
method builderCanGenerateAToken (line 96) | #[PHPUnit\Test]
method parserCanReadAToken (line 122) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight (line 133) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenAlgorithmIsDifferent (line 149) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotEcdsaCompatible (line 165) | #[PHPUnit\Test]
method signatureValidationShouldSucceedWhenKeyIsRight (line 178) | #[PHPUnit\Test]
FILE: tests/EcdsaTokenTest.php
class EcdsaTokenTest (line 31) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 59) | #[PHPUnit\Before]
method builderShouldRaiseExceptionWhenKeyIsInvalid (line 69) | #[PHPUnit\Test]
method builderShouldRaiseExceptionWhenKeyIsNotEcdsaCompatible (line 84) | #[PHPUnit\Test]
method builderCanGenerateAToken (line 99) | #[PHPUnit\Test]
method parserCanReadAToken (line 125) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight (line 136) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenAlgorithmIsDifferent (line 152) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotEcdsaCompatible (line 168) | #[PHPUnit\Test]
method signatureValidationShouldSucceedWhenKeyIsRight (line 181) | #[PHPUnit\Test]
method everythingShouldWorkWithAKeyWithParams (line 193) | #[PHPUnit\Test]
method everythingShouldWorkWhenUsingATokenGeneratedByOtherLibs (line 214) | #[PHPUnit\Test]
FILE: tests/EddsaTokenTest.php
class EddsaTokenTest (line 26) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 51) | #[PHPUnit\Before]
method builderShouldRaiseExceptionWhenKeyIsInvalid (line 61) | #[PHPUnit\Test]
method builderCanGenerateAToken (line 76) | #[PHPUnit\Test]
method parserCanReadAToken (line 102) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight (line 113) | #[PHPUnit\Test]
method signatureValidationShouldSucceedWhenKeyIsRight (line 129) | #[PHPUnit\Test]
FILE: tests/Encoding/ChainedFormatterTest.php
class ChainedFormatterTest (line 15) | #[PHPUnit\CoversClass(ChainedFormatter::class)]
method formatClaimsShouldApplyAllConfiguredFormatters (line 21) | #[PHPUnit\Test]
FILE: tests/Encoding/JoseEncoderTest.php
class JoseEncoderTest (line 17) | #[PHPUnit\CoversClass(JoseEncoder::class)]
method jsonEncodeMustReturnAJSONString (line 23) | #[PHPUnit\Test]
method jsonEncodeShouldNotEscapeUnicode (line 31) | #[PHPUnit\Test]
method jsonEncodeShouldNotEscapeSlashes (line 39) | #[PHPUnit\Test]
method jsonEncodeMustRaiseExceptionWhenAnErrorHasOccurred (line 47) | #[PHPUnit\Test]
method jsonDecodeMustReturnTheDecodedData (line 59) | #[PHPUnit\Test]
method jsonDecodeMustRaiseExceptionWhenAnErrorHasOccurred (line 70) | #[PHPUnit\Test]
method base64UrlEncodeMustReturnAUrlSafeBase64 (line 82) | #[PHPUnit\Test]
method base64UrlEncodeMustEncodeBilboMessageProperly (line 92) | #[PHPUnit\Test]
method base64UrlDecodeMustRaiseExceptionWhenInvalidBase64CharsAreUsed (line 109) | #[PHPUnit\Test]
method base64UrlDecodeMustReturnTheRightData (line 121) | #[PHPUnit\Test]
method base64UrlDecodeMustDecodeBilboMessageProperly (line 130) | #[PHPUnit\Test]
FILE: tests/Encoding/MicrosecondBasedDateConversionTest.php
class MicrosecondBasedDateConversionTest (line 12) | #[PHPUnit\CoversClass(MicrosecondBasedDateConversion::class)]
method dateClaimsHaveMicrosecondsOrSeconds (line 15) | #[PHPUnit\Test]
method notAllDateClaimsNeedToBeConfigured (line 41) | #[PHPUnit\Test]
FILE: tests/Encoding/UnifyAudienceTest.php
class UnifyAudienceTest (line 11) | #[PHPUnit\CoversClass(UnifyAudience::class)]
method nothingShouldBeDoneWhenAudienceIsNotSet (line 14) | #[PHPUnit\Test]
method audienceShouldBeFormattedAsSingleStringWhenOneValueIsUsed (line 25) | #[PHPUnit\Test]
method audienceShouldBeFormattedAsArrayWhenMultipleValuesAreUsed (line 40) | #[PHPUnit\Test]
FILE: tests/Encoding/UnixTimestampDatesTest.php
class UnixTimestampDatesTest (line 12) | #[PHPUnit\CoversClass(UnixTimestampDates::class)]
method dateClaimsHaveMicrosecondsOrSeconds (line 15) | #[PHPUnit\Test]
method notAllDateClaimsNeedToBeConfigured (line 41) | #[PHPUnit\Test]
FILE: tests/HmacTokenTest.php
class HmacTokenTest (line 31) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 56) | #[PHPUnit\Before]
method builderCanGenerateAToken (line 65) | #[PHPUnit\Test]
method parserCanReadAToken (line 86) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight (line 97) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenAlgorithmIsDifferent (line 113) | #[PHPUnit\Test]
method signatureValidationShouldSucceedWhenKeyIsRight (line 126) | #[PHPUnit\Test]
method everythingShouldWorkWhenUsingATokenGeneratedByOtherLibs (line 135) | #[PHPUnit\Test]
method signatureValidationWithLocalFileKeyReferenceWillOperateWithKeyContents (line 154) | #[PHPUnit\Test]
FILE: tests/JwtFacadeTest.php
class JwtFacadeTest (line 24) | #[PHPUnit\CoversClass(JwtFacade::class)]
method configureDependencies (line 55) | #[PHPUnit\Before]
method createToken (line 65) | private function createToken(): string
method issueSetTimeValidity (line 76) | #[PHPUnit\Test]
method issueAllowsTimeValidityOverwrite (line 104) | #[PHPUnit\Test]
method goodJwt (line 138) | #[PHPUnit\Test]
method badSigner (line 151) | #[PHPUnit\Test]
method badKey (line 165) | #[PHPUnit\Test]
method badTime (line 182) | #[PHPUnit\Test]
method badIssuer (line 199) | #[PHPUnit\Test]
method parserForNonUnencryptedTokens (line 213) | #[PHPUnit\Test]
method customPsrClock (line 226) | #[PHPUnit\Test]
method multipleKeys (line 254) | #[PHPUnit\Test]
FILE: tests/KeyDumpSigner.php
class KeyDumpSigner (line 9) | final readonly class KeyDumpSigner implements Signer
method algorithmId (line 11) | public function algorithmId(): string
method sign (line 17) | public function sign(string $payload, Key $key): string
method verify (line 23) | public function verify(string $expected, string $payload, Key $key): bool
FILE: tests/Keys.php
type Keys (line 9) | trait Keys
method createRsaKeys (line 20) | #[PHPUnit\BeforeClass]
method createEcdsaKeys (line 37) | #[PHPUnit\BeforeClass]
method createEddsaKeys (line 58) | #[PHPUnit\BeforeClass]
FILE: tests/MaliciousTamperingPreventionTest.php
class MaliciousTamperingPreventionTest (line 30) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 50) | #[PHPUnit\Before]
method preventRegressionsThatAllowsMaliciousTampering (line 67) | #[PHPUnit\Test]
method createMaliciousToken (line 110) | private function createMaliciousToken(string $token): string
FILE: tests/RFC6978VectorTest.php
class RFC6978VectorTest (line 20) | #[PHPUnit\CoversClass(InMemory::class)]
method theVectorsFromRFC6978CanBeVerified (line 36) | #[PHPUnit\Test]
method dataRFC6979 (line 53) | public static function dataRFC6979(): iterable
method sha256Data (line 61) | public static function sha256Data(): iterable
method sha384Data (line 89) | public static function sha384Data(): iterable
method sha512Data (line 118) | public static function sha512Data(): iterable
FILE: tests/RsaTokenTest.php
class RsaTokenTest (line 25) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 52) | #[PHPUnit\Before]
method builderShouldRaiseExceptionWhenKeyIsInvalid (line 62) | #[PHPUnit\Test]
method builderShouldRaiseExceptionWhenKeyIsNotRsaCompatible (line 77) | #[PHPUnit\Test]
method builderCanGenerateAToken (line 92) | #[PHPUnit\Test]
method parserCanReadAToken (line 113) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRight (line 124) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenAlgorithmIsDifferent (line 137) | #[PHPUnit\Test]
method signatureAssertionShouldRaiseExceptionWhenKeyIsNotRsaCompatible (line 150) | #[PHPUnit\Test]
method signatureValidationShouldSucceedWhenKeyIsRight (line 166) | #[PHPUnit\Test]
method everythingShouldWorkWhenUsingATokenGeneratedByOtherLibs (line 175) | #[PHPUnit\Test]
FILE: tests/Signer/Blake2bTest.php
class Blake2bTest (line 15) | #[PHPUnit\CoversClass(Blake2b::class)]
method initializeKey (line 33) | #[PHPUnit\Before]
method algorithmIdMustBeCorrect (line 45) | #[PHPUnit\Test]
method generatedSignatureMustBeSuccessfullyVerified (line 53) | #[PHPUnit\Test]
method signShouldRejectShortKeys (line 62) | #[PHPUnit\Test]
method verifyShouldReturnFalseWhenExpectedHashWasNotCreatedWithSameInformation (line 73) | #[PHPUnit\Test]
FILE: tests/Signer/Ecdsa/EcdsaTestCase.php
class EcdsaTestCase (line 22) | abstract class EcdsaTestCase extends TestCase
method clearOpenSSLErrors (line 28) | #[PHPUnit\After]
method createDependencies (line 36) | #[PHPUnit\Before]
method algorithm (line 42) | abstract protected function algorithm(): Ecdsa;
method algorithmId (line 44) | abstract protected function algorithmId(): string;
method signatureAlgorithm (line 46) | abstract protected function signatureAlgorithm(): int;
method pointLength (line 48) | abstract protected function pointLength(): int;
method keyLength (line 50) | abstract protected function keyLength(): int;
method verificationKey (line 52) | abstract protected function verificationKey(): Key;
method signingKey (line 54) | abstract protected function signingKey(): Key;
method algorithmIdMustBeCorrect (line 56) | #[PHPUnit\Test]
method signatureAlgorithmMustBeCorrect (line 62) | #[PHPUnit\Test]
method pointLengthMustBeCorrect (line 68) | #[PHPUnit\Test]
method expectedKeyLengthMustBeCorrect (line 74) | #[PHPUnit\Test]
method signShouldReturnTheAHashBasedOnTheOpenSslSignature (line 80) | #[PHPUnit\Test]
method signShouldRaiseAnExceptionWhenKeyLengthIsNotTheExpectedOne (line 102) | #[PHPUnit\Test]
method incompatibleKeys (line 120) | abstract public static function incompatibleKeys(): iterable;
method signShouldRaiseAnExceptionWhenKeyTypeIsNotEC (line 122) | #[PHPUnit\Test]
method verifyShouldDelegateToEcdsaSignerUsingPublicKey (line 131) | #[PHPUnit\Test]
FILE: tests/Signer/Ecdsa/MultibyteStringConverterTest.php
class MultibyteStringConverterTest (line 16) | #[PHPUnit\CoversClass(MultibyteStringConverter::class)]
method toAsn1ShouldReturnThePointsInAnAsn1SequenceFormat (line 25) | #[PHPUnit\Test]
method toAsn1ShouldRaiseExceptionWhenPointsDoNotHaveCorrectLength (line 40) | #[PHPUnit\Test]
method fromAsn1ShouldReturnTheConcatenatedPoints (line 50) | #[PHPUnit\Test]
method pointsConversionData (line 63) | public static function pointsConversionData(): iterable
method fromAsn1ShouldRaiseExceptionOnInvalidMessage (line 91) | #[PHPUnit\Test]
method invalidAsn1Structures (line 105) | public static function invalidAsn1Structures(): iterable
FILE: tests/Signer/Ecdsa/Sha256Test.php
class Sha256Test (line 15) | #[PHPUnit\CoversClass(Ecdsa::class)]
method algorithm (line 23) | protected function algorithm(): Ecdsa
method algorithmId (line 28) | protected function algorithmId(): string
method signatureAlgorithm (line 33) | protected function signatureAlgorithm(): int
method pointLength (line 38) | protected function pointLength(): int
method keyLength (line 43) | protected function keyLength(): int
method verificationKey (line 48) | protected function verificationKey(): Key
method signingKey (line 53) | protected function signingKey(): Key
method incompatibleKeys (line 59) | public static function incompatibleKeys(): iterable
FILE: tests/Signer/Ecdsa/Sha384Test.php
class Sha384Test (line 15) | #[PHPUnit\CoversClass(Ecdsa::class)]
method algorithm (line 23) | protected function algorithm(): Ecdsa
method algorithmId (line 28) | protected function algorithmId(): string
method signatureAlgorithm (line 33) | protected function signatureAlgorithm(): int
method pointLength (line 38) | protected function pointLength(): int
method keyLength (line 43) | protected function keyLength(): int
method verificationKey (line 48) | protected function verificationKey(): Key
method signingKey (line 53) | protected function signingKey(): Key
method incompatibleKeys (line 59) | public static function incompatibleKeys(): iterable
FILE: tests/Signer/Ecdsa/Sha512Test.php
class Sha512Test (line 15) | #[PHPUnit\CoversClass(Ecdsa::class)]
method algorithm (line 23) | protected function algorithm(): Ecdsa
method algorithmId (line 28) | protected function algorithmId(): string
method signatureAlgorithm (line 33) | protected function signatureAlgorithm(): int
method pointLength (line 38) | protected function pointLength(): int
method keyLength (line 43) | protected function keyLength(): int
method verificationKey (line 48) | protected function verificationKey(): Key
method signingKey (line 53) | protected function signingKey(): Key
method incompatibleKeys (line 59) | public static function incompatibleKeys(): iterable
FILE: tests/Signer/EddsaTest.php
class EddsaTest (line 18) | #[PHPUnit\CoversClass(Eddsa::class)]
method algorithmIdMustBeCorrect (line 26) | #[PHPUnit\Test]
method signShouldReturnAValidEddsaSignature (line 32) | #[PHPUnit\Test]
method signShouldRaiseAnExceptionWhenKeyIsInvalid (line 45) | #[PHPUnit\Test]
method verifyShouldReturnTrueWhenSignatureIsValid (line 57) | #[PHPUnit\Test]
method verifyShouldRaiseAnExceptionWhenKeyIsNotParseable (line 67) | #[PHPUnit\Test]
method signatureOfRfcExample (line 80) | #[PHPUnit\Test]
method verificationOfRfcExample (line 104) | #[PHPUnit\Test]
FILE: tests/Signer/FakeSigner.php
class FakeSigner (line 9) | final class FakeSigner implements Signer
method __construct (line 12) | public function __construct(private readonly string $signature)
method algorithmId (line 16) | public function algorithmId(): string
method sign (line 21) | public function sign(string $payload, Key $key): string
method verify (line 26) | public function verify(string $expected, string $payload, Key $key): bool
FILE: tests/Signer/Hmac/HmacTestCase.php
class HmacTestCase (line 20) | abstract class HmacTestCase extends TestCase
method algorithm (line 22) | abstract protected function algorithm(): Hmac;
method hashAlgorithm (line 24) | abstract protected function hashAlgorithm(): string;
method expectedAlgorithmId (line 26) | abstract protected function expectedAlgorithmId(): string;
method expectedMinimumBits (line 28) | abstract protected function expectedMinimumBits(): int;
method algorithmIdMustBeCorrect (line 30) | #[PHPUnit\Test]
method signMustReturnAHashAccordingWithTheAlgorithm (line 36) | #[PHPUnit\Test]
method verifyMustReturnTrueWhenContentWasSignedWithTheSameKey (line 47) | #[PHPUnit\Test]
method verifyMustReturnTrueWhenContentWasSignedWithADifferentKey (line 57) | #[PHPUnit\Test]
method keyMustFulfillMinimumLengthRequirement (line 76) | #[PHPUnit\Test]
method generateSecret (line 94) | private function generateSecret(?int $length = null): string
FILE: tests/Signer/Hmac/Sha256Test.php
class Sha256Test (line 12) | #[PHPUnit\CoversClass(Hmac::class)]
method algorithm (line 18) | protected function algorithm(): Hmac
method expectedAlgorithmId (line 23) | protected function expectedAlgorithmId(): string
method expectedMinimumBits (line 28) | protected function expectedMinimumBits(): int
method hashAlgorithm (line 33) | protected function hashAlgorithm(): string
FILE: tests/Signer/Hmac/Sha384Test.php
class Sha384Test (line 12) | #[PHPUnit\CoversClass(Hmac::class)]
method algorithm (line 18) | protected function algorithm(): Hmac
method expectedAlgorithmId (line 23) | protected function expectedAlgorithmId(): string
method expectedMinimumBits (line 28) | protected function expectedMinimumBits(): int
method hashAlgorithm (line 33) | protected function hashAlgorithm(): string
FILE: tests/Signer/Hmac/Sha512Test.php
class Sha512Test (line 12) | #[PHPUnit\CoversClass(Hmac::class)]
method algorithm (line 18) | protected function algorithm(): Hmac
method expectedAlgorithmId (line 23) | protected function expectedAlgorithmId(): string
method expectedMinimumBits (line 28) | protected function expectedMinimumBits(): int
method hashAlgorithm (line 33) | protected function hashAlgorithm(): string
FILE: tests/Signer/Key/InMemoryTest.php
class InMemoryTest (line 16) | #[PHPUnit\CoversClass(CannotDecodeContent::class)]
method exceptionShouldBeRaisedWhenInvalidBase64CharsAreUsed (line 23) | #[PHPUnit\Test]
method base64EncodedShouldDecodeKeyContents (line 32) | #[PHPUnit\Test]
method exceptionShouldBeRaisedWhenFileDoesNotExists (line 40) | #[PHPUnit\Test]
method exceptionShouldBeRaisedWhenFileIsEmpty (line 52) | #[PHPUnit\Test]
method contentsShouldReturnConfiguredData (line 61) | #[PHPUnit\Test]
method contentsShouldReturnFileContentsWhenFilePathHasBeenPassed (line 69) | #[PHPUnit\Test]
method passphraseShouldReturnConfiguredData (line 77) | #[PHPUnit\Test]
method emptyPlainTextContentShouldRaiseException (line 85) | #[PHPUnit\Test]
method emptyBase64ContentShouldRaiseException (line 94) | #[PHPUnit\Test]
FILE: tests/Signer/Rsa/KeyValidationSigner.php
class KeyValidationSigner (line 11) | final readonly class KeyValidationSigner extends OpenSSL
method guardAgainstIncompatibleKey (line 14) | protected function guardAgainstIncompatibleKey(int $type, int $lengthI...
method algorithm (line 18) | public function algorithm(): int
method algorithmId (line 23) | public function algorithmId(): string
method sign (line 28) | public function sign(string $payload, Key $key): string
method verify (line 33) | public function verify(string $expected, string $payload, Key $key): bool
FILE: tests/Signer/Rsa/KeyValidationTest.php
class KeyValidationTest (line 16) | #[PHPUnit\CoversClass(OpenSSL::class)]
method clearOpenSSLErrors (line 21) | #[PHPUnit\After]
method signShouldRaiseAnExceptionWhenKeyIsInvalid (line 29) | #[PHPUnit\Test]
method algorithm (line 46) | private function algorithm(): OpenSSL
FILE: tests/Signer/Rsa/RsaTestCase.php
class RsaTestCase (line 23) | abstract class RsaTestCase extends TestCase
method algorithm (line 27) | abstract protected function algorithm(): Rsa;
method algorithmId (line 29) | abstract protected function algorithmId(): string;
method signatureAlgorithm (line 31) | abstract protected function signatureAlgorithm(): int;
method clearOpenSSLErrors (line 33) | #[PHPUnit\After]
method algorithmIdMustBeCorrect (line 41) | #[PHPUnit\Test]
method signatureAlgorithmMustBeCorrect (line 47) | #[PHPUnit\Test]
method signShouldReturnAValidOpensslSignature (line 53) | #[PHPUnit\Test]
method signShouldRaiseAnExceptionWhenKeyIsNotParseable (line 68) | #[PHPUnit\Test]
method allOpenSSLErrorsShouldBeOnTheErrorMessage (line 77) | #[PHPUnit\Test]
method signShouldRaiseAnExceptionWhenKeyTypeIsNotRsa (line 89) | #[PHPUnit\Test]
method signShouldRaiseAnExceptionWhenKeyLengthIsBelowMinimum (line 98) | #[PHPUnit\Test]
method verifyShouldReturnTrueWhenSignatureIsValid (line 107) | #[PHPUnit\Test]
method verifyShouldRaiseAnExceptionWhenKeyIsNotParseable (line 120) | #[PHPUnit\Test]
method verifyShouldRaiseAnExceptionWhenKeyTypeIsNotRsa (line 129) | #[PHPUnit\Test]
FILE: tests/Signer/Rsa/Sha256Test.php
class Sha256Test (line 15) | #[PHPUnit\CoversClass(Sha256::class)]
method algorithm (line 22) | protected function algorithm(): Rsa
method algorithmId (line 27) | protected function algorithmId(): string
method signatureAlgorithm (line 32) | protected function signatureAlgorithm(): int
FILE: tests/Signer/Rsa/Sha384Test.php
class Sha384Test (line 15) | #[PHPUnit\CoversClass(Sha384::class)]
method algorithm (line 22) | protected function algorithm(): Rsa
method algorithmId (line 27) | protected function algorithmId(): string
method signatureAlgorithm (line 32) | protected function signatureAlgorithm(): int
FILE: tests/Signer/Rsa/Sha512Test.php
class Sha512Test (line 15) | #[PHPUnit\CoversClass(Rsa::class)]
method algorithm (line 22) | protected function algorithm(): Rsa
method algorithmId (line 27) | protected function algorithmId(): string
method signatureAlgorithm (line 32) | protected function signatureAlgorithm(): int
FILE: tests/SodiumBase64PolyfillTest.php
class SodiumBase64PolyfillTest (line 19) | #[PHPUnit\CoversClass(SodiumBase64Polyfill::class)]
method constantsMatchExtensionOnes (line 26) | #[PHPUnit\Test]
method bin2base64 (line 51) | #[PHPUnit\Test]
method base642binFallback (line 59) | #[PHPUnit\Test]
method base64Variants (line 68) | public static function base64Variants(): iterable
method sodiumBase642BinRaisesExceptionOnInvalidBase64 (line 81) | #[PHPUnit\Test]
method fallbackBase642BinRaisesExceptionOnInvalidBase64 (line 90) | #[PHPUnit\Test]
method invalidBase64 (line 100) | public static function invalidBase64(): iterable
FILE: tests/TimeFractionPrecisionTest.php
class TimeFractionPrecisionTest (line 16) | #[PHPUnit\CoversClass(Configuration::class)]
method timeFractionsPrecisionsAreRespected (line 30) | #[PHPUnit\Test]
method datesWithPotentialRoundingIssues (line 55) | public static function datesWithPotentialRoundingIssues(): iterable
method typeConversionDoesNotCauseParsingErrors (line 63) | #[PHPUnit\Test]
method timeFractionConversions (line 82) | public static function timeFractionConversions(): iterable
FILE: tests/Token/BuilderTest.php
class BuilderTest (line 22) | #[PHPUnit\CoversClass(Builder::class)]
method initializeDependencies (line 34) | #[PHPUnit\Before]
method withClaimShouldRaiseExceptionWhenTryingToConfigureARegisteredClaim (line 42) | #[PHPUnit\Test]
method getTokenShouldReturnACompletelyConfigureToken (line 59) | #[PHPUnit\Test]
method immutability (line 110) | #[PHPUnit\Test]
FILE: tests/Token/DataSetTest.php
class DataSetTest (line 10) | #[PHPUnit\CoversClass(DataSet::class)]
method getShouldReturnTheConfiguredValue (line 13) | #[PHPUnit\Test]
method getShouldReturnTheFallbackValueWhenItWasGiven (line 21) | #[PHPUnit\Test]
method getShouldReturnNullWhenFallbackValueWasNotGiven (line 29) | #[PHPUnit\Test]
method hasShouldReturnTrueWhenItemWasConfigured (line 37) | #[PHPUnit\Test]
method hasShouldReturnFalseWhenItemWasNotConfigured (line 45) | #[PHPUnit\Test]
method allShouldReturnAllConfiguredItems (line 53) | #[PHPUnit\Test]
method toStringShouldReturnTheEncodedData (line 62) | #[PHPUnit\Test]
FILE: tests/Token/ParserTest.php
class ParserTest (line 20) | #[PHPUnit\CoversClass(Parser::class)]
method createDependencies (line 30) | #[PHPUnit\Before]
method createParser (line 36) | private function createParser(): Parser
method parseMustRaiseExceptionWhenTokenDoesNotHaveThreeParts (line 41) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenTokenDoesNotHaveHeaders (line 54) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenTokenDoesNotHaveClaims (line 67) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenTokenDoesNotHaveSignature (line 80) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenHeaderCannotBeDecoded (line 93) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenDealingWithNonArrayHeaders (line 116) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenDealingWithHeadersThatHaveEmptyStringKeys (line 131) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenHeaderIsFromAnEncryptedToken (line 146) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenDealingWithNonArrayClaims (line 161) | #[PHPUnit\Test]
method parseMustRaiseExceptionWhenDealingWithClaimsThatHaveEmptyStringKeys (line 176) | #[PHPUnit\Test]
method parseMustReturnAnUnsecuredTokenWhenSignatureIsNotInformed (line 191) | #[PHPUnit\Test]
method parseMustConfigureTypeToJWTWhenItIsMissing (line 223) | #[PHPUnit\Test]
method parseMustNotChangeTypeWhenItIsConfigured (line 255) | #[PHPUnit\Test]
method parseShouldReplicateClaimValueOnHeaderWhenNeeded (line 287) | #[PHPUnit\Test]
method parseMustReturnANonSignedTokenWhenSignatureAlgorithmIsMissing (line 319) | #[PHPUnit\Test]
method parseMustReturnANonSignedTokenWhenSignatureAlgorithmIsNone (line 351) | #[PHPUnit\Test]
method parseMustReturnASignedTokenWhenSignatureIsInformed (line 383) | #[PHPUnit\Test]
method parseMustConvertDateClaimsToObjects (line 415) | #[PHPUnit\Test]
method parseMustConvertStringDates (line 454) | #[PHPUnit\Test]
method parseShouldRaiseExceptionOnInvalidDate (line 485) | #[PHPUnit\Test]
method parseShouldRaiseExceptionOnTimestampBeyondDateTimeImmutableRange (line 509) | #[PHPUnit\Test]
FILE: tests/Token/PlainTest.php
class PlainTest (line 14) | #[PHPUnit\CoversClass(Plain::class)]
method createDependencies (line 23) | #[PHPUnit\Before]
method createToken (line 31) | private function createToken(
method signedShouldCreateATokenWithSignature (line 43) | #[PHPUnit\Test]
method payloadShouldReturnAStringWithTheEncodedHeadersAndClaims (line 53) | #[PHPUnit\Test]
method isPermittedForShouldReturnFalseWhenNoAudienceIsConfigured (line 61) | #[PHPUnit\Test]
method isPermittedForShouldReturnFalseWhenAudienceDoesNotMatchAsArray (line 69) | #[PHPUnit\Test]
method isPermittedForShouldReturnFalseWhenAudienceTypeDoesNotMatch (line 80) | #[PHPUnit\Test]
method isPermittedForShouldReturnTrueWhenAudienceMatchesAsArray (line 91) | #[PHPUnit\Test]
method isIdentifiedByShouldReturnFalseWhenNoIdWasConfigured (line 102) | #[PHPUnit\Test]
method isIdentifiedByShouldReturnFalseWhenIdDoesNotMatch (line 110) | #[PHPUnit\Test]
method isIdentifiedByShouldReturnTrueWhenIdMatches (line 121) | #[PHPUnit\Test]
method isRelatedToShouldReturnFalseWhenNoSubjectWasConfigured (line 132) | #[PHPUnit\Test]
method isRelatedToShouldReturnFalseWhenSubjectDoesNotMatch (line 140) | #[PHPUnit\Test]
method isRelatedToShouldReturnTrueWhenSubjectMatches (line 151) | #[PHPUnit\Test]
method hasBeenIssuedByShouldReturnFalseWhenIssuerIsNotConfigured (line 162) | #[PHPUnit\Test]
method hasBeenIssuedByShouldReturnFalseWhenIssuerTypeDoesNotMatches (line 170) | #[PHPUnit\Test]
method hasBeenIssuedByShouldReturnFalseWhenIssuerIsNotInTheGivenList (line 181) | #[PHPUnit\Test]
method hasBeenIssuedByShouldReturnTrueWhenIssuerIsInTheGivenList (line 192) | #[PHPUnit\Test]
method hasBeenIssuedBeforeShouldReturnTrueWhenIssueTimeIsNotConfigured (line 203) | #[PHPUnit\Test]
method hasBeenIssuedBeforeShouldReturnTrueWhenIssueTimeIsBeforeThanNow (line 211) | #[PHPUnit\Test]
method hasBeenIssuedBeforeShouldReturnTrueWhenIssueTimeIsEqualsToNow (line 223) | #[PHPUnit\Test]
method hasBeenIssuedBeforeShouldReturnFalseWhenIssueTimeIsGreaterThanNow (line 235) | #[PHPUnit\Test]
method isMinimumTimeBeforeShouldReturnTrueWhenIssueTimeIsNotConfigured (line 247) | #[PHPUnit\Test]
method isMinimumTimeBeforeShouldReturnTrueWhenNotBeforeClaimIsBeforeThanNow (line 255) | #[PHPUnit\Test]
method isMinimumTimeBeforeShouldReturnTrueWhenNotBeforeClaimIsEqualsToNow (line 267) | #[PHPUnit\Test]
method isMinimumTimeBeforeShouldReturnFalseWhenNotBeforeClaimIsGreaterThanNow (line 279) | #[PHPUnit\Test]
method isExpiredShouldReturnFalseWhenTokenDoesNotExpires (line 291) | #[PHPUnit\Test]
method isExpiredShouldReturnFalseWhenTokenIsNotExpired (line 299) | #[PHPUnit\Test]
method isExpiredShouldReturnTrueWhenExpirationIsEqualsToNow (line 312) | #[PHPUnit\Test]
method isExpiredShouldReturnTrueAfterTokenExpires (line 325) | #[PHPUnit\Test]
method toStringMustReturnEncodedDataWithEmptySignature (line 338) | #[PHPUnit\Test]
method toStringMustReturnEncodedData (line 346) | #[PHPUnit\Test]
FILE: tests/Token/SignatureTest.php
class SignatureTest (line 10) | #[PHPUnit\CoversClass(Signature::class)]
method hashShouldReturnTheHash (line 13) | #[PHPUnit\Test]
FILE: tests/UnsignedTokenTest.php
class UnsignedTokenTest (line 26) | #[PHPUnit\CoversClass(Configuration::class)]
method createConfiguration (line 51) | #[PHPUnit\Before]
method builderCanGenerateAToken (line 60) | #[PHPUnit\Test]
method parserCanReadAToken (line 84) | #[PHPUnit\Test]
method tokenValidationShouldPassWhenEverythingIsFine (line 95) | #[PHPUnit\Test]
method tokenValidationShouldAllowCustomConstraint (line 111) | #[PHPUnit\Test]
method tokenAssertionShouldRaiseExceptionWhenOneOfTheConstraintsFails (line 118) | #[PHPUnit\Test]
method validUserConstraint (line 133) | private function validUserConstraint(): Constraint
FILE: tests/UnsupportedParser.php
class UnsupportedParser (line 11) | final readonly class UnsupportedParser implements Parser
method parse (line 13) | public function parse(string $jwt): Token
FILE: tests/Validation/Constraint/ConstraintTestCase.php
class ConstraintTestCase (line 16) | abstract class ConstraintTestCase extends TestCase
method buildToken (line 22) | protected function buildToken(
method issueToken (line 34) | protected function issueToken(Signer $signer, Signer\Key $key, ?Closur...
FILE: tests/Validation/Constraint/HasClaimTest.php
class HasClaimTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method registeredClaimsCannotBeValidatedUsingThisConstraint (line 21) | #[PHPUnit\Test]
method registeredClaims (line 34) | public static function registeredClaims(): iterable
method assertShouldRaiseExceptionWhenClaimIsNotSet (line 41) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenTokenIsNotAPlainToken (line 52) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenClaimMatches (line 64) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/HasClaimWithValueTest.php
class HasClaimWithValueTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method registeredClaimsCannotBeValidatedUsingThisConstraint (line 21) | #[PHPUnit\Test]
method registeredClaims (line 34) | public static function registeredClaims(): iterable
method assertShouldRaiseExceptionWhenClaimIsNotSet (line 41) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenClaimValueDoesNotMatch (line 52) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenTokenIsNotAPlainToken (line 63) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenClaimMatches (line 74) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/IdentifiedByTest.php
class IdentifiedByTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method assertShouldRaiseExceptionWhenIdIsNotSet (line 19) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenIdDoesNotMatch (line 30) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenIdMatches (line 41) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/IssuedByTest.php
class IssuedByTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method assertShouldRaiseExceptionWhenIssuerIsNotSet (line 19) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenIssuerValueDoesNotMatch (line 29) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenIssuerTypeValueDoesNotMatch (line 39) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenIssuerMatches (line 49) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/LooseValidAtTest.php
class LooseValidAtTest (line 14) | #[PHPUnit\CoversClass(Constraint\LeewayCannotBeNegative::class)]
method buildValidAtConstraint (line 22) | protected function buildValidAtConstraint(Clock $clock, ?DateInterval ...
method assertShouldNotRaiseExceptionWhenTokenDoesNotHaveTimeClaims (line 27) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/PermittedForTest.php
class PermittedForTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method assertShouldRaiseExceptionWhenAudienceIsNotSet (line 19) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenAudienceValueDoesNotMatch (line 30) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenAudienceTypeDoesNotMatch (line 41) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenAudienceMatches (line 52) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/RelatedToTest.php
class RelatedToTest (line 12) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method assertShouldRaiseExceptionWhenSubjectIsNotSet (line 19) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenSubjectDoesNotMatch (line 30) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenSubjectMatches (line 41) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/SignedWithOneInSetTest.php
class SignedWithOneInSetTest (line 29) | #[PHPUnit\CoversClass(SignedWithOneInSet::class)]
method exceptionShouldBeRaisedWhenSignatureIsNotVerifiedByAllConstraints (line 47) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionsWhenSignatureIsVerifiedByAtLeastOneConstraint (line 69) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/SignedWithTest.php
class SignedWithTest (line 14) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method createDependencies (line 26) | #[PHPUnit\Before]
method assertShouldRaiseExceptionWhenTokenIsNotAPlainToken (line 36) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenSignerIsNotTheSame (line 49) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenSignatureIsInvalid (line 64) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenSignatureIsValid (line 82) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/SignedWithUntilDateTest.php
class SignedWithUntilDateTest (line 26) | #[PHPUnit\CoversClass(SignedWithUntilDate::class)]
method assertShouldRaiseExceptionWhenConstraintUsageIsNotValidAnymore (line 43) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenTokenIsNotAPlainToken (line 61) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenSignerIsNotTheSame (line 74) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenSignatureIsInvalid (line 88) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenSignatureIsValid (line 102) | #[PHPUnit\Test]
method clockShouldBeOptional (line 116) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/StrictValidAtTest.php
class StrictValidAtTest (line 15) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method buildValidAtConstraint (line 23) | protected function buildValidAtConstraint(Clock $clock, ?DateInterval ...
method assertShouldRaiseExceptionWhenTokenIsNotAPlainToken (line 28) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenIatClaimIsMissing (line 39) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenNbfClaimIsMissing (line 50) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenExpClaimIsMissing (line 66) | #[PHPUnit\Test]
FILE: tests/Validation/Constraint/ValidAtTestCase.php
class ValidAtTestCase (line 16) | abstract class ValidAtTestCase extends ConstraintTestCase
method createDependencies (line 20) | #[PHPUnit\Before]
method buildValidAtConstraint (line 26) | abstract protected function buildValidAtConstraint(Clock $clock, ?Date...
method constructShouldRaiseExceptionOnNegativeLeeway (line 28) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenTokenIsExpired (line 40) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenMinimumTimeIsNotMet (line 59) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture (line 78) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenLeewayIsUsed (line 97) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment (line 114) | #[PHPUnit\Test]
FILE: tests/Validation/ConstraintViolationTest.php
class ConstraintViolationTest (line 11) | #[PHPUnit\CoversClass(ConstraintViolation::class)]
method errorShouldConfigureMessageAndConstraint (line 15) | #[PHPUnit\Test]
FILE: tests/Validation/RequiredConstraintsViolatedTest.php
class RequiredConstraintsViolatedTest (line 11) | #[PHPUnit\CoversClass(RequiredConstraintsViolated::class)]
method fromViolationsShouldConfigureMessageAndViolationList (line 15) | #[PHPUnit\Test]
FILE: tests/Validation/ValidatorTest.php
class ValidatorTest (line 16) | #[PHPUnit\CoversClass(Validator::class)]
method createDependencies (line 23) | #[PHPUnit\Before]
method assertShouldRaiseExceptionWhenNoConstraintIsGiven (line 29) | #[PHPUnit\Test]
method assertShouldRaiseExceptionWhenAtLeastOneConstraintFails (line 39) | #[PHPUnit\Test]
method assertShouldNotRaiseExceptionWhenNoConstraintFails (line 64) | #[PHPUnit\Test]
method validateShouldRaiseExceptionWhenNoConstraintIsGiven (line 76) | #[PHPUnit\Test]
method validateShouldReturnFalseWhenAtLeastOneConstraintFails (line 86) | #[PHPUnit\Test]
method validateShouldReturnTrueWhenNoConstraintFails (line 110) | #[PHPUnit\Test]
Condensed preview — 201 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (416K chars).
[
{
"path": ".composer-require-checker.config.json",
"chars": 156,
"preview": "{\n \"symbol-whitelist\" : [\n \"NoDiscard\"\n ],\n \"php-core-extensions\" : [\n \"Core\",\n \"date\",\n \"json\",\n \"has"
},
{
"path": ".gitattributes",
"chars": 896,
"preview": "/docs export-ignore\n/tests export-ignore\n/.gitatt"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 1669,
"preview": "# Contributing to lcobucci/jwt\n\nFirst off, thanks for taking the time to contribute!\n\n## Reporting issues\n\nWe accept bug"
},
{
"path": ".github/FUNDING.yml",
"chars": 35,
"preview": "github: lcobucci\npatreon: lcobucci\n"
},
{
"path": ".github/SECURITY.md",
"chars": 467,
"preview": "# Security Policy\n\n## Supported Versions\n\n| Version | Supported |\n| ----------- | ------------------ |\n| 4."
},
{
"path": ".github/workflows/backwards-compatibility.yml",
"chars": 1298,
"preview": "name: \"Backwards compatibility check\"\n\non:\n pull_request:\n\njobs:\n bc-check:\n name: \"Backwards compatibility check\"\n"
},
{
"path": ".github/workflows/benchmarks.yml",
"chars": 837,
"preview": "name: \"Benchmarks\"\n\non:\n pull_request:\n push:\n\njobs:\n benchmarks:\n name: \"Run benchmarks\"\n\n runs-on: ${{ matrix"
},
{
"path": ".github/workflows/coding-standards.yml",
"chars": 912,
"preview": "name: \"Check Coding Standards\"\n\non:\n pull_request:\n push:\n\njobs:\n coding-standards:\n name: \"Check Coding Standards"
},
{
"path": ".github/workflows/composer-json-lint.yml",
"chars": 1271,
"preview": "name: \"Lint composer.json\"\n\non:\n pull_request:\n push:\n\njobs:\n coding-standards:\n name: \"Lint composer.json\"\n\n r"
},
{
"path": ".github/workflows/mutation-tests.yml",
"chars": 1010,
"preview": "name: \"Mutation tests\"\n\non:\n pull_request:\n push:\n\njobs:\n mutation-tests:\n name: \"Mutation tests\"\n\n runs-on: ${"
},
{
"path": ".github/workflows/phpunit.yml",
"chars": 911,
"preview": "name: \"PHPUnit tests\"\n\non:\n pull_request:\n push:\n\njobs:\n phpunit:\n name: \"PHPUnit tests\"\n\n runs-on: ${{ matrix."
},
{
"path": ".github/workflows/release-on-milestone-closed.yml",
"chars": 2726,
"preview": "# https://help.github.com/en/categories/automating-your-workflow-with-github-actions\n\nname: \"Automatic Releases\"\n\non:\n "
},
{
"path": ".github/workflows/static-analysis.yml",
"chars": 846,
"preview": "name: \"Static Analysis\"\n\non:\n pull_request:\n push:\n\njobs:\n static-analysis:\n name: \"Static Analysis\"\n\n runs-on:"
},
{
"path": ".gitignore",
"chars": 112,
"preview": "vendor\nphpunit.xml\ninfection.txt\ncoverage\nphpcs.xml\n/.phpcs.cache\n/.phpunit.result.cache\n/.phpunit.cache\n/build\n"
},
{
"path": ".readthedocs.yaml",
"chars": 100,
"preview": "version: 2\n\nbuild:\n os: ubuntu-22.04\n tools:\n python: \"3\"\n\nmkdocs:\n configuration: mkdocs.yml\n"
},
{
"path": ".roave-backward-compatibility-check.json",
"chars": 105,
"preview": "{\n \"baseline\": [\n \"#\\\\[BC\\\\] SKIPPED: Unable to compile initializer in method Lcobucci\\\\\\\\.+#\"\n ]\n}\n"
},
{
"path": "LICENSE",
"chars": 1488,
"preview": "Copyright (c) 2014, Luís Cobucci\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or withou"
},
{
"path": "Makefile",
"chars": 1249,
"preview": "PARALLELISM := $(shell nproc)\n\n.PHONY: all\nall: install phpcbf phpcs phpstan phpunit infection phpbench\n\n.PHONY: install"
},
{
"path": "README.md",
"chars": 1528,
"preview": "# JWT\n[![Gitter]](https://gitter.im/lcobucci/jwt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=bad"
},
{
"path": "composer.json",
"chars": 1626,
"preview": "{\n \"name\": \"lcobucci/jwt\",\n \"description\": \"A simple library to work with JSON Web Token and JSON Web Signature\",\n"
},
{
"path": "docs/configuration.md",
"chars": 7266,
"preview": "# Configuration\n\nIn order to simplify the setup of the library, we provide the class `Lcobucci\\JWT\\Configuration`.\n\nIt's"
},
{
"path": "docs/extending-the-library.md",
"chars": 5249,
"preview": "# Extending the library\n\n!!! Note\n The examples here fetch the configuration object from a hypothetical dependency in"
},
{
"path": "docs/index.md",
"chars": 628,
"preview": "# Overview\n\n`lcobucci/jwt` is a framework-agnostic PHP library that allows you to issue, parse, and validate JSON Web To"
},
{
"path": "docs/installation.md",
"chars": 880,
"preview": "# Installation\n\nThis package is available on [Packagist] and you can install it using [Composer].\n\nBy running the follow"
},
{
"path": "docs/issuing-tokens.md",
"chars": 2791,
"preview": "# Issuing tokens\n\nTo issue new tokens you must create a new token builder, customise it, and ask it to build the token:\n"
},
{
"path": "docs/parsing-tokens.md",
"chars": 1118,
"preview": "# Parsing tokens\n\nTo parse a token you must create a new parser and ask it to parse a string:\n\n```php\n<?php\ndeclare(stri"
},
{
"path": "docs/quick-start.md",
"chars": 4211,
"preview": "# Quick start\n\nOnce the library has been [installed](installation.md), you are able to issue and parse JWTs.\nThe class `"
},
{
"path": "docs/rotating-keys.md",
"chars": 6912,
"preview": "# Rotating Keys\n\nKey rotation consists in retiring and replacing cryptographic keys with new ones.\nPerforming that opera"
},
{
"path": "docs/supported-algorithms.md",
"chars": 3675,
"preview": "# Supported Algorithms\n\nThis library supports signing and verifying tokens with both symmetric and asymmetric algorithms"
},
{
"path": "docs/upgrading.md",
"chars": 15902,
"preview": "# Upgrading steps\n\nHere we'll keep a list of all steps you need to take to make sure your code is compatible with newer "
},
{
"path": "docs/validating-tokens.md",
"chars": 3780,
"preview": "# Validating tokens\n\nTo validate a token you must create a new validator and assert or validate a token.\n\n## Using `Lcob"
},
{
"path": "infection.json.dist",
"chars": 1366,
"preview": "{\n \"source\": {\n \"directories\": [\"src\"]\n },\n \"timeout\": 3,\n \"logs\": {\n \"text\": \"infection.txt\"\n"
},
{
"path": "mkdocs.yml",
"chars": 446,
"preview": "site_name: lcobucci/jwt\ntheme: readthedocs\n\nnav:\n - Intro:\n - 'index.md'\n - 'installation.md'\n - 'quick-start."
},
{
"path": "phpbench.json",
"chars": 88,
"preview": "{\n \"runner.bootstrap\": \"vendor/autoload.php\",\n \"runner.path\": \"tests/Benchmark\"\n}\n"
},
{
"path": "phpcs.xml.dist",
"chars": 597,
"preview": "<?xml version=\"1.0\"?>\n<ruleset\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNamespaceSchemaLocati"
},
{
"path": "phpstan.neon.dist",
"chars": 109,
"preview": "parameters:\n level: 8 # not yet ready for all the `mixed` checks\n paths:\n - src\n - tests\n"
},
{
"path": "phpunit.xml.dist",
"chars": 751,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNamespac"
},
{
"path": "renovate.json",
"chars": 144,
"preview": "{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\n \"local>lcobucci/.github:r"
},
{
"path": "src/Builder.php",
"chars": 2433,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoding\\CannotEncodeCo"
},
{
"path": "src/ClaimsFormatter.php",
"chars": 298,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse NoDiscard;\n\ninterface ClaimsFormatter\n{\n /**\n * @par"
},
{
"path": "src/Configuration.php",
"chars": 4907,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Closure;\nuse Lcobucci\\JWT\\Encoding\\ChainedFormatter;\nuse Lc"
},
{
"path": "src/Decoder.php",
"chars": 754,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;\nuse NoDiscard;\n\n"
},
{
"path": "src/Encoder.php",
"chars": 661,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Encoding\\CannotEncodeContent;\nuse NoDiscard;\n\n"
},
{
"path": "src/Encoding/CannotDecodeContent.php",
"chars": 556,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse JsonException;\nuse Lcobucci\\JWT\\Exception;\nuse Run"
},
{
"path": "src/Encoding/CannotEncodeContent.php",
"chars": 386,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse JsonException;\nuse Lcobucci\\JWT\\Exception;\nuse Run"
},
{
"path": "src/Encoding/ChainedFormatter.php",
"chars": 876,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse Lcobucci\\JWT\\ClaimsFormatter;\n\nfinal readonly clas"
},
{
"path": "src/Encoding/JoseEncoder.php",
"chars": 1536,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse JsonException;\nuse Lcobucci\\JWT\\Decoder;\nuse Lcobu"
},
{
"path": "src/Encoding/MicrosecondBasedDateConversion.php",
"chars": 858,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\ClaimsFormatte"
},
{
"path": "src/Encoding/UnifyAudience.php",
"chars": 685,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse Lcobucci\\JWT\\ClaimsFormatter;\nuse Lcobucci\\JWT\\Tok"
},
{
"path": "src/Encoding/UnixTimestampDates.php",
"chars": 730,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Encoding;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\ClaimsFormatte"
},
{
"path": "src/Exception.php",
"chars": 115,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Throwable;\n\ninterface Exception extends Throwable\n{\n}\n"
},
{
"path": "src/JwtFacade.php",
"chars": 1961,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Closure;\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoding\\C"
},
{
"path": "src/Parser.php",
"chars": 653,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;\nuse Lcobucci\\JWT"
},
{
"path": "src/Signer/Blake2b.php",
"chars": 904,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse Lcobucci\\JWT\\Signer;\n\nuse function hash_equals;\nuse "
},
{
"path": "src/Signer/CannotSignPayload.php",
"chars": 371,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exception"
},
{
"path": "src/Signer/Ecdsa/ConversionFailed.php",
"chars": 607,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exc"
},
{
"path": "src/Signer/Ecdsa/MultibyteStringConverter.php",
"chars": 4690,
"preview": "<?php\ndeclare(strict_types=1);\n\n/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2014-2018 Spomky-Labs\n *\n * This softwar"
},
{
"path": "src/Signer/Ecdsa/Sha256.php",
"chars": 493,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse const OPENSSL_"
},
{
"path": "src/Signer/Ecdsa/Sha384.php",
"chars": 493,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse const OPENSSL_"
},
{
"path": "src/Signer/Ecdsa/Sha512.php",
"chars": 592,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\n\nuse const OPENSSL_"
},
{
"path": "src/Signer/Ecdsa/SignatureConverter.php",
"chars": 1158,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Ecdsa;\n\n/**\n * Manipulates the result of a ECDSA signature"
},
{
"path": "src/Signer/Ecdsa.php",
"chars": 1811,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa\\MultibyteStringConverter;\n"
},
{
"path": "src/Signer/Eddsa.php",
"chars": 1008,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse Lcobucci\\JWT\\Signer;\nuse SodiumException;\n\nuse funct"
},
{
"path": "src/Signer/Hmac/Sha256.php",
"chars": 385,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\n\nfinal readonly class"
},
{
"path": "src/Signer/Hmac/Sha384.php",
"chars": 385,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\n\nfinal readonly class"
},
{
"path": "src/Signer/Hmac/Sha512.php",
"chars": 385,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\n\nfinal readonly class"
},
{
"path": "src/Signer/Hmac.php",
"chars": 1062,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse Lcobucci\\JWT\\Signer;\n\nuse function hash_equals;\nuse "
},
{
"path": "src/Signer/InvalidKeyProvided.php",
"chars": 1425,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exception"
},
{
"path": "src/Signer/Key/FileCouldNotBeRead.php",
"chars": 513,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Key;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Excep"
},
{
"path": "src/Signer/Key/InMemory.php",
"chars": 2521,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Key;\n\nuse Lcobucci\\JWT\\Signer\\InvalidKeyProvided;\nuse Lcob"
},
{
"path": "src/Signer/Key.php",
"chars": 201,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\ninterface Key\n{\n /** @return non-empty-string */\n "
},
{
"path": "src/Signer/OpenSSL.php",
"chars": 3395,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse Lcobucci\\JWT\\Signer;\nuse OpenSSLAsymmetricKey;\n\nuse "
},
{
"path": "src/Signer/Rsa/Sha256.php",
"chars": 338,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\Rsa;\n\nuse const OPENSSL_ALGO"
},
{
"path": "src/Signer/Rsa/Sha384.php",
"chars": 338,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\Rsa;\n\nuse const OPENSSL_ALGO"
},
{
"path": "src/Signer/Rsa/Sha512.php",
"chars": 338,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\Rsa;\n\nuse const OPENSSL_ALGO"
},
{
"path": "src/Signer/Rsa.php",
"chars": 994,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Signer;\n\nuse const OPENSSL_KEYTYPE_RSA;\n\nabstract readonly class "
},
{
"path": "src/Signer.php",
"chars": 1268,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Signer\\CannotSignPayload;\nuse Lcobucci\\JWT\\Sig"
},
{
"path": "src/SodiumBase64Polyfill.php",
"chars": 2949,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;\nuse SodiumExcept"
},
{
"path": "src/Token/Builder.php",
"chars": 4246,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Builder as Builde"
},
{
"path": "src/Token/DataSet.php",
"chars": 800,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse function array_key_exists;\n\nfinal readonly class Data"
},
{
"path": "src/Token/InvalidTokenStructure.php",
"chars": 1120,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exception;"
},
{
"path": "src/Token/Parser.php",
"chars": 4954,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Decoder;\nuse Lcob"
},
{
"path": "src/Token/Plain.php",
"chars": 2102,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse DateTimeInterface;\nuse Lcobucci\\JWT\\UnencryptedToken;"
},
{
"path": "src/Token/RegisteredClaimGiven.php",
"chars": 604,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exception;"
},
{
"path": "src/Token/RegisteredClaims.php",
"chars": 1944,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\n/**\n * Defines the list of claims that are registered in "
},
{
"path": "src/Token/Signature.php",
"chars": 584,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nfinal readonly class Signature\n{\n /**\n * @param no"
},
{
"path": "src/Token/UnsupportedHeaderFound.php",
"chars": 333,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Token;\n\nuse InvalidArgumentException;\nuse Lcobucci\\JWT\\Exception;"
},
{
"path": "src/Token.php",
"chars": 1554,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse DateTimeInterface;\nuse Lcobucci\\JWT\\Token\\DataSet;\nuse NoDi"
},
{
"path": "src/UnencryptedToken.php",
"chars": 480,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Token\\DataSet;\nuse Lcobucci\\JWT\\Token\\Signatur"
},
{
"path": "src/Validation/Constraint/CannotValidateARegisteredClaim.php",
"chars": 503,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse InvalidArgumentException;\nuse Lcobucc"
},
{
"path": "src/Validation/Constraint/HasClaim.php",
"chars": 973,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/HasClaimWithValue.php",
"chars": 1259,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/IdentifiedBy.php",
"chars": 629,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/IssuedBy.php",
"chars": 738,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/LeewayCannotBeNegative.php",
"chars": 339,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse InvalidArgumentException;\nuse Lcobucc"
},
{
"path": "src/Validation/Constraint/LooseValidAt.php",
"chars": 1959,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse DateInterval;\nuse DateTimeInterface;\n"
},
{
"path": "src/Validation/Constraint/PermittedFor.php",
"chars": 651,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/RelatedTo.php",
"chars": 638,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/SignedWith.php",
"chars": 1002,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT"
},
{
"path": "src/Validation/Constraint/SignedWithOneInSet.php",
"chars": 1028,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\"
},
{
"path": "src/Validation/Constraint/SignedWithUntilDate.php",
"chars": 1319,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse DateTimeImmutable;\nuse DateTimeInterf"
},
{
"path": "src/Validation/Constraint/StrictValidAt.php",
"chars": 2692,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation\\Constraint;\n\nuse DateInterval;\nuse DateTimeInterface;\n"
},
{
"path": "src/Validation/Constraint.php",
"chars": 205,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\nuse Lcobucci\\JWT\\Token;\n\ninterface Constraint\n{\n "
},
{
"path": "src/Validation/ConstraintViolation.php",
"chars": 642,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\nuse Lcobucci\\JWT\\Exception;\nuse RuntimeException;\n\nf"
},
{
"path": "src/Validation/NoConstraintsGiven.php",
"chars": 200,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\nuse Lcobucci\\JWT\\Exception;\nuse RuntimeException;\n\nf"
},
{
"path": "src/Validation/RequiredConstraintsViolated.php",
"chars": 1289,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\nuse Lcobucci\\JWT\\Exception;\nuse RuntimeException;\n\nu"
},
{
"path": "src/Validation/SignedWith.php",
"chars": 112,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\ninterface SignedWith extends Constraint\n{\n}\n"
},
{
"path": "src/Validation/ValidAt.php",
"chars": 109,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\ninterface ValidAt extends Constraint\n{\n}\n"
},
{
"path": "src/Validation/Validator.php",
"chars": 1403,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Validation;\n\nuse Lcobucci\\JWT\\Token;\n\nfinal readonly class Valida"
},
{
"path": "src/Validator.php",
"chars": 545,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT;\n\nuse Lcobucci\\JWT\\Validation\\Constraint;\nuse Lcobucci\\JWT\\Valida"
},
{
"path": "tests/Benchmark/AlgorithmsBench.php",
"chars": 5559,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Benchmark;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signe"
},
{
"path": "tests/Benchmark/CreateSignatureBench.php",
"chars": 702,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Benchmark;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signe"
},
{
"path": "tests/Benchmark/Ecdsa/private-256.key",
"chars": 227,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIK/5B8mfmtOq5sTN8hEivOK9aLUoPmkHFUrZEYQPogjPoAoGCCqGSM49\nAwEHoUQDQgAEZe2loSV3wrro"
},
{
"path": "tests/Benchmark/Ecdsa/private-384.key",
"chars": 288,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDDZi2q9Sdoiu0XNsrdD/PLBtgAr48L4MWy7XMiND8riSeJkTYnhPlra\nxMSRKkd2OhSgBwYFK4EEACKh"
},
{
"path": "tests/Benchmark/Ecdsa/private-521.key",
"chars": 365,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMIHcAgEBBEIBCwZmxfodGCjbu5tgb4al9Qwv36dS9lCYk4Hjq6VMMneMH2tlDaS1\nkEid5mVnJrznhLJFn5IO3mB+"
},
{
"path": "tests/Benchmark/Ecdsa/public-256.key",
"chars": 178,
"preview": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZe2loSV3wrroKUN/4zhwGhCqo3Xh\nu1td4QjeQ5wIVR0eUu11cBFj9/nk"
},
{
"path": "tests/Benchmark/Ecdsa/public-384.key",
"chars": 215,
"preview": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfqCXVUp/2a3S0c7Gneg7+2zlS3T5HNBm\nbp+f/1RS4KG5dpcRIUb1sVtLQUZC"
},
{
"path": "tests/Benchmark/Ecdsa/public-521.key",
"chars": 268,
"preview": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBo4UM3fpsJZBVc2NgJNX1vgzK6j8b\nM0DukkfHKqXAIW/QUkfcIaoIZYzl"
},
{
"path": "tests/Benchmark/IssueTokenBench.php",
"chars": 1035,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Benchmark;\n\nuse Lcobucci\\JWT\\Builder;\nuse Lcobucci\\JWT\\JwtF"
},
{
"path": "tests/Benchmark/ParseTokenBench.php",
"chars": 1668,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Benchmark;\n\nuse Lcobucci\\Clock\\SystemClock;\nuse Lcobucci\\JW"
},
{
"path": "tests/Benchmark/Rsa/private.key",
"chars": 1712,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfgQ+0A4Jz0CWR\n5Ac/MdK2ABuCzttNkvBQFl1"
},
{
"path": "tests/Benchmark/Rsa/public.key",
"chars": 451,
"preview": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn4EPtAOCc9AlkeQHPzHS\ntgAbgs7bTZLwUBZdR8/KuKPEHLd4"
},
{
"path": "tests/Benchmark/VerifySignatureBench.php",
"chars": 941,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Benchmark;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signe"
},
{
"path": "tests/ConfigurationTest.php",
"chars": 7614,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Builder;\nuse Lcobucci\\JWT\\ClaimsFormatte"
},
{
"path": "tests/ES512TokenTest.php",
"chars": 6841,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/EcdsaTokenTest.php",
"chars": 8989,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/EddsaTokenTest.php",
"chars": 5002,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/Encoding/ChainedFormatterTest.php",
"chars": 1630,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Encoding;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/Encoding/JoseEncoderTest.php",
"chars": 5091,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Encoding;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;\nu"
},
{
"path": "tests/Encoding/MicrosecondBasedDateConversionTest.php",
"chars": 2376,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Encoding;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/Encoding/UnifyAudienceTest.php",
"chars": 1664,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Encoding;\n\nuse Lcobucci\\JWT\\Encoding\\UnifyAudience;\nuse Lco"
},
{
"path": "tests/Encoding/UnixTimestampDatesTest.php",
"chars": 2295,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Encoding;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/HmacTokenTest.php",
"chars": 7194,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/JwtFacadeTest.php",
"chars": 9535,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse AssertionError;\nuse DateTimeImmutable;\nuse Lcobucci\\C"
},
{
"path": "tests/KeyDumpSigner.php",
"chars": 669,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signer\\Key;\n\nfi"
},
{
"path": "tests/Keys.php",
"chars": 2876,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Signer\\Key;\nuse PHPUnit\\Framework\\Attrib"
},
{
"path": "tests/MaliciousTamperingPreventionTest.php",
"chars": 4526,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/RFC6978VectorTest.php",
"chars": 5341,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\nuse Lcobucci\\JWT\\Signer\\Ec"
},
{
"path": "tests/RsaTokenTest.php",
"chars": 7500,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Configuration;\nuse Lcobucci\\JWT\\Encoding"
},
{
"path": "tests/Signer/Blake2bTest.php",
"chars": 2822,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer;\n\nuse Lcobucci\\JWT\\Signer\\Blake2b;\nuse Lcobucci\\JWT\\"
},
{
"path": "tests/Signer/Ecdsa/EcdsaTestCase.php",
"chars": 4563,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\nuse Lcobucci\\"
},
{
"path": "tests/Signer/Ecdsa/MultibyteStringConverterTest.php",
"chars": 4663,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa\\ConversionFail"
},
{
"path": "tests/Signer/Ecdsa/Sha256Test.php",
"chars": 1485,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\nuse Lcobucci\\"
},
{
"path": "tests/Signer/Ecdsa/Sha384Test.php",
"chars": 1490,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\nuse Lcobucci\\"
},
{
"path": "tests/Signer/Ecdsa/Sha512Test.php",
"chars": 1491,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Ecdsa;\n\nuse Lcobucci\\JWT\\Signer\\Ecdsa;\nuse Lcobucci\\"
},
{
"path": "tests/Signer/EddsaTest.php",
"chars": 4018,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer;\n\nuse Lcobucci\\JWT\\Encoding\\JoseEncoder;\nuse Lcobucc"
},
{
"path": "tests/Signer/FakeSigner.php",
"chars": 688,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobucci\\JWT\\Signer\\K"
},
{
"path": "tests/Signer/Hmac/HmacTestCase.php",
"chars": 2933,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\nuse Lcobucci\\JW"
},
{
"path": "tests/Signer/Hmac/Sha256Test.php",
"chars": 820,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\nuse Lcobucci\\JW"
},
{
"path": "tests/Signer/Hmac/Sha384Test.php",
"chars": 820,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\nuse Lcobucci\\JW"
},
{
"path": "tests/Signer/Hmac/Sha512Test.php",
"chars": 820,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Hmac;\n\nuse Lcobucci\\JWT\\Signer\\Hmac;\nuse Lcobucci\\JW"
},
{
"path": "tests/Signer/Key/InMemoryTest.php",
"chars": 2995,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Key;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;"
},
{
"path": "tests/Signer/Key/empty.pem",
"chars": 0,
"preview": ""
},
{
"path": "tests/Signer/Key/test.pem",
"chars": 7,
"preview": "testing"
},
{
"path": "tests/Signer/Rsa/KeyValidationSigner.php",
"chars": 864,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\Key;\nuse Lcobucci\\JWT\\"
},
{
"path": "tests/Signer/Rsa/KeyValidationTest.php",
"chars": 1385,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\CannotSignPayload;\nuse"
},
{
"path": "tests/Signer/Rsa/RsaTestCase.php",
"chars": 4539,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\InvalidKeyProvided;\nus"
},
{
"path": "tests/Signer/Rsa/Sha256Test.php",
"chars": 840,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\InvalidKeyProvided;\nus"
},
{
"path": "tests/Signer/Rsa/Sha384Test.php",
"chars": 840,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\InvalidKeyProvided;\nus"
},
{
"path": "tests/Signer/Rsa/Sha512Test.php",
"chars": 840,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Signer\\Rsa;\n\nuse Lcobucci\\JWT\\Signer\\InvalidKeyProvided;\nus"
},
{
"path": "tests/SodiumBase64PolyfillTest.php",
"chars": 4089,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse Lcobucci\\JWT\\Encoding\\CannotDecodeContent;\nuse Lcobuc"
},
{
"path": "tests/TimeFractionPrecisionTest.php",
"chars": 3853,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Configuration;\nus"
},
{
"path": "tests/Token/BuilderTest.php",
"chars": 5956,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Token;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Encoder;\nus"
},
{
"path": "tests/Token/DataSetTest.php",
"chars": 1705,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Token;\n\nuse Lcobucci\\JWT\\Token\\DataSet;\nuse PHPUnit\\Framewo"
},
{
"path": "tests/Token/ParserTest.php",
"chars": 17379,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Token;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Decoder;\nus"
},
{
"path": "tests/Token/PlainTest.php",
"chars": 10244,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Token;\n\nuse DateTimeImmutable;\nuse Lcobucci\\JWT\\Token\\DataS"
},
{
"path": "tests/Token/SignatureTest.php",
"chars": 524,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Token;\n\nuse Lcobucci\\JWT\\Token\\Signature;\nuse PHPUnit\\Frame"
},
{
"path": "tests/UnsignedTokenTest.php",
"chars": 5764,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse DateTimeImmutable;\nuse Lcobucci\\Clock\\FrozenClock;\nus"
},
{
"path": "tests/UnsupportedParser.php",
"chars": 1483,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests;\n\nuse DateTimeInterface;\nuse Lcobucci\\JWT\\Parser;\nuse Lcobu"
},
{
"path": "tests/Validation/Constraint/ConstraintTestCase.php",
"chars": 1145,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Closure;\nuse Lcobucci\\JWT\\Build"
},
{
"path": "tests/Validation/Constraint/HasClaimTest.php",
"chars": 2431,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/HasClaimWithValueTest.php",
"chars": 2941,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/IdentifiedByTest.php",
"chars": 1627,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/IssuedByTest.php",
"chars": 2085,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/LooseValidAtTest.php",
"chars": 1159,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse DateInterval;\nuse Lcobucci\\Cloc"
},
{
"path": "tests/Validation/Constraint/PermittedForTest.php",
"chars": 2109,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/RelatedToTest.php",
"chars": 1667,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucc"
},
{
"path": "tests/Validation/Constraint/SignedWithOneInSetTest.php",
"chars": 3388,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse DateTimeImmutable;\nuse Lcobucci"
},
{
"path": "tests/Validation/Constraint/SignedWithTest.php",
"chars": 3232,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse Lcobucci\\JWT\\Signer;\nuse Lcobuc"
},
{
"path": "tests/Validation/Constraint/SignedWithUntilDateTest.php",
"chars": 4603,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse DateTimeImmutable;\nuse Lcobucci"
},
{
"path": "tests/Validation/Constraint/StrictValidAtTest.php",
"chars": 2716,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse DateInterval;\nuse Lcobucci\\Cloc"
},
{
"path": "tests/Validation/Constraint/ValidAtTestCase.php",
"chars": 4728,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation\\Constraint;\n\nuse DateInterval;\nuse DateTimeImmut"
},
{
"path": "tests/Validation/ConstraintViolationTest.php",
"chars": 730,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation;\n\nuse Lcobucci\\JWT\\Validation\\Constraint\\Identif"
},
{
"path": "tests/Validation/RequiredConstraintsViolatedTest.php",
"chars": 904,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation;\n\nuse Lcobucci\\JWT\\Validation\\ConstraintViolatio"
},
{
"path": "tests/Validation/ValidatorTest.php",
"chars": 3657,
"preview": "<?php\ndeclare(strict_types=1);\n\nnamespace Lcobucci\\JWT\\Tests\\Validation;\n\nuse Lcobucci\\JWT\\Token;\nuse Lcobucci\\JWT\\Valid"
},
{
"path": "tests/_keys/ecdsa/private.key",
"chars": 226,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIBGpMoZJ64MMSzuo5JbmXpf9V4qSWdLIl/8RmJLcfn/qoAoGCCqGSM49\nAwEHoUQDQgAE7it/EKmcv9bf"
},
{
"path": "tests/_keys/ecdsa/private2.key",
"chars": 302,
"preview": "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIM6G7W"
},
{
"path": "tests/_keys/ecdsa/private_ec384.key",
"chars": 359,
"preview": "-----BEGIN EC PARAMETERS-----\nBgUrgQQAIg==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDC9EkM5Bm"
},
{
"path": "tests/_keys/ecdsa/private_ec512.key",
"chars": 365,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMIHcAgEBBEIAD3yO2xWzu9sSrWXsUcsjppnOPGisza3C4cAL5eAV65xAm4Q6o0LP\nD7xIBDhxxqDXHsV9iGwbO0yy"
},
{
"path": "tests/_keys/ecdsa/public1.key",
"chars": 177,
"preview": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7it/EKmcv9bfpcV1fBreLMRXxWpn\nd0wxa2iFruiI2tsEdGFTLTsyU+Ge"
},
{
"path": "tests/_keys/ecdsa/public2.key",
"chars": 177,
"preview": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdgxRxlhzhHGj+v6S2ikp+33LoGp5\nQWbEWv8BORsr2Ayg6C7deDDRM/s/"
},
{
"path": "tests/_keys/ecdsa/public2_ec512.key",
"chars": 268,
"preview": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBpHZ4gSVGEeYFWsHYNDMSO49wPtsP\n4/yAqywK7D+OQ5P+1yhM3tAUm8wn"
},
{
"path": "tests/_keys/ecdsa/public3.key",
"chars": 178,
"preview": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE09Hkp/u0tIGdzlQ99R/sXCOr9DTZ\nAfLex4D4Po0C1L3qUqHrzZ0mB3bA"
},
{
"path": "tests/_keys/ecdsa/public_ec384.key",
"chars": 215,
"preview": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfOqWfWSvBFpBbKAY/IncDCal7TjCeBgf\nuhhKtf7sqYK55B27YFi00SYFp5Yr"
},
{
"path": "tests/_keys/ecdsa/public_ec512.key",
"chars": 268,
"preview": "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQATqtkp30ZR4tDn8YOiUxTfii29+hV\nsUq7EsyLLWVusyyGD0t5AGCRd3fo"
},
{
"path": "tests/_keys/rsa/encrypted-private.key",
"chars": 1766,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,0D71668CE71033CB9150ED82FC87F4A1\n\nuLzPNDdlH"
},
{
"path": "tests/_keys/rsa/encrypted-public.key",
"chars": 450,
"preview": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLpbUP8a9yflt5LKUUS3\nNPuRM7yEouPWg0VKeY5AURu4i8bq"
},
{
"path": "tests/_keys/rsa/private.key",
"chars": 1703,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDTvwE87MtgREYL\nTL4aHhQo3ZzogmxxvMUsKnPzyxR"
},
{
"path": "tests/_keys/rsa/private_512.key",
"chars": 522,
"preview": "-----BEGIN PRIVATE KEY-----\nMIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAxZT4cHZXf5QfGX1m\noiSKKSC6AeFO8tGIn9C+4x/bEaQ"
},
{
"path": "tests/_keys/rsa/public.key",
"chars": 450,
"preview": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA078BPOzLYERGC0y+Gh4U\nKN2c6IJscbzFLCpz88sUbNWK1zkj"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the lcobucci/jwt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 201 files (378.5 KB), approximately 108.8k tokens, and a symbol index with 784 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.