diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6337909 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space + +[*.{md,yml,yaml,css,json}] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 0000000..3b0a865 --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,7 @@ +# GitBook serves the domain until it points to GitHub Pages. The pages moved to docs/ for MkDocs, and this +# tells GitBook where they are. It goes away with the GitBook space. +root: ./docs/ + +structure: + readme: README.md + summary: SUMMARY.md diff --git a/.github/workflows/auto-switch-default-branch.yml b/.github/workflows/auto-switch-default-branch.yml deleted file mode 100644 index a10fc79..0000000 --- a/.github/workflows/auto-switch-default-branch.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: Auto-switch default branch on new branch - -# Se déclenche lorsqu'une branche est créée -on: - create: - -jobs: - update-default-branch: - # On ne traite que les créations de branches (pas les tags) - if: github.event.ref_type == 'branch' - runs-on: ubuntu-latest - permissions: - contents: read - - # Changer la branche par défaut relève de la permission "administration", - # que le GITHUB_TOKEN d'Actions ne peut pas obtenir. Il faut un jeton personnel - # (PAT) avec le scope "repo", stocké dans le secret DEFAULT_BRANCH_TOKEN. - env: - GH_TOKEN: ${{ secrets.DEFAULT_BRANCH_TOKEN }} - - steps: - - name: Vérifier que le jeton est disponible - id: token - run: | - if [ -z "${GH_TOKEN}" ]; then - echo "Le secret DEFAULT_BRANCH_TOKEN n'est pas défini, on ignore." - echo "available=false" >> "$GITHUB_OUTPUT" - else - echo "available=true" >> "$GITHUB_OUTPUT" - fi - - - name: Extraire la version depuis le nom de la branche - id: vars - if: steps.token.outputs.available == 'true' - run: | - ref="${GITHUB_REF#refs/heads/}" # ex: 1.2, v1.2, 1.2.x, v1.2.x - echo "Branch ref: $ref" - - # On enlève un éventuel préfixe 'v' (v1.2 -> 1.2, v1.2.x -> 1.2.x) - version="${ref#v}" - - # On enlève un éventuel suffixe '.x' (1.2.x -> 1.2) - version="${version%.x}" - - echo "Version nettoyée: $version" - - # Vérifier que c'est bien au format major.minor - if [[ ! "$version" =~ ^([0-9]+)\.([0-9]+)$ ]]; then - echo "Le nom de branche '$ref' n'est pas un format de version reconnu, on ignore." - echo "valid=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - major="${BASH_REMATCH[1]}" - minor="${BASH_REMATCH[2]}" - - echo "Major: $major" - echo "Minor: $minor" - - echo "valid=true" >> "$GITHUB_OUTPUT" - echo "major=$major" >> "$GITHUB_OUTPUT" - echo "minor=$minor" >> "$GITHUB_OUTPUT" - echo "new_branch=$ref" >> "$GITHUB_OUTPUT" - - - name: Récupérer la branche par défaut actuelle - if: steps.vars.outputs.valid == 'true' - id: repo - run: | - default_branch=$(gh repo view "${GITHUB_REPOSITORY}" --json defaultBranchRef --jq .defaultBranchRef.name) - echo "Branche par défaut actuelle: $default_branch" - echo "default_branch=$default_branch" >> "$GITHUB_OUTPUT" - - - name: Décider s'il faut basculer - if: steps.vars.outputs.valid == 'true' - id: decide - run: | - new_branch="${{ steps.vars.outputs.new_branch }}" - current="${{ steps.repo.outputs.default_branch }}" - major="${{ steps.vars.outputs.major }}" - minor="${{ steps.vars.outputs.minor }}" - - echo "Branche par défaut actuelle: $current" - echo "Nouvelle branche: $new_branch" - echo "Version de la nouvelle branche: $major.$minor" - - # Nettoyer le nom de la branche par défaut actuelle pour en extraire la version - current_clean="${current#v}" - current_clean="${current_clean%.x}" - - if [[ "$current_clean" =~ ^([0-9]+)\.([0-9]+)$ ]]; then - current_major="${BASH_REMATCH[1]}" - current_minor="${BASH_REMATCH[2]}" - echo "Version actuelle parsée: $current_major.$current_minor" - else - echo "La branche par défaut actuelle '$current' n'a pas un format de version reconnu, on ignore." - echo "switch_needed=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Comparaison (major, minor) - if [ "$major" -gt "$current_major" ] || { [ "$major" -eq "$current_major" ] && [ "$minor" -gt "$current_minor" ]; }; then - echo "La nouvelle version $major.$minor est supérieure à $current_major.$current_minor → on bascule." - echo "switch_needed=true" >> "$GITHUB_OUTPUT" - else - echo "La nouvelle version $major.$minor n'est pas supérieure à $current_major.$current_minor → pas de changement." - echo "switch_needed=false" >> "$GITHUB_OUTPUT" - fi - - - name: Mettre à jour la branche par défaut - if: steps.vars.outputs.valid == 'true' && steps.decide.outputs.switch_needed == 'true' - run: | - new_branch="${{ steps.vars.outputs.new_branch }}" - echo "Basculement de la branche par défaut vers: $new_branch" - - gh api -X PATCH "repos/${GITHUB_REPOSITORY}" \ - -f default_branch="$new_branch" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..aab53ac --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,38 @@ +name: Check + +on: + pull_request: + workflow_dispatch: + +# MkDocs 2.0 is announced as backward incompatible with every plugin and theme, so the toolchain is pinned +# to the 1.x line in requirements.txt and the two notices about it are silenced: they are news about the +# ecosystem, not findings about this site. +env: + NO_MKDOCS_2_WARNING: 'true' + DISABLE_MKDOCS_2_WARNING: 'true' + +permissions: + contents: read + +jobs: + build: + name: Strict build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v6 + with: + python-version: '3.13' + cache: pip + cache-dependency-path: requirements.txt + + - name: Install the toolchain + run: pip install -r requirements.txt + + # --strict turns every warning into an error: a broken internal link, an anchor that does not + # exist, a page absent from the navigation or a page in the navigation that does not exist. + - name: Build + run: mkdocs build --strict diff --git a/.github/workflows/default-branch.yml b/.github/workflows/default-branch.yml new file mode 100644 index 0000000..fdee05b --- /dev/null +++ b/.github/workflows/default-branch.yml @@ -0,0 +1,73 @@ +name: Default branch + +# A new version branch becomes the default branch of the repository, so that a clone and the "edit this +# page" links land on the newest documentation. Changing the default branch is an administration +# permission, which the token of Actions cannot be granted: it needs a personal access token with the +# "repo" scope, in the DEFAULT_BRANCH_TOKEN secret. Without it the workflow does nothing. +on: + create: + workflow_dispatch: + +jobs: + switch: + if: github.event_name == 'workflow_dispatch' || github.event.ref_type == 'branch' + runs-on: ubuntu-latest + permissions: + contents: read + env: + GH_TOKEN: ${{ secrets.DEFAULT_BRANCH_TOKEN }} + steps: + - name: Is the token available? + id: token + run: | + if [ -z "${GH_TOKEN}" ]; then + echo "DEFAULT_BRANCH_TOKEN is not set, nothing to do." + echo "available=false" >> "$GITHUB_OUTPUT" + else + echo "available=true" >> "$GITHUB_OUTPUT" + fi + + - name: The version of the branch that was created + id: created + if: steps.token.outputs.available == 'true' + run: | + ref="${GITHUB_REF#refs/heads/}" + version="${ref#v}" + version="${version%.x}" + if [[ ! "$version" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + echo "The branch '$ref' is not a version, nothing to do." + echo "valid=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "valid=true" >> "$GITHUB_OUTPUT" + echo "branch=$ref" >> "$GITHUB_OUTPUT" + echo "major=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + echo "minor=${BASH_REMATCH[2]}" >> "$GITHUB_OUTPUT" + + - name: Is it newer than the default branch? + id: decide + if: steps.created.outputs.valid == 'true' + run: | + current=$(gh repo view "${GITHUB_REPOSITORY}" --json defaultBranchRef --jq .defaultBranchRef.name) + clean="${current#v}" + clean="${clean%.x}" + if [[ ! "$clean" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + echo "The current default branch '$current' is not a version, nothing to do." + echo "switch=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + major="${{ steps.created.outputs.major }}" + minor="${{ steps.created.outputs.minor }}" + if [ "$major" -gt "${BASH_REMATCH[1]}" ] || { [ "$major" -eq "${BASH_REMATCH[1]}" ] && [ "$minor" -gt "${BASH_REMATCH[2]}" ]; }; then + echo "${major}.${minor} is newer than $clean, switching." + echo "switch=true" >> "$GITHUB_OUTPUT" + else + echo "${major}.${minor} is not newer than $clean, nothing to do." + echo "switch=false" >> "$GITHUB_OUTPUT" + fi + + - name: Switch it + if: steps.decide.outputs.switch == 'true' + run: | + gh api -X PATCH "repos/${GITHUB_REPOSITORY}" \ + -f default_branch="${{ steps.created.outputs.branch }}" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..6971ddc --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,111 @@ +name: Pages + +# One version of the site per branch, published by mike into gh-pages: 4.0 is served at /4.0/, and the +# alias "latest" follows the highest version, which is what the root of the site redirects to. A branch is +# named 4.0, or 4.0.x like the maintenance branches of a library; 4.x is the next minor. Pages is +# configured to deploy from the gh-pages branch, root folder. +on: + push: + branches: + - '[0-9]*.[0-9]*' + - '[0-9]*.x' + paths-ignore: + - '.github/workflows/check.yml' + workflow_dispatch: + +# MkDocs 2.0 is announced as backward incompatible with every plugin and theme, so the toolchain is pinned +# to the 1.x line in requirements.txt and the two notices about it are silenced: they are news about the +# ecosystem, not findings about this site. +env: + NO_MKDOCS_2_WARNING: 'true' + DISABLE_MKDOCS_2_WARNING: 'true' + +permissions: + contents: write + +# Two branches pushed at once must not write gh-pages at the same time, and a queued run is never +# cancelled: it carries a version the other does not. +concurrency: + group: gh-pages + cancel-in-progress: false + +jobs: + deploy: + name: Deploy ${{ github.ref_name }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v6 + with: + python-version: '3.13' + cache: pip + cache-dependency-path: requirements.txt + + - name: Install the toolchain + run: pip install -r requirements.txt + + - name: Identify the committer + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # The version a branch publishes: 4.0.x publishes 4.0, any other name publishes itself. + - name: The version of this branch + id: version + run: | + version="${GITHUB_REF_NAME}" + if [[ "$version" =~ ^[0-9]+\.[0-9]+\.x$ ]]; then + version="${version%.x}" + fi + echo "The branch '${GITHUB_REF_NAME}' publishes the version '$version'." + echo "name=$version" >> "$GITHUB_OUTPUT" + + # The highest version branch of the repository, by version order, in which a "1.x" branch comes + # after "1.2" and "1.2.x": it is where the next minor is written. sort -V alone puts "1.x" first, + # so each name is sorted by a key where "x" is a large number. Only that branch carries "latest". + - name: Is this the newest version? + id: newest + run: | + git fetch --quiet origin '+refs/heads/*:refs/remotes/origin/*' + newest=$(git for-each-ref --format='%(refname:lstrip=3)' 'refs/remotes/origin/*' \ + | grep -E '^[0-9]+\.([0-9]+(\.x)?|x)$' \ + | awk '{ key = $0; if (key ~ /^[0-9]+\.x$/) sub(/x$/, "999999", key); else sub(/\.x$/, "", key); print key " " $0 }' \ + | sort -V -k1,1 \ + | tail -n 1 \ + | cut -d' ' -f2) + echo "The newest version branch is '$newest', this one is '${GITHUB_REF_NAME}'." + if [ "$newest" = "${GITHUB_REF_NAME}" ]; then + echo "latest=true" >> "$GITHUB_OUTPUT" + else + echo "latest=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish this version + if: steps.newest.outputs.latest == 'false' + run: mike deploy --push --update-aliases "${{ steps.version.outputs.name }}" + + - name: Publish this version as the latest one + if: steps.newest.outputs.latest == 'true' + run: | + mike deploy --push --update-aliases "${{ steps.version.outputs.name }}" latest + mike set-default --push latest + + # The root of gh-pages is not a version: mike leaves it alone, and it holds what GitHub Pages reads + # there. site-root/ in this branch is copied over it: CNAME, the domain of the site, and 404.html, + # which sends the addresses of the former GitBook site to the latest version. + - name: Publish the root of the site + run: | + git fetch --quiet origin gh-pages + git worktree add "${RUNNER_TEMP}/gh-pages" origin/gh-pages + cp -R site-root/. "${RUNNER_TEMP}/gh-pages/" + cd "${RUNNER_TEMP}/gh-pages" + git add --all + if git diff --cached --quiet; then + echo "The root of the site is up to date." + else + git commit --quiet -m "Publish the root of the site from ${GITHUB_REF_NAME}" + git push origin HEAD:gh-pages + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f576159 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/site/ +/.venv/ +/.cache/ +__pycache__/ +.idea/ +.claude/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..80eb70b --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# The documentation of Beffroi. "make install" once, "make serve" to write, "make check" before a pull +# request. Every target runs in .venv, created from requirements.txt, with uv when it is installed. + +VENV := .venv +BIN := $(VENV)/bin +UV := $(shell command -v uv 2>/dev/null) + +# News about the MkDocs ecosystem, not findings about this site. requirements.txt pins the 1.x line. +export NO_MKDOCS_2_WARNING := true +export DISABLE_MKDOCS_2_WARNING := true + +.DEFAULT_GOAL := help +.PHONY: help install serve build check versions clean + +help: ## Show this help + @grep -E '^[a-z-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-10s\033[0m %s\n", $$1, $$2}' + +$(BIN)/mkdocs: requirements.txt +ifdef UV + uv venv --allow-existing $(VENV) + VIRTUAL_ENV=$(VENV) uv pip install -r requirements.txt +else + python3 -m venv $(VENV) + $(BIN)/pip install --upgrade pip + $(BIN)/pip install -r requirements.txt +endif + @touch $(BIN)/mkdocs + +install: $(BIN)/mkdocs ## Install the toolchain in .venv + +serve: install ## Serve the site on http://127.0.0.1:8000, rebuilt as you save + $(BIN)/mkdocs serve + +build: install ## Build the site into site/ + $(BIN)/mkdocs build + +check: install ## Build strictly: a broken link, a missing anchor or a page outside the navigation fails + $(BIN)/mkdocs build --strict + +versions: install ## List the versions published on gh-pages + $(BIN)/mike list + +clean: ## Remove the build and the virtual environment + rm -rf site $(VENV) diff --git a/README.md b/README.md index 0e54b20..b979d4b 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,51 @@ -# Introduction +# The documentation of JWT Framework -Welcome to the **JWT Framework** documentation. This is a modern, secure, and comprehensive PHP library for working with JSON Web Tokens (JWT), designed for both standalone applications and Symfony projects. +The public documentation of [JWT Framework](https://github.com/web-token/jwt-framework), JSON Web Tokens for +PHP and Symfony: signature (JWS), encryption (JWE), keys (JWK) and algorithms (JWA), the components, the +Symfony bundle and the console commands. -## What is JWT Framework? +Published with [MkDocs](https://www.mkdocs.org/) and +[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) on GitHub Pages: +**** -JWT Framework is a complete implementation of the JOSE (JSON Object Signing and Encryption) specifications, providing robust tools for creating, signing, encrypting, and validating JSON Web Tokens. It follows industry standards and security best practices to help you implement secure token-based authentication and data exchange in your applications. - -## Supported Standards - -This framework provides full implementation of the following RFCs: - -* **JWS** - [JSON Web Signature (RFC 7515)](https://tools.ietf.org/html/rfc7515) - Sign your tokens to ensure integrity and authenticity -* **JWE** - [JSON Web Encryption (RFC 7516)](https://tools.ietf.org/html/rfc7516) - Encrypt your tokens to ensure confidentiality -* **JWK** - [JSON Web Key (RFC 7517)](https://tools.ietf.org/html/rfc7517) - Manage cryptographic keys in a standardized format -* **JWA** - [JSON Web Algorithms (RFC 7518)](https://tools.ietf.org/html/rfc7518) - Use industry-standard cryptographic algorithms -* **JWT** - [JSON Web Token (RFC 7519)](https://tools.ietf.org/html/rfc7519) - Create and validate token claims -* **JSON Web Key Thumbprint** - [RFC 7638](https://tools.ietf.org/html/rfc7638) - Generate unique key identifiers -* **Unencoded Payload Option** - [RFC 7797](https://tools.ietf.org/html/rfc7797) - Support for unencoded payloads in JWS - -## Key Features - -### 🔐 Comprehensive Security -- Support for all standard signature algorithms (HMAC, RSA, ECDSA, EdDSA) -- Support for all standard encryption algorithms (AES-GCM, AES-CBC, RSA-OAEP, ECDH-ES) -- Built-in header and claim validation -- Protection against common JWT vulnerabilities - -### 🛠️ Flexible Integration -- **Standalone library** - Use it in any PHP project -- **Symfony Bundle** - Seamless integration with Symfony applications -- **Console commands** - CLI tools for key management and token inspection - -### 🚀 Developer Friendly -- Fluent API for building and loading tokens -- Factory pattern for creating services -- PSR-20 Clock support for time-based validation -- Comprehensive error handling - -### 📦 Production Ready -- Extensively tested against RFC test vectors -- Support for nested tokens (signed then encrypted) -- Multiple serialization formats (Compact, JSON Flattened, JSON General) -- Key set management for key rotation - -## Installation - -Install the package that matches your project: +## Writing ```bash -# Any PHP project — the core library -composer require web-token/jwt-library - -# Symfony application — the bundle (pulls the library in) -composer require web-token/jwt-bundle +make install # the toolchain, in .venv, from requirements.txt +make serve # http://127.0.0.1:8000, rebuilt as you save +make check # the strict build continuous integration runs ``` -For Symfony projects, the bundle is automatically registered. - -## Quick Start +`make check` is the gate: a broken internal link, an anchor that does not exist, a page absent from the +navigation or a navigation entry without a page fails the build. -```php -use Jose\Component\Core\AlgorithmManager; -use Jose\Component\Core\JWK; -use Jose\Component\Signature\Algorithm\HS256; -use Jose\Component\Signature\JWSBuilder; -use Jose\Component\Signature\Serializer\CompactSerializer; +## Where things are -// Create an algorithm manager -$algorithmManager = new AlgorithmManager([new HS256()]); +| Path | What it is | +|---|---| +| `mkdocs.yml` | The site: theme, extensions, plugins, and the whole navigation | +| `docs/` | The pages, one directory per section of the navigation | +| `docs/assets/stylesheets/` | The look: `slabs-tokens.css`, copied as it is from the Slabs design kit, and `slabs.css`, which applies the guidelines to the theme | +| `docs/assets/brand/`, `docs/assets/fonts/` | The project mark, the signature, the favicons and the share image of the design kit, and its typefaces (SIL Open Font License) | +| `docs/overrides/` | Theme partials: the logo, the footer, the link to the repository | +| `site-root/` | The root of the published site, copied to the root of the `gh-pages` branch: `CNAME`, and the `404.html` that sends the addresses of the former GitBook site to their version | +| `requirements.txt` | The toolchain, pinned. Continuous integration installs exactly this | +| `.github/workflows/check.yml` | The strict build, on every pull request | +| `.github/workflows/pages.yml` | The publication: one version per branch, through mike | +| `.github/workflows/default-branch.yml` | A new version branch becomes the default branch | +| `docs/SUMMARY.md`, `.gitbook.yaml` | The GitBook space, until the domain points to GitHub Pages | -// Create a JWS Builder -$jwsBuilder = new JWSBuilder($algorithmManager); +How the documentation left GitBook, and the rules every documentation site of Slabs follows, are in +[Spomky-Labs/docs](https://github.com/Spomky-Labs/docs/blob/main/MIGRATING.md). -// Create a key -$jwk = new JWK([ - 'kty' => 'oct', - 'k' => 'your-secret-key-here', -]); +## One branch per version -// Build and sign your token -$jws = $jwsBuilder - ->create() - ->withPayload(json_encode(['user_id' => 123])) - ->addSignature($jwk, ['alg' => 'HS256']) - ->build(); +A branch of this repository is a version of the packages: `4.0`, `4.1`, `4.2`, `4.3`. A push to one of +them publishes it under its own name, and the newest one also carries the `latest` alias, which the root +of the site redirects to. A fix that applies to several versions is merged forward. -// Serialize to compact format -$serializer = new CompactSerializer(); -$token = $serializer->serialize($jws, 0); +```bash +git switch -c 4.4 4.3 # from the version it continues +# in mkdocs.yml, point edit_uri at the new branch +git push -u origin 4.4 # publishes it, and moves "latest" and the default branch ``` - -## Documentation Structure - -This documentation is organized to help you get started quickly and dive deep when needed: - -* **[Introduction](introduction/the-framework.md)** - Overview, features, prerequisites, and security recommendations -* **[The Components](the-components/algorithm-management-jwa.md)** - Core library documentation for standalone usage -* **[The Symfony Bundle](the-symfony-bundle/symfony-bundle.md)** - Integration guide for Symfony applications -* **[Console Commands](console-command/console.md)** - CLI tools for key and token management -* **[Advanced Topics](advanced-topics/nested-tokens.md)** - Nested tokens, custom algorithms, and advanced features -* **[Migration Guides](migration/from-v4.1-to-v4.2.md)** - Upgrade guides for major versions - -## Requirements - -* PHP 8.2 or higher -* OpenSSL extension -* Recommended: GMP or BCMath for better performance -* Recommended: Sodium extension for EdDSA and ECDH-ES algorithms - -## Get Help - -* **GitHub Issues**: [https://github.com/web-token/jwt-framework/issues](https://github.com/web-token/jwt-framework/issues) -* **Documentation**: [https://web-token.spomky-labs.com](https://web-token.spomky-labs.com) -* **Security Issues**: Please report security vulnerabilities privately to the maintainers - -## Contributing - -Contributions are welcome! Please read the [contributing guidelines](introduction/contributing.md) before submitting pull requests. - -## License - -This project is released under the [MIT license](https://github.com/web-token/jwt-framework/blob/4.2.x/LICENSE). diff --git a/advanced-topics/encrypted-tokens/README.md b/advanced-topics/encrypted-tokens/README.md deleted file mode 100644 index 3bc1bd6..0000000 --- a/advanced-topics/encrypted-tokens/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Encrypted Tokens - diff --git a/advanced-topics/signed-tokens/README.md b/advanced-topics/signed-tokens/README.md deleted file mode 100644 index 7e60820..0000000 --- a/advanced-topics/signed-tokens/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Signed Tokens - diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c4b5735 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,123 @@ +# Introduction + +Welcome to the **JWT Framework** documentation. This is a modern, secure, and comprehensive PHP library for working with JSON Web Tokens (JWT), designed for both standalone applications and Symfony projects. + +## What is JWT Framework? + +JWT Framework is a complete implementation of the JOSE (JSON Object Signing and Encryption) specifications, providing robust tools for creating, signing, encrypting, and validating JSON Web Tokens. It follows industry standards and security best practices to help you implement secure token-based authentication and data exchange in your applications. + +## Supported standards + +This framework provides full implementation of the following RFCs: + +* **JWS** - [JSON Web Signature (RFC 7515)](https://tools.ietf.org/html/rfc7515) - Sign your tokens to ensure integrity and authenticity +* **JWE** - [JSON Web Encryption (RFC 7516)](https://tools.ietf.org/html/rfc7516) - Encrypt your tokens to ensure confidentiality +* **JWK** - [JSON Web Key (RFC 7517)](https://tools.ietf.org/html/rfc7517) - Manage cryptographic keys in a standardized format +* **JWA** - [JSON Web Algorithms (RFC 7518)](https://tools.ietf.org/html/rfc7518) - Use industry-standard cryptographic algorithms +* **JWT** - [JSON Web Token (RFC 7519)](https://tools.ietf.org/html/rfc7519) - Create and validate token claims +* **JSON Web Key Thumbprint** - [RFC 7638](https://tools.ietf.org/html/rfc7638) - Generate unique key identifiers +* **Unencoded Payload Option** - [RFC 7797](https://tools.ietf.org/html/rfc7797) - Support for unencoded payloads in JWS + +## Key features + +### 🔐 comprehensive security +- Support for all standard signature algorithms (HMAC, RSA, ECDSA, EdDSA) +- Support for all standard encryption algorithms (AES-GCM, AES-CBC, RSA-OAEP, ECDH-ES) +- Built-in header and claim validation +- Protection against common JWT vulnerabilities + +### 🛠️ flexible integration +- **Standalone library** - Use it in any PHP project +- **Symfony Bundle** - Seamless integration with Symfony applications +- **Console commands** - CLI tools for key management and token inspection + +### 🚀 developer friendly +- Fluent API for building and loading tokens +- Factory pattern for creating services +- PSR-20 Clock support for time-based validation +- Comprehensive error handling + +### 📦 production ready +- Extensively tested against RFC test vectors +- Support for nested tokens (signed then encrypted) +- Multiple serialization formats (Compact, JSON Flattened, JSON General) +- Key set management for key rotation + +## Installation + +Install the package that matches your project: + +```bash +# Any PHP project — the core library +composer require web-token/jwt-library + +# Symfony application — the bundle (pulls the library in) +composer require web-token/jwt-bundle +``` + +For Symfony projects, the bundle is automatically registered. + +## Quick start + +```php +use Jose\Component\Core\AlgorithmManager; +use Jose\Component\Core\JWK; +use Jose\Component\Signature\Algorithm\HS256; +use Jose\Component\Signature\JWSBuilder; +use Jose\Component\Signature\Serializer\CompactSerializer; + +// Create an algorithm manager +$algorithmManager = new AlgorithmManager([new HS256()]); + +// Create a JWS Builder +$jwsBuilder = new JWSBuilder($algorithmManager); + +// Create a key +$jwk = new JWK([ + 'kty' => 'oct', + 'k' => 'your-secret-key-here', +]); + +// Build and sign your token +$jws = $jwsBuilder + ->create() + ->withPayload(json_encode(['user_id' => 123])) + ->addSignature($jwk, ['alg' => 'HS256']) + ->build(); + +// Serialize to compact format +$serializer = new CompactSerializer(); +$token = $serializer->serialize($jws, 0); +``` + +## Documentation structure + +This documentation is organized to help you get started quickly and dive deep when needed: + +* **[Introduction](introduction/the-framework.md)** - Overview, features, prerequisites, and security recommendations +* **[The Components](the-components/algorithm-management-jwa.md)** - Core library documentation for standalone usage +* **[The Symfony Bundle](the-symfony-bundle/symfony-bundle.md)** - Integration guide for Symfony applications +* **[Console Commands](console-command/console.md)** - CLI tools for key and token management +* **[Advanced Topics](advanced-topics/nested-tokens.md)** - Nested tokens, custom algorithms, and advanced features +* **[Migration Guides](migration/from-v4.1-to-v4.2.md)** - Upgrade guides for major versions + +## Requirements + +* PHP 8.2 or higher +* OpenSSL extension +* Recommended: GMP or BCMath for better performance +* Recommended: Sodium extension for EdDSA and ECDH-ES algorithms + +## Get help + +* **GitHub Issues**: [https://github.com/web-token/jwt-framework/issues](https://github.com/web-token/jwt-framework/issues) +* **Documentation**: [https://web-token.spomky-labs.com](https://web-token.spomky-labs.com) +* **Security Issues**: Please report security vulnerabilities privately to the maintainers + +## Contributing + +Contributions are welcome! Please read the [contributing guidelines](introduction/contributing.md) before submitting pull requests. + +## License + +This project is released under the [MIT license](https://github.com/web-token/jwt-framework/blob/4.2.x/LICENSE). diff --git a/SUMMARY.md b/docs/SUMMARY.md similarity index 100% rename from SUMMARY.md rename to docs/SUMMARY.md diff --git a/advanced-topics/custom-algorithm.md b/docs/advanced-topics/custom-algorithm.md similarity index 94% rename from advanced-topics/custom-algorithm.md rename to docs/advanced-topics/custom-algorithm.md index 82128c0..e6bb758 100644 --- a/advanced-topics/custom-algorithm.md +++ b/docs/advanced-topics/custom-algorithm.md @@ -1,4 +1,4 @@ -# Custom Algorithm +# Custom algorithm This framework provides dozens of signature or encryption algorithms. If your application uses a custom algorithm or if another algorithm has been recently approved in the JWT context, it may be interesting to use it with this framework. @@ -102,7 +102,7 @@ final class ChaCha20Poly1305IETF implements KeyEncryption //The algorithm acts a } ``` -## The Expected CEK Size +## The expected CEK size Since 4.2, the `JWEDecrypter` passes the size of the key expected by the content encryption algorithm as an **additional argument** of `KeyEncryption::decryptKey()` and `KeyWrapping::unwrapKey()`. The value is the one returned by `ContentEncryptionAlgorithm::getCEKSize()`, in bits. @@ -120,7 +120,7 @@ public function decryptKey(JWK $key, string $encrypted_cek, array $header): stri } ``` -{% hint style="warning" %} -The argument will be added to the interfaces and become **required** in 5.0. If you maintain a custom key encryption or key wrapping algorithm, start reading it now. -{% endhint %} +!!! warning + + The argument will be added to the interfaces and become **required** in 5.0. If you maintain a custom key encryption or key wrapping algorithm, start reading it now. diff --git a/docs/advanced-topics/encrypted-tokens/README.md b/docs/advanced-topics/encrypted-tokens/README.md new file mode 100644 index 0000000..246bd8e --- /dev/null +++ b/docs/advanced-topics/encrypted-tokens/README.md @@ -0,0 +1,2 @@ +# Encrypted tokens + diff --git a/advanced-topics/encrypted-tokens/additional-authentication-data-aad.md b/docs/advanced-topics/encrypted-tokens/additional-authentication-data-aad.md similarity index 93% rename from advanced-topics/encrypted-tokens/additional-authentication-data-aad.md rename to docs/advanced-topics/encrypted-tokens/additional-authentication-data-aad.md index 04e6655..e4cce14 100644 --- a/advanced-topics/encrypted-tokens/additional-authentication-data-aad.md +++ b/docs/advanced-topics/encrypted-tokens/additional-authentication-data-aad.md @@ -1,4 +1,4 @@ -# Additional Authentication Data (AAD) +# Additional authentication data (AAD) The Additional Authenticated Data (AAD) is an input to an Authenticated Encryption operation. The AAD is integrity protected but not encrypted. diff --git a/advanced-topics/encrypted-tokens/multiple-recipients.md b/docs/advanced-topics/encrypted-tokens/multiple-recipients.md similarity index 96% rename from advanced-topics/encrypted-tokens/multiple-recipients.md rename to docs/advanced-topics/encrypted-tokens/multiple-recipients.md index ffc4f25..ef26da1 100644 --- a/advanced-topics/encrypted-tokens/multiple-recipients.md +++ b/docs/advanced-topics/encrypted-tokens/multiple-recipients.md @@ -1,4 +1,4 @@ -# Multiple Recipients +# Multiple recipients When you need to encrypt the same payload for several audiences, you may want to do it at once. The JWE Builder supports multiple recipients. @@ -21,11 +21,11 @@ $jweBuilder **Note: when an unprotected header is set, the Compact Serialization mode is not available.** -## Key Management Modes +## Key management modes Each Key Encryption Algorithm has its own Key Management Mode. -### Key Encryption Algorithms And Associated Key Management Mode. +### Key encryption algorithms and associated key management mode. | Algorithm Key Management Mode | Key Encryption | Key Wrapping | Direct Key Agreement | Key Agreement with Key Wrapping | Direct Encryption | | ------------------------------ | -------------- | ------------ | -------------------- | ------------------------------- | ----------------- | @@ -47,7 +47,7 @@ Each Key Encryption Algorithm has its own Key Management Mode. | A192GCMKW | | X | | | | | A256GCMKW | | X | | | | -### Compatibility table between Key Management Modes: +### Compatibility table between key management modes: | Key Management Mode | Key Encryption | Key Wrapping | Direct Key Agreement | Key Agreement with Key Wrapping | Direct Encryption | | ------------------------------- | -------------- | ------------ | -------------------- | ------------------------------- | ----------------- | diff --git a/advanced-topics/encrypted-tokens/unprotected-headers.md b/docs/advanced-topics/encrypted-tokens/unprotected-headers.md similarity index 97% rename from advanced-topics/encrypted-tokens/unprotected-headers.md rename to docs/advanced-topics/encrypted-tokens/unprotected-headers.md index 4ad7dd8..d916906 100644 --- a/advanced-topics/encrypted-tokens/unprotected-headers.md +++ b/docs/advanced-topics/encrypted-tokens/unprotected-headers.md @@ -1,4 +1,4 @@ -# Unprotected Headers +# Unprotected headers As well as the [signed tokens](../signed-tokens/unprotected-header.md), the encrypted tokens also have unprotected header. But with one difference: there are two unprotected headers: @@ -22,7 +22,7 @@ The variable `$jwe` will be a valid JWE object built for two recipients. The unp **Note: when an unprotected header is set, the Compact Serialization mode is not available.** -## Disjoint Header Parameters +## Disjoint header parameters [RFC 7516 section 7.2.1](https://datatracker.ietf.org/doc/html/rfc7516#section-7.2.1) requires the header parameter names of the three locations to be **disjoint**: a given parameter must appear in exactly one of them. The builder refuses to produce such a token, and since 4.2 the `JWEDecrypter` refuses to read one. diff --git a/advanced-topics/nested-tokens.md b/docs/advanced-topics/nested-tokens.md similarity index 97% rename from advanced-topics/nested-tokens.md rename to docs/advanced-topics/nested-tokens.md index 67c244f..e5c9080 100644 --- a/advanced-topics/nested-tokens.md +++ b/docs/advanced-topics/nested-tokens.md @@ -1,10 +1,10 @@ -# Nested Tokens +# Nested tokens JWT can be signed or encrypted and both. A nested token is a signed token enclosed in an encrypted one. This order is very important: signed then encrypted. The `NestedTokenLoader` and `NestedTokenBuilder` classes will help you to create nested tokens with ease. Just install the package `web-token/jwt-nested-token`. It contains all the classes and dependencies will be directly managed by composer. You can install it if needed. -## Nested Token Loading +## Nested token loading To instantiate the `NestedTokenLoader`, you need a `JWSLoader` and a `JWELoader`. @@ -22,7 +22,7 @@ The last argument (`$signature` in the following example) will represents the si $jws = $nestedTokenLoader->load($token, $encryptionKeySet, $signatureKeySet, $signature); ``` -## Nested Token Building +## Nested token building To instantiate the `NestedTokenBuilder`, you will need the following components: @@ -66,7 +66,7 @@ As a reminder, if one of the following parameter is set, the compact serializati * recipient unprotected header, * Additional Authenticated Data. -## Symfony Bundle +## Symfony bundle ### Configuration @@ -102,7 +102,7 @@ This configuration will create two public services: These services can be called from the container (unless private) or injected in your services. -### Configuration Helper +### Configuration helper As any other services, you can create a nested token loader or builder from another bundle extension. The following bundle extension class will create the same configuration and services as above. diff --git a/advanced-topics/serialization.md b/docs/advanced-topics/serialization.md similarity index 97% rename from advanced-topics/serialization.md rename to docs/advanced-topics/serialization.md index cdb27a0..ca661c8 100644 --- a/advanced-topics/serialization.md +++ b/docs/advanced-topics/serialization.md @@ -8,9 +8,9 @@ The [RFC7515](https://tools.ietf.org/html/rfc7515) (JWS) and [RFC7516](https://t The _Compact_ mode is most known and commonly used as it is compact and URL safe i.e. it is designed for web context. JSON Flattened and General are not URL safe, but provides features that may fit your application context. -## JWS Serialization +## JWS serialization -### JWS Compact +### JWS compact This serialization mode is probably the one you know the most. It it a string composed of three parts encoded in Base64 Url Safe and separated by a dot (`.`). @@ -27,7 +27,7 @@ There are some limitations when you use this serialization mode: * Unprotected header not supported. * Unencoded payload must contain characters within the following range of ASCII characters: 0x20-0x2d and 0x2f-0x7e -### JWS JSON Flattened +### JWS JSON flattened This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object. @@ -46,7 +46,7 @@ Example: } ``` -### JWS JSON General +### JWS JSON general This serialization mode is similar to the JWS JSON Flattened, but may contain more than one signature. It it a JSON object. @@ -83,7 +83,7 @@ Example: } ``` -### JWS Serializer Manager +### JWS serializer manager The serializer manager can be helpful when your application deals more than one serialization mode. @@ -107,9 +107,9 @@ $token = $manager->serialize('jws_json_flattened', $jws, 1); $jws = $manager->unserialize($token); ``` -## JWE Serialization +## JWE serialization -### JWE Compact +### JWE compact This serialization mode is probably the one you know the most. It it a string composed of five parts encoded in Base64 Url Safe and separated by a dot (`.`). @@ -126,7 +126,7 @@ There are some limitations when you use this serialization mode: * No Additional Authentication Data can be used. * No shared unprotected header or per-recipient header can be used. -### JWE JSON Flattened +### JWE JSON flattened This serialization mode is useful when you need to use the unprotected header. It it a simple JSON object. @@ -146,7 +146,7 @@ Example: } ``` -### JWE JSON General +### JWE JSON general This serialization mode is similar to the JWE JSON Flattened, but may contain more than one recipient. It it a JSON object. @@ -174,7 +174,7 @@ Example: } ``` -### JWE Serializer Manager +### JWE serializer manager The serializer manager can be helpful when your application deals more than one serialization mode. diff --git a/docs/advanced-topics/signed-tokens/README.md b/docs/advanced-topics/signed-tokens/README.md new file mode 100644 index 0000000..32bd91b --- /dev/null +++ b/docs/advanced-topics/signed-tokens/README.md @@ -0,0 +1,2 @@ +# Signed tokens + diff --git a/advanced-topics/signed-tokens/detached-payload.md b/docs/advanced-topics/signed-tokens/detached-payload.md similarity index 98% rename from advanced-topics/signed-tokens/detached-payload.md rename to docs/advanced-topics/signed-tokens/detached-payload.md index aa27c43..55049aa 100644 --- a/advanced-topics/signed-tokens/detached-payload.md +++ b/docs/advanced-topics/signed-tokens/detached-payload.md @@ -1,8 +1,8 @@ -# Detached Payload +# Detached payload As per the [RFC7519](https://tools.ietf.org/html/rfc7515#appendix-F), the payload of a JWS may be detached. This framework supports this feature. -## JWS Creation +## JWS creation There is not much difference between the creation of a JWS with or without detached payload. The following example comes from the [JWS Creation page](../../the-components/signed-tokens-jws/jws-creation.md). There is only one argument that will change during the call of `withPayload`. @@ -48,7 +48,7 @@ $jws = $jwsBuilder And voilà! When you will serialize this token, the payload will not be present. -## JWS Loading +## JWS loading The loading of a signed token with a detached payload is as easy as when the payload is attached. The only difference is that you have to pass the payload to the JWS Verifier when you want to check the signature. diff --git a/advanced-topics/signed-tokens/multiple-signatures.md b/docs/advanced-topics/signed-tokens/multiple-signatures.md similarity index 98% rename from advanced-topics/signed-tokens/multiple-signatures.md rename to docs/advanced-topics/signed-tokens/multiple-signatures.md index 02d1618..f924b14 100644 --- a/advanced-topics/signed-tokens/multiple-signatures.md +++ b/docs/advanced-topics/signed-tokens/multiple-signatures.md @@ -1,4 +1,4 @@ -# Multiple Signatures +# Multiple signatures When you need to sign the same payload for several audiences, you may want to do it at once. The JWS Builder supports multiple signatures. diff --git a/advanced-topics/signed-tokens/unencoded-payload.md b/docs/advanced-topics/signed-tokens/unencoded-payload.md similarity index 70% rename from advanced-topics/signed-tokens/unencoded-payload.md rename to docs/advanced-topics/signed-tokens/unencoded-payload.md index fa0a552..8e24d47 100644 --- a/advanced-topics/signed-tokens/unencoded-payload.md +++ b/docs/advanced-topics/signed-tokens/unencoded-payload.md @@ -1,4 +1,4 @@ -# Unencoded Payload +# Unencoded payload The [RFC7797](https://tools.ietf.org/html/rfc7797) allows the use of an unencoded payload for the signed tokens. This behaviour is interesting when your tokens have a detached payload and may reduce the token computation. @@ -21,7 +21,7 @@ $jws = $jwsBuilder _As a reminder, both_ `b64` _and_ `crit` _parameters MUST be in the protected header._ -{% hint style="warning" %} -`b64` set to `false` removes the Base64Url encoding altogether, so it contradicts [`JWSBuilder::withEncodedPayload()`](../../the-components/signed-tokens-jws/jws-creation.md#already-encoded-payloads), which takes a payload that is already encoded. The combination is rejected whichever order the two are set in. -{% endhint %} +!!! warning + + `b64` set to `false` removes the Base64Url encoding altogether, so it contradicts [`JWSBuilder::withEncodedPayload()`](../../the-components/signed-tokens-jws/jws-creation.md#already-encoded-payloads), which takes a payload that is already encoded. The combination is rejected whichever order the two are set in. diff --git a/advanced-topics/signed-tokens/unprotected-header.md b/docs/advanced-topics/signed-tokens/unprotected-header.md similarity index 97% rename from advanced-topics/signed-tokens/unprotected-header.md rename to docs/advanced-topics/signed-tokens/unprotected-header.md index 306c692..9c50784 100644 --- a/advanced-topics/signed-tokens/unprotected-header.md +++ b/docs/advanced-topics/signed-tokens/unprotected-header.md @@ -1,4 +1,4 @@ -# Unprotected Header +# Unprotected header You may want to set data in a token header that are not important for your application (e.g. general information). The integrity protection of the data is therefore not needed at all. diff --git a/docs/assets/brand/apple-touch-icon.png b/docs/assets/brand/apple-touch-icon.png new file mode 100644 index 0000000..e384e06 Binary files /dev/null and b/docs/assets/brand/apple-touch-icon.png differ diff --git a/docs/assets/brand/favicon.ico b/docs/assets/brand/favicon.ico new file mode 100644 index 0000000..901a26c Binary files /dev/null and b/docs/assets/brand/favicon.ico differ diff --git a/docs/assets/brand/favicon.svg b/docs/assets/brand/favicon.svg new file mode 100644 index 0000000..4b601fb --- /dev/null +++ b/docs/assets/brand/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/brand/marque-jwt-framework-couleur.svg b/docs/assets/brand/marque-jwt-framework-couleur.svg new file mode 100644 index 0000000..29c11c2 --- /dev/null +++ b/docs/assets/brand/marque-jwt-framework-couleur.svg @@ -0,0 +1 @@ + diff --git a/docs/assets/brand/marque-jwt-framework-inverse.svg b/docs/assets/brand/marque-jwt-framework-inverse.svg new file mode 100644 index 0000000..47d88ad --- /dev/null +++ b/docs/assets/brand/marque-jwt-framework-inverse.svg @@ -0,0 +1 @@ + diff --git a/docs/assets/brand/og-slabs-1200x630.png b/docs/assets/brand/og-slabs-1200x630.png new file mode 100644 index 0000000..4c3d9fd Binary files /dev/null and b/docs/assets/brand/og-slabs-1200x630.png differ diff --git a/docs/assets/brand/slabs-logotype-signature-inverse.svg b/docs/assets/brand/slabs-logotype-signature-inverse.svg new file mode 100644 index 0000000..3fa1506 --- /dev/null +++ b/docs/assets/brand/slabs-logotype-signature-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/fonts/IBMPlexMono-Italic.woff2 b/docs/assets/fonts/IBMPlexMono-Italic.woff2 new file mode 100644 index 0000000..3948c82 Binary files /dev/null and b/docs/assets/fonts/IBMPlexMono-Italic.woff2 differ diff --git a/docs/assets/fonts/IBMPlexMono-Regular.woff2 b/docs/assets/fonts/IBMPlexMono-Regular.woff2 new file mode 100644 index 0000000..01a68eb Binary files /dev/null and b/docs/assets/fonts/IBMPlexMono-Regular.woff2 differ diff --git a/docs/assets/fonts/IBMPlexMono-SemiBold.woff2 b/docs/assets/fonts/IBMPlexMono-SemiBold.woff2 new file mode 100644 index 0000000..7134ce0 Binary files /dev/null and b/docs/assets/fonts/IBMPlexMono-SemiBold.woff2 differ diff --git a/docs/assets/fonts/OFL-IBMPlexMono.txt b/docs/assets/fonts/OFL-IBMPlexMono.txt new file mode 100644 index 0000000..c35c4c6 --- /dev/null +++ b/docs/assets/fonts/OFL-IBMPlexMono.txt @@ -0,0 +1,93 @@ +Copyright © 2017 IBM Corp. with Reserved Font Name "Plex" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/assets/fonts/OFL-SpaceGrotesk.txt b/docs/assets/fonts/OFL-SpaceGrotesk.txt new file mode 100644 index 0000000..cb512b9 --- /dev/null +++ b/docs/assets/fonts/OFL-SpaceGrotesk.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/assets/fonts/SpaceGrotesk-Variable.woff2 b/docs/assets/fonts/SpaceGrotesk-Variable.woff2 new file mode 100644 index 0000000..477234a Binary files /dev/null and b/docs/assets/fonts/SpaceGrotesk-Variable.woff2 differ diff --git a/docs/assets/stylesheets/slabs-tokens.css b/docs/assets/stylesheets/slabs-tokens.css new file mode 100644 index 0000000..2bbe80e --- /dev/null +++ b/docs/assets/stylesheets/slabs-tokens.css @@ -0,0 +1,86 @@ +/* + * Slabs design tokens, brand guidelines v2.0 (October 2026). + * Source: Charte Graphique.pdf; page numbers in comments. + * Use these values as they are: no new color, no tint of orange, no other radius. + */ +:root { + /* Brand colors (p. 17) */ + --slabs-anthracite: #333334; /* logo, text, dark grounds */ + --slabs-beton: #B3AEA6; /* secondary grounds, rules, text on dark */ + --slabs-calcaire: #F5F1E8; /* light grounds, logo on dark */ + --slabs-orange: #ED6A20; /* the cursor, the slab, the main action; never text on light, never a text background */ + + /* Complementary colors (p. 19): small surfaces, always with a word, text on light grounds only */ + --slabs-acier: #4A5D6E; /* information, notes, neutral data series */ + --slabs-patine: #3E7268; /* success, validation, second data series */ + --slabs-brique: #A6452C; /* errors, alerts, negative values; never a button */ + + /* Utility colors (p. 20): screen only, never brand colors */ + --slabs-schiste: #5E5B56; /* secondary text, link underline, field borders */ + --slabs-calcaire-clair: #FBF9F4; /* cards, panels, menus, state notes */ + --slabs-calcaire-ombre: #ECE7DD; /* notes, tags, inline code, disabled controls */ + --slabs-platre: #DCD5C6; /* rules on light grounds; types and properties in code */ + --slabs-sable: #E8C9A8; /* strings and numbers in code on Anthracite only */ + + /* Chart series, in order (p. 21): orange for the series the chart is about, Brique for negative values only */ + --slabs-series-1: var(--slabs-orange); + --slabs-series-2: var(--slabs-anthracite); + --slabs-series-3: var(--slabs-acier); + --slabs-series-4: var(--slabs-patine); + --slabs-series-5: var(--slabs-schiste); + --slabs-series-negative: var(--slabs-brique); + + /* Code on Anthracite (p. 34) */ + --slabs-code-bg: var(--slabs-anthracite); + --slabs-code-text: var(--slabs-calcaire); /* text, variables; keywords in SemiBold */ + --slabs-code-type: var(--slabs-platre); /* types, properties */ + --slabs-code-string: var(--slabs-sable); /* strings, numbers */ + --slabs-code-comment: var(--slabs-beton); /* comments, italic */ + + /* Typefaces (p. 22) */ + --slabs-font-sans: "Space Grotesk", Arial, sans-serif; + --slabs-font-mono: "IBM Plex Mono", Consolas, monospace; /* 400 and 600 only on the web */ + + /* Web type scale (p. 34): desktop, then mobile */ + --slabs-h1: 42px; --slabs-h1-mobile: 32px; --slabs-h1-lh: 1.1; /* Bold 700 */ + --slabs-h2: 32px; --slabs-h2-mobile: 26px; --slabs-h2-lh: 1.2; /* Bold 700 */ + --slabs-h3: 26px; --slabs-h3-mobile: 22px; --slabs-h3-lh: 1.3; /* SemiBold 600 */ + --slabs-h4: 22px; --slabs-h4-mobile: 18px; --slabs-h4-lh: 1.4; /* SemiBold 600 */ + --slabs-h5: 15px; --slabs-h5-mobile: 14px; --slabs-h5-tracking: 0.08em; /* Plex Mono 600, capitals */ + --slabs-h6: 13px; --slabs-h6-tracking: 0.12em; /* Plex Mono 600, capitals */ + --slabs-body-reading: 18px; --slabs-body-reading-mobile: 16px; + --slabs-body: 16px; + --slabs-code-size: 14px; + --slabs-caption: 12px; + --slabs-label-tracking: 0.12em; /* Plex Mono 600 capitals */ + --slabs-nav: 15px; /* Space Grotesk 500 */ + + /* Line heights (p. 34) */ + --slabs-lh-ui: 1.5; + --slabs-lh-reading: 1.6; + --slabs-lh-code: 1.55; + + /* Layout (p. 31) */ + --slabs-content-max: 1200px; /* measured in the frame: the window minus the side band */ + --slabs-reading-max: 864px; /* about 80 characters, at least 72 % of the content area */ + --slabs-side-band: 288px; + --slabs-gutter: 24px; + --slabs-gutter-mobile: 16px; + --slabs-space-base: 4px; /* spacing in multiples of 4 px, from 4 to 96 px */ + --slabs-space-max: 96px; + --slabs-radius-card: 6px; + --slabs-radius-control: 4px; + --slabs-header-logo: 32px; /* logotype height in the header */ + /* Breakpoints, where the layout stops fitting: 640px and 1024px with a header, about 1100px with a side band */ + + /* Lines and focus (p. 33) */ + --slabs-link-underline: 1px; /* Schiste; 2px orange on hover and active */ + --slabs-focus-width: 2px; /* Anthracite on light grounds, orange on dark */ + --slabs-focus-offset: 2px; + --slabs-field-border: 1px; /* Schiste; 2px Brique in error */ + + /* Icons (Icones library) */ + --slabs-icon-size: 24px; + --slabs-icon-stroke: 2px; + /* --slabs-icon-accent: #ED6A20; set on an icon or a parent to turn the accent on */ +} diff --git a/docs/assets/stylesheets/slabs.css b/docs/assets/stylesheets/slabs.css new file mode 100644 index 0000000..0255cef --- /dev/null +++ b/docs/assets/stylesheets/slabs.css @@ -0,0 +1,1120 @@ +/* + * The Slabs brand guidelines v2.0, applied to Material for MkDocs. + * + * Every value comes from slabs-tokens.css, loaded before this file and copied from the design kit as it is. + * This file only says which token goes where. The pages of the guidelines it follows are cited by number: + * p. 30 for documentation, p. 31 for the frame, p. 33 for components, p. 34 for reading and code. + * + * Two schemes. The light one is the guidelines: Calcaire ground, Anthracite text. The dark one keeps the + * same rules on an Anthracite ground, as the guidelines do for every dark surface: Calcaire text, Béton + * for what is secondary, notes on Calcaire clair (p. 19), focus in orange (p. 33). + * + * Signal orange marks the cursor of the logo, the slab of a note, the cursor of a primary button and the + * hovered link. Never text, never a background, never a tint. + */ + +/* ---------------------------------------------------------------- Typefaces (p. 22) */ + +@font-face { + font-display: swap; + font-family: "Space Grotesk"; + font-style: normal; + font-weight: 300 700; + src: url("../fonts/SpaceGrotesk-Variable.woff2") format("woff2"); +} + +/* IBM Plex Mono is served in 400 and 600 only on the web; the italic is for comments in code. */ +@font-face { + font-display: swap; + font-family: "IBM Plex Mono"; + font-style: normal; + font-weight: 400; + src: url("../fonts/IBMPlexMono-Regular.woff2") format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "IBM Plex Mono"; + font-style: italic; + font-weight: 400; + src: url("../fonts/IBMPlexMono-Italic.woff2") format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "IBM Plex Mono"; + font-style: normal; + font-weight: 600; + src: url("../fonts/IBMPlexMono-SemiBold.woff2") format("woff2"); +} + +/* ---------------------------------------------------------------- The two schemes */ + +[data-md-color-scheme="default"] { + --doc-ground: var(--slabs-calcaire); + --doc-raised: var(--slabs-calcaire-clair); + --doc-tint: var(--slabs-calcaire-ombre); + --doc-text: var(--slabs-anthracite); + --doc-text-secondary: var(--slabs-schiste); + --doc-rule: var(--slabs-platre); + --doc-rule-strong: var(--slabs-anthracite); + --doc-link-underline: var(--slabs-schiste); + --doc-focus: var(--slabs-anthracite); + --doc-code-edge: var(--slabs-anthracite); + --doc-field: #ffffff; + --doc-field-border: var(--slabs-schiste); + --doc-note-ground: var(--slabs-calcaire-ombre); +} + +[data-md-color-scheme="slate"] { + --doc-ground: var(--slabs-anthracite); + --doc-raised: var(--slabs-anthracite); + --doc-tint: var(--slabs-schiste); + --doc-text: var(--slabs-calcaire); + --doc-text-secondary: var(--slabs-beton); + --doc-rule: var(--slabs-schiste); + --doc-rule-strong: var(--slabs-calcaire); + --doc-link-underline: var(--slabs-beton); + --doc-focus: var(--slabs-orange); + /* A code block is Anthracite on an Anthracite ground: a Schiste line keeps its edge. */ + --doc-code-edge: var(--slabs-schiste); + --doc-field: var(--slabs-anthracite); + --doc-field-border: var(--slabs-beton); + --doc-note-ground: var(--slabs-calcaire-clair); +} + +[data-md-color-scheme="default"], +[data-md-color-scheme="slate"] { + --md-text-font: "Space Grotesk"; + --md-code-font: "IBM Plex Mono"; + + --md-default-fg-color: var(--doc-text); + --md-default-fg-color--light: var(--doc-text-secondary); + --md-default-fg-color--lighter: var(--doc-text-secondary); + --md-default-fg-color--lightest: var(--doc-rule); + --md-default-bg-color: var(--doc-ground); + --md-default-bg-color--light: var(--doc-ground); + --md-default-bg-color--lighter: var(--doc-ground); + --md-default-bg-color--lightest: var(--doc-ground); + + --md-primary-fg-color: var(--doc-text); + --md-primary-fg-color--light: var(--doc-text); + --md-primary-fg-color--dark: var(--doc-text); + --md-primary-bg-color: var(--doc-ground); + --md-primary-bg-color--light: var(--doc-text-secondary); + --md-accent-fg-color: var(--doc-text); + --md-accent-fg-color--transparent: var(--doc-tint); + --md-accent-bg-color: var(--doc-ground); + + --md-typeset-color: var(--doc-text); + --md-typeset-a-color: var(--doc-text); + --md-typeset-mark-color: var(--doc-tint); + --md-typeset-table-color: var(--doc-rule); + + --md-code-fg-color: var(--slabs-code-text); + --md-code-bg-color: var(--slabs-code-bg); + --md-code-hl-color: var(--slabs-schiste); + --md-code-hl-number-color: var(--slabs-code-string); + --md-code-hl-string-color: var(--slabs-code-string); + --md-code-hl-special-color: var(--slabs-code-string); + --md-code-hl-constant-color: var(--slabs-code-type); + --md-code-hl-name-color: var(--slabs-code-text); + --md-code-hl-function-color: var(--slabs-code-text); + --md-code-hl-keyword-color: var(--slabs-code-text); + --md-code-hl-operator-color: var(--slabs-code-text); + --md-code-hl-punctuation-color: var(--slabs-code-text); + --md-code-hl-variable-color: var(--slabs-code-text); + --md-code-hl-generic-color: var(--slabs-code-text); + --md-code-hl-comment-color: var(--slabs-code-comment); + --md-code-hl-color--light: var(--slabs-schiste); + + --md-footer-fg-color: var(--slabs-calcaire); + --md-footer-fg-color--light: var(--slabs-beton); + --md-footer-fg-color--lighter: var(--slabs-beton); + --md-footer-bg-color: var(--slabs-anthracite); + --md-footer-bg-color--dark: var(--slabs-anthracite); + + --md-shadow-z1: none; + --md-shadow-z2: none; + --md-shadow-z3: none; +} + +body { + -webkit-font-smoothing: antialiased; + background-color: var(--doc-ground); + text-rendering: optimizeLegibility; +} + +/* Focus, always visible (p. 33). */ +:focus-visible, +.md-typeset :focus-visible { + outline: var(--slabs-focus-width) solid var(--doc-focus); + outline-offset: var(--slabs-focus-offset); +} + +/* ---------------------------------------------------------------- Header (p. 31) */ + +.md-header, +.md-header--shadow { + background-color: var(--doc-raised); + border-bottom: 1px solid var(--doc-rule); + box-shadow: none; + color: var(--doc-text); +} + +/* The logo, 32 px high: the project mark, or the Slabs logotype for a project that has none yet. The + colour version on light grounds, the reversed one on dark. */ +.md-header__button.md-logo { + margin: 0 0 0 var(--slabs-gutter-mobile); + padding: 0; +} + +.md-logo .sl-logo, +.md-header__button.md-logo img { + display: block; + height: var(--slabs-header-logo); + width: auto; +} + +/* In the drawer of a narrow screen, the mark takes the width of the drawer. */ +.md-nav__button.md-logo .sl-logo { + height: auto; + max-width: 100%; +} + +[data-md-color-scheme="default"] .sl-logo--dark, +[data-md-color-scheme="slate"] .sl-logo--light { + display: none !important; +} + +/* The title of the page, after a rule, once the reader has scrolled past its heading. A project mark + already names the project: on a screen wide enough to show it, the name is not written a second time. + Beside the Slabs logotype, the name stays. */ +.md-header__title { + font-size: var(--slabs-nav); + font-weight: 600; + margin-left: var(--slabs-gutter); +} + +.md-header__title .md-header__ellipsis { + border-left: 1px solid var(--doc-rule); + padding-left: var(--slabs-gutter); +} + +@media screen and (min-width: 76.25em) { + .md-header:has(.sl-logo--mark) .md-header__title:not(.md-header__title--active) .md-header__ellipsis { + border-left-color: transparent; + } + + .md-header:has(.sl-logo--mark) .md-header__title .md-header__topic:first-child { + visibility: hidden; + } +} + +.md-header__button, +.md-header__topic, +.md-header__option, +.md-header__source { + color: var(--doc-text); +} + +.md-header__source { + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); +} + +.md-source__repository, +.md-source__facts { + font-family: var(--md-code-font-family); +} + +/* The search field is a field (p. 33): the ground of a field, a 1 px border, the radius of a control. */ +.md-search__form { + background-color: var(--doc-field); + border: var(--slabs-field-border) solid var(--doc-field-border); + border-radius: var(--slabs-radius-control); + box-shadow: none; +} + +.md-search__form:hover { + background-color: var(--doc-field); +} + +[data-md-toggle="search"]:checked ~ .md-header .md-search__form { + background-color: var(--doc-field); + border-radius: var(--slabs-radius-control) var(--slabs-radius-control) 0 0; +} + +.md-search__input, +.md-search__icon, +.md-search__options .md-icon { + color: var(--doc-text); +} + +.md-search__input { + font-size: var(--slabs-body); +} + +.md-search__input::placeholder { + color: var(--doc-text-secondary); +} + +.md-search__output { + border: var(--slabs-field-border) solid var(--doc-field-border); + border-radius: 0 0 var(--slabs-radius-control) var(--slabs-radius-control); + border-top: 0; +} + +.md-search__scrollwrap, +.md-search-result { + background-color: var(--doc-raised); +} + +.md-search-result__meta { + background-color: var(--doc-tint); + color: var(--doc-text-secondary); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); +} + +.md-search-result__link:is(:focus, :hover) { + background-color: var(--doc-tint); +} + +.md-search-result mark { + color: var(--doc-text); + text-decoration: underline var(--slabs-orange) 2px; + text-underline-offset: 0.2em; +} + +/* The version is a control: Plex Mono, a 1 px rule, the radius of a control. */ +.md-version { + font-family: var(--md-code-font-family); +} + +.md-version__current { + border: 1px solid var(--doc-rule); + border-radius: var(--slabs-radius-control); + color: var(--doc-text-secondary); + font-size: var(--slabs-caption); + margin-left: var(--slabs-gutter-mobile); + padding: 0 calc(var(--slabs-space-base) * 2); +} + +.md-version__current::after { + background-color: var(--doc-text-secondary); +} + +.md-version__list { + background-color: var(--doc-raised); + border: 1px solid var(--doc-rule); + border-radius: var(--slabs-radius-card); + box-shadow: none; +} + +.md-version__link { + font-size: var(--slabs-caption); +} + +.md-version__link:is(:focus, :hover) { + background-color: var(--doc-tint); + color: var(--doc-text); +} + +/* ---------------------------------------------------------------- Layout (p. 31) */ + +/* The navigation and the contents of the page take the width of the side band, 288 px each: the 168 px + the guidelines leave beside the reading column broke a title in two on most pages. The reading column + keeps its 864 px. */ +.md-grid { + max-width: calc(2 * var(--slabs-side-band) + var(--slabs-reading-max) + 2 * var(--slabs-gutter)); +} + +@media screen and (min-width: 76.25em) { + .md-sidebar--primary { + width: var(--slabs-side-band); + } + + .md-sidebar--secondary { + width: var(--slabs-side-band); + } +} + +@media screen and (min-width: 60em) { + .md-content__inner { + margin-left: auto; + margin-right: auto; + max-width: var(--slabs-reading-max); + } +} + +.md-main { + background-color: var(--doc-ground); +} + +/* ---------------------------------------------------------------- Navigation (p. 31) */ + +.md-nav { + font-size: var(--slabs-nav); + line-height: var(--slabs-lh-ui); +} + +.md-nav__link { + color: var(--doc-text); + font-weight: 500; + margin-top: calc(var(--slabs-space-base) * 2); +} + +.md-nav__link:is(:focus, :hover), +.md-nav__link[for]:is(:focus, :hover) { + color: var(--doc-text); +} + +/* The current page: SemiBold and a 2 px underline in the colour of the text. */ +.md-nav__link--active, +.md-nav__item .md-nav__link--active:hover { + color: var(--doc-text); + font-weight: 600; +} + +/* The underline is drawn on the text of the link only: drawn on the link as well, it doubles. */ +.md-nav--primary .md-nav__link { + text-decoration: none; +} + +.md-nav--primary .md-nav__link--active > .md-ellipsis { + text-decoration: underline 2px var(--doc-rule-strong); + text-underline-offset: 0.35em; +} + +/* Under the pointer, a link of the navigation behaves as a link of the text (p. 33): a 2 px orange + underline, on the current page as on the others. */ +.md-nav--primary a.md-nav__link:is(:hover, :focus-visible) > .md-ellipsis { + text-decoration: underline 2px var(--slabs-orange); + text-underline-offset: 0.35em; +} + +/* A section title is a label: Plex Mono SemiBold capitals, 12 px, tracked, in the secondary colour. */ +.md-nav--primary .md-nav__item--section > .md-nav__link, +.md-nav--primary .md-nav__item--section > .md-nav__container > .md-nav__link { + color: var(--doc-text-secondary); + cursor: default; + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + font-weight: 600; + letter-spacing: var(--slabs-label-tracking); + margin-top: calc(var(--slabs-space-base) * 6); + text-decoration: none; + text-transform: uppercase; +} + +.md-nav--primary .md-nav__item--section > .md-nav__link .md-nav__icon { + display: none; +} + +@media screen and (min-width: 76.25em) { + .md-nav--primary > .md-nav__title { + display: none; + } +} + +/* The drawer of a narrow screen opens on the same band as the header. */ +.md-nav--primary > .md-nav__title, +.md-nav--primary > .md-nav__title .md-nav__button { + background-color: var(--doc-raised); + color: var(--doc-text); +} + +.md-nav--primary .md-nav__title { + box-shadow: none; + font-weight: 600; +} + +.md-sidebar { + background-color: var(--doc-ground); +} + +@media screen and (max-width: 76.234375em) { + .md-nav--primary .md-nav__title, + .md-nav--primary .md-nav__item { + border-top: 0; + } + + .md-nav--primary .md-nav__link { + margin-top: 0; + padding: calc(var(--slabs-space-base) * 2) var(--slabs-gutter-mobile); + } +} + +/* The contents of the page, on the right: the same label, then the headings. */ +.md-nav--secondary .md-nav__title { + background: transparent; + box-shadow: none; + color: var(--doc-text-secondary); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + font-weight: 600; + letter-spacing: var(--slabs-label-tracking); + text-transform: uppercase; +} + +.md-nav--secondary .md-nav__link { + color: var(--doc-text-secondary); + font-weight: 400; +} + +.md-nav--secondary .md-nav__link--active, +.md-nav--secondary .md-nav__link:is(:hover, :focus-visible) { + color: var(--doc-text); +} + +.md-nav--secondary a.md-nav__link:is(:hover, :focus-visible) { + text-decoration: underline 2px var(--slabs-orange); + text-underline-offset: 0.35em; +} + +/* ---------------------------------------------------------------- Reading (p. 34) */ + +/* The body of a page reads as an article: 18 px, 16 px on a phone, line height 1.6. Interface text, the + navigation, the fields and the buttons, keeps its own sizes. */ +.md-typeset { + color: var(--doc-text); + font-size: var(--slabs-body-reading); + line-height: var(--slabs-lh-reading); +} + +@media screen and (max-width: 640px) { + .md-typeset { + font-size: var(--slabs-body-reading-mobile); + } +} + +.md-typeset :is(h1, h2, h3, h4) { + color: var(--doc-text); + font-family: var(--md-text-font-family); + letter-spacing: 0; +} + +.md-typeset h1 { + font-size: var(--slabs-h1); + font-weight: 700; + line-height: var(--slabs-h1-lh); + margin: 0 0 calc(var(--slabs-space-base) * 6); +} + +.md-typeset h2 { + font-size: var(--slabs-h2); + font-weight: 700; + line-height: var(--slabs-h2-lh); + margin: calc(var(--slabs-space-base) * 12) 0 calc(var(--slabs-space-base) * 4); +} + +.md-typeset h3 { + font-size: var(--slabs-h3); + font-weight: 600; + line-height: var(--slabs-h3-lh); + margin: calc(var(--slabs-space-base) * 10) 0 calc(var(--slabs-space-base) * 3); +} + +.md-typeset h4 { + font-size: var(--slabs-h4); + font-weight: 600; + line-height: var(--slabs-h4-lh); + margin: calc(var(--slabs-space-base) * 8) 0 calc(var(--slabs-space-base) * 2); +} + +/* H5 and H6 are labels: Plex Mono SemiBold capitals. */ +.md-typeset h5, +.md-typeset h6 { + color: var(--doc-text); + font-family: var(--md-code-font-family); + font-weight: 600; + text-transform: uppercase; +} + +.md-typeset h5 { + font-size: var(--slabs-h5); + letter-spacing: var(--slabs-h5-tracking); +} + +.md-typeset h6 { + font-size: var(--slabs-h6); + letter-spacing: var(--slabs-h6-tracking); +} + +@media screen and (max-width: 640px) { + .md-typeset h1 { font-size: var(--slabs-h1-mobile); } + .md-typeset h2 { font-size: var(--slabs-h2-mobile); } + .md-typeset h3 { font-size: var(--slabs-h3-mobile); } + .md-typeset h4 { font-size: var(--slabs-h4-mobile); } + .md-typeset h5 { font-size: var(--slabs-h5-mobile); } +} + +.md-typeset .headerlink { + color: var(--doc-text-secondary); +} + +.md-typeset p, +.md-typeset li { + text-align: left; +} + +.md-typeset hr { + border-bottom: 1px solid var(--doc-rule); +} + +.md-typeset blockquote { + border-left: 0; + color: var(--doc-text-secondary); + padding-left: var(--slabs-gutter); +} + +/* The date of the last change and the pencil that edits a page: secondary, in Plex Mono. */ +.md-source-file, +.md-typeset .md-source-file__fact { + color: var(--doc-text-secondary); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); +} + +.md-content__button { + color: var(--doc-text-secondary); +} + +.md-content__button:hover { + color: var(--doc-text); +} + +/* ---------------------------------------------------------------- Links (p. 33) */ + +.md-typeset a { + color: var(--doc-text); + text-decoration: underline var(--slabs-link-underline) var(--doc-link-underline); + text-underline-offset: 0.2em; +} + +.md-typeset a:is(:hover, :active) { + color: var(--doc-text); + text-decoration-color: var(--slabs-orange); + text-decoration-thickness: 2px; +} + +.md-typeset a.headerlink, +.md-typeset a.md-button, +.md-typeset a.md-content__button { + text-decoration: none; +} + +/* ---------------------------------------------------------------- Code (p. 30, 34) */ + +.md-typeset code, +.md-typeset kbd, +.md-typeset pre { + font-feature-settings: "kern"; +} + +/* Inline code, on Calcaire ombré. */ +.md-typeset :not(pre) > code { + background-color: var(--doc-tint); + border-radius: var(--slabs-radius-control); + color: var(--doc-text); + font-size: var(--slabs-code-size); + padding: 0.1em 0.35em; + word-break: normal; +} + +/* In a heading, inline code follows the size of the heading. */ +.md-typeset :is(h1, h2, h3, h4) code { + font-size: 0.85em; +} + +/* A code block: Plex Mono 14 px on Anthracite, the radius of a card. */ +.md-typeset .highlight { + background-color: var(--slabs-code-bg); + border: 1px solid var(--doc-code-edge); + border-radius: var(--slabs-radius-card); + overflow: hidden; +} + +/* A numbered block is a table inside the block: the block already draws the edge. */ +.md-typeset .highlight .highlighttable, +.md-typeset .highlighttable { + background-color: var(--slabs-code-bg); + border: 0; + border-radius: 0; + margin: 0; +} + +.md-typeset .highlighttable :is(.linenos, .code) { + vertical-align: top; +} + +.md-typeset pre { + margin: 0; +} + +.md-typeset pre > code, +.md-typeset .highlighttable code { + background-color: var(--slabs-code-bg); + color: var(--slabs-code-text); + font-size: var(--slabs-code-size); + line-height: var(--slabs-lh-code); + padding: calc(var(--slabs-space-base) * 4) calc(var(--slabs-space-base) * 5); +} + +.md-typeset .highlight + .highlight, +.md-typeset .highlight + p, +.md-typeset p + .highlight { + margin-top: calc(var(--slabs-space-base) * 4); +} + +.md-typeset .highlight { + margin: calc(var(--slabs-space-base) * 4) 0; +} + +/* The filename of a block, a caption above the code. */ +.md-typeset .highlight span.filename { + background-color: var(--slabs-code-bg); + border-bottom: 1px solid var(--slabs-schiste); + color: var(--slabs-beton); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + font-weight: 400; + margin: 0; + padding: calc(var(--slabs-space-base) * 2) calc(var(--slabs-space-base) * 5); +} + +.md-typeset .highlight span.filename::before { + display: none; +} + +/* Line numbers, at the start of each line, in Béton behind a Schiste rule. */ +.md-typeset .highlight [data-linenos]::before { + background-color: var(--slabs-code-bg); + box-shadow: -1px 0 var(--slabs-schiste) inset; + color: var(--slabs-code-comment); + margin-right: calc(var(--slabs-space-base) * 4); + padding-right: calc(var(--slabs-space-base) * 3); +} + +/* The line numbers are anchors: they are not links of the text. */ +.md-typeset .highlighttable .linenos a, +.md-typeset .highlight .linenos a { + color: var(--slabs-code-comment); + text-decoration: none; +} + +.md-typeset .highlight .hll { + background-color: var(--md-code-hl-color); +} + +/* The copy button sits on the code, in Béton, with no box of its own. */ +.md-code__nav, +:hover > .md-code__nav { + background-color: transparent; +} + +.md-code__button, +.md-clipboard { + background-color: transparent; + color: var(--slabs-beton); +} + +:hover > * > .md-code__button { + color: var(--slabs-beton); +} + +.md-code__button:is(.focus-visible, :hover), +.md-clipboard:is(:focus, :hover) { + color: var(--slabs-calcaire); + outline-color: var(--slabs-orange); +} + +/* The five token styles: text and variables Calcaire, keywords Calcaire SemiBold, types and properties + Plâtre, strings and numbers Sable, comments Béton italic. Operators and punctuation are text. */ +.md-typeset .highlight :is(.n, .nv, .vc, .vg, .vi, .vm, .nf, .fm, .nl, .ni, .ne, .o, .p, .w, .x, .err) { + color: var(--slabs-code-text); + font-style: normal; + font-weight: 400; +} + +.md-typeset .highlight :is(.k, .kc, .kd, .kn, .kp, .kr, .ow) { + color: var(--slabs-code-text); + font-style: normal; + font-weight: 600; +} + +.md-typeset .highlight :is(.kt, .nc, .nn, .nx, .na, .nb, .bp, .nt, .nd, .no, .py) { + color: var(--slabs-code-type); + font-style: normal; + font-weight: 400; +} + +.md-typeset .highlight :is(.l, .ld, .s, .s1, .s2, .sa, .sb, .sc, .sd, .se, .sh, .si, .sr, .ss, .sx, .dl, + .m, .mb, .mf, .mh, .mi, .il, .mo) { + color: var(--slabs-code-string); + font-style: normal; + font-weight: 400; +} + +.md-typeset .highlight :is(.c, .c1, .ch, .cm, .cp, .cpf, .cs) { + color: var(--slabs-code-comment); + font-style: italic; + font-weight: 400; +} + +.md-typeset .highlight :is(.gd, .gi, .gh, .gu, .go, .gp) { + color: var(--slabs-code-text); +} + +/* The orange cursor marks the prompt of a console session (p. 30). */ +.md-typeset .highlight .gp { + color: var(--slabs-orange); +} + +/* Tabs, for alternatives the reader chooses between: labels in Plex Mono, the current one underlined. */ +.md-typeset .tabbed-labels > label { + color: var(--doc-text-secondary); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + font-weight: 600; + letter-spacing: var(--slabs-label-tracking); + text-transform: uppercase; +} + +/* The theme turns each label into an anchor, to link to a tab: it is not a link of the text. */ +.md-typeset .tabbed-labels a { + color: inherit; + text-decoration: none; +} + +.md-typeset .tabbed-labels > label:hover, +.md-typeset .tabbed-set > input:checked + label { + color: var(--doc-text); +} + +.md-typeset .tabbed-labels::before { + background-color: var(--doc-rule-strong); +} + +/* ---------------------------------------------------------------- Notes (p. 30) */ + +/* A tinted box with a slab on top, never a coloured side border. Every kind of note looks the same: the + word of its title says what it is, colour never does (p. 19). On a dark ground the box is Calcaire + clair and its text Anthracite, as a message on Anthracite always is (p. 19). */ +.md-typeset .admonition, +.md-typeset details { + --md-typeset-a-color: var(--slabs-anthracite); + + background-color: var(--doc-note-ground); + border: 0; + border-radius: var(--slabs-radius-card); + box-shadow: none; + color: var(--slabs-anthracite); + font-size: inherit; + margin: calc(var(--slabs-space-base) * 6) 0; + padding: calc(var(--slabs-space-base) * 5) calc(var(--slabs-space-base) * 5) calc(var(--slabs-space-base) * 4); + position: relative; +} + +/* The slab: a quarter of the size of an H4 high, 1.2 times as wide, flat, square ends, on the top edge. */ +.md-typeset .admonition::before, +.md-typeset details::before { + background-color: var(--slabs-orange); + content: ""; + height: calc(var(--slabs-h4) / 4); + left: calc(var(--slabs-space-base) * 5); + position: absolute; + top: 0; + width: calc(var(--slabs-h4) * 1.2); +} + +.md-typeset :is(.admonition, details) > :is(.admonition-title, summary) { + background-color: transparent !important; + border: 0; + color: var(--slabs-anthracite); + font-family: var(--md-code-font-family); + font-size: var(--slabs-h6); + font-weight: 600; + letter-spacing: var(--slabs-h6-tracking); + margin: 0 0 calc(var(--slabs-space-base) * 2); + padding: 0; + text-transform: uppercase; +} + +/* The icons of the theme are not the Slabs set: the word is enough. */ +.md-typeset :is(.admonition, details) > :is(.admonition-title, summary)::before { + display: none; +} + +.md-typeset details > summary { + cursor: pointer; + padding-right: calc(var(--slabs-space-base) * 6); +} + +.md-typeset details > summary::after { + background-color: var(--slabs-anthracite); + top: 0; +} + +.md-typeset .admonition > :last-child, +.md-typeset details > :last-child { + margin-bottom: 0; +} + +.md-typeset :is(.admonition, details) :is(a, a:hover) { + color: var(--slabs-anthracite); +} + +.md-typeset :is(.admonition, details) a { + text-decoration-color: var(--slabs-schiste); +} + +.md-typeset :is(.admonition, details) a:hover { + text-decoration-color: var(--slabs-orange); +} + +.md-typeset :is(.admonition, details) :not(pre) > code { + background-color: var(--slabs-calcaire-ombre); + color: var(--slabs-anthracite); +} + +[data-md-color-scheme="default"] .md-typeset :is(.admonition, details) :not(pre) > code { + background-color: var(--slabs-calcaire-clair); +} + +/* ---------------------------------------------------------------- Tables (p. 33) */ + +.md-typeset table:not([class]) { + background-color: transparent; + border: 0; + border-collapse: collapse; + border-radius: 0; + box-shadow: none; + display: table; + font-size: inherit; + width: 100%; +} + +/* The header: Plex Mono capitals over a rule in the colour of the text. */ +.md-typeset table:not([class]) th { + background-color: transparent; + border-bottom: 1px solid var(--doc-rule-strong); + color: var(--doc-text); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + font-weight: 600; + letter-spacing: var(--slabs-label-tracking); + text-transform: uppercase; + vertical-align: bottom; +} + +.md-typeset table:not([class]) :is(th, td) { + padding: calc(var(--slabs-space-base) * 3) calc(var(--slabs-space-base) * 4) calc(var(--slabs-space-base) * 3) 0; +} + +.md-typeset table:not([class]) :is(th, td):last-child { + padding-right: 0; +} + +/* Plâtre between the rows. */ +.md-typeset table:not([class]) td { + border-top: 0; + border-bottom: 1px solid var(--doc-rule); + vertical-align: top; +} + +.md-typeset table:not([class]) tbody tr:hover { + background-color: transparent; + box-shadow: none; +} + +.md-typeset__scrollwrap { + margin: calc(var(--slabs-space-base) * 6) 0; +} + +.md-typeset__table { + padding: 0; +} + +/* ---------------------------------------------------------------- Buttons (p. 33) */ + +.md-typeset .md-button { + border: 1px solid var(--doc-text); + border-radius: var(--slabs-radius-control); + color: var(--doc-text); + font-size: var(--slabs-body); + font-weight: 500; + padding: calc(var(--slabs-space-base) * 2) calc(var(--slabs-space-base) * 4); + position: relative; +} + +.md-typeset .md-button:is(:focus, :hover) { + background-color: transparent; + border-color: var(--doc-text); + color: var(--doc-text); +} + +/* The primary button: Anthracite, Calcaire text, the orange cursor after the label. On hover the cursor + stretches under the label. */ +.md-typeset .md-button--primary, +.md-typeset .md-button--primary:is(:focus, :hover) { + background-color: var(--slabs-anthracite); + border-color: var(--slabs-anthracite); + color: var(--slabs-calcaire); + padding-right: calc(var(--slabs-space-base) * 8); +} + +[data-md-color-scheme="slate"] .md-typeset .md-button--primary, +[data-md-color-scheme="slate"] .md-typeset .md-button--primary:is(:focus, :hover) { + background-color: var(--slabs-calcaire); + border-color: var(--slabs-calcaire); + color: var(--slabs-anthracite); +} + +.md-typeset .md-button--primary::after { + background-color: var(--slabs-orange); + bottom: calc(var(--slabs-space-base) * 2 + 0.2em); + content: ""; + height: 0.25em; + position: absolute; + right: calc(var(--slabs-space-base) * 4); + transition: left 150ms, right 150ms, bottom 150ms; + width: auto; + left: calc(100% - var(--slabs-space-base) * 4 - 0.6em); +} + +.md-typeset .md-button--primary:hover::after { + bottom: calc(var(--slabs-space-base) * 1); + left: calc(var(--slabs-space-base) * 4); + right: calc(var(--slabs-space-base) * 8); +} + +/* ---------------------------------------------------------------- Footer (p. 31) */ + +/* Anthracite, whatever the scheme: the previous and next pages, then the reversed signature, the links in + Calcaire and the legal line in Béton. */ +.md-footer { + background-color: var(--slabs-anthracite); + color: var(--slabs-calcaire); +} + +.md-footer__inner { + border-bottom: 1px solid var(--slabs-schiste); + max-width: calc(var(--slabs-content-max) + 2 * var(--slabs-gutter)); + padding: calc(var(--slabs-space-base) * 4) var(--slabs-gutter); +} + +.md-footer__link { + color: var(--slabs-calcaire); + opacity: 1; +} + +.md-footer__link:is(:focus, :hover) { + opacity: 1; +} + +.md-footer__link:hover .md-footer__title { + text-decoration: underline 2px var(--slabs-orange); + text-underline-offset: 0.2em; +} + +.md-footer__link:focus-visible { + outline-color: var(--slabs-orange); +} + +.md-footer__direction { + color: var(--slabs-beton); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + letter-spacing: var(--slabs-label-tracking); + text-transform: uppercase; +} + +.md-footer__title { + font-size: var(--slabs-body); + font-weight: 600; +} + +.md-footer-meta { + background-color: var(--slabs-anthracite); +} + +.md-footer-meta__inner { + max-width: calc(var(--slabs-content-max) + 2 * var(--slabs-gutter)); + padding: calc(var(--slabs-space-base) * 8) var(--slabs-gutter); +} + +.sl-footer { + display: grid; + gap: calc(var(--slabs-space-base) * 6); + grid-template-columns: auto 1fr; + width: 100%; +} + +/* The signature: the S at 40 px at least, which makes the file 60 px high. */ +.sl-footer__signature img { + display: block; + height: 60px; + width: auto; +} + +.md-typeset ul.sl-footer__links > li { + list-style: none; + margin: 0; +} + +.md-typeset ul.sl-footer__links:not([hidden]) { + align-self: start; + display: flex; + justify-content: flex-end; + flex-wrap: wrap; + gap: calc(var(--slabs-space-base) * 2) calc(var(--slabs-space-base) * 6); + list-style: none; + margin: 0; + padding: 0; +} + +.sl-footer__links a, +.md-footer-meta .sl-footer__links a { + color: var(--slabs-calcaire); + font-size: var(--slabs-nav); + text-decoration: underline var(--slabs-link-underline) var(--slabs-beton); + text-underline-offset: 0.2em; +} + +.sl-footer__legal { + border-top: 1px solid var(--slabs-schiste); + color: var(--slabs-beton); + font-family: var(--md-code-font-family); + font-size: var(--slabs-caption); + grid-column: 1 / -1; + margin: 0; + padding-top: calc(var(--slabs-space-base) * 4); +} + +.sl-footer__legal a, +.md-footer-meta .sl-footer__legal a { + color: var(--slabs-beton); + text-decoration: none; +} + +.md-footer-meta :is(.sl-footer__links, .sl-footer__legal) a:is(:focus, :hover) { + color: var(--slabs-calcaire); + text-decoration: underline 2px var(--slabs-orange); + text-underline-offset: 0.2em; +} + +.md-footer-meta a:focus-visible { + outline-color: var(--slabs-orange); +} + +@media screen and (max-width: 640px) { + .sl-footer { + grid-template-columns: 1fr; + } + + .md-typeset ul.sl-footer__links:not([hidden]) { + justify-content: flex-start; + } +} diff --git a/benchmark/benchmarks.md b/docs/benchmark/benchmarks.md similarity index 98% rename from benchmark/benchmarks.md rename to docs/benchmark/benchmarks.md index 926f967..0bbd6c7 100644 --- a/benchmark/benchmarks.md +++ b/docs/benchmark/benchmarks.md @@ -1,4 +1,4 @@ -# How To +# How to You can easily check if an algorithm is fast enough on your platform. This project has tests for all operations with (almost) all algorithms. @@ -12,7 +12,7 @@ To run those tests, you must install the library with all dev dependencies. When **The previous command will test ALL algorithms and may take more than 7 hours.** -## Algorithm Selection +## Algorithm selection We recommend you to run tests only for the algorithm(s) you want to use. Just run the following command: @@ -83,7 +83,7 @@ Test the RSA1\_5 algorithm: ./vendor/bin/phpbench run --group RSA1_5 --store ``` -## Benchmark Details +## Benchmark details The result of the following command will only give you partial result. For example: diff --git a/benchmark/result-table.md b/docs/benchmark/result-table.md similarity index 100% rename from benchmark/result-table.md rename to docs/benchmark/result-table.md diff --git a/console-command/console.md b/docs/console-command/console.md similarity index 89% rename from console-command/console.md rename to docs/console-command/console.md index 8dc54d9..6670d04 100644 --- a/console-command/console.md +++ b/docs/console-command/console.md @@ -1,33 +1,33 @@ -# Console Commands +# Console commands The JWT Framework provides a comprehensive set of command-line tools for managing keys, key sets, and tokens. These tools are invaluable for development, testing, and production operations. -## Installation Options +## Installation options The console commands are available in three different ways to suit your needs: -### 1. Standalone Application +### 1. Standalone application Perfect for quick operations without a full project setup. - **[Learn more](standalone.md)** -### 2. Symfony Console Integration +### 2. Symfony Console integration Integrated into your Symfony application's console. - **[Learn more](symfony-console.md)** -### 3. PHAR (PHP Archive) +### 3. PHAR (PHP archive) A self-contained executable file that works anywhere PHP is installed. - **[Learn more](phar-application.md)** - Download the latest PHAR from the [releases page](https://github.com/web-token/jwt-framework/releases) -## Quick Start +## Quick start -{% hint style="info" %} -In the following examples, we use `./jose.phar` as the command prefix. Replace it with: -- `php bin/console jose:` if using Symfony Console -- `vendor/bin/jose` if using the standalone application -{% endhint %} +!!! info -### Getting Help + In the following examples, we use `./jose.phar` as the command prefix. Replace it with: + - `php bin/console jose:` if using Symfony Console + - `vendor/bin/jose` if using the standalone application + +### Getting help All commands support the `--help` option for detailed usage information: @@ -35,7 +35,7 @@ All commands support the `--help` option for detailed usage information: ./jose.phar key:generate --help ``` -### Saving Output to Files +### Saving output to files You can easily save command output to files for later use: @@ -47,11 +47,11 @@ You can easily save command output to files for later use: ./jose.phar key:convert:pkcs1 '{"kty":"EC",...}' > key.pem ``` -## Command Categories +## Command categories The console commands are organized into logical categories: -### 🔑 Key Management +### 🔑 key management Create, convert, analyze, and optimize cryptographic keys: - Generate new keys (RSA, EC, OKP, oct) - Convert between formats (JWK, PEM, DER, PKCS#1, PKCS#8) @@ -60,23 +60,23 @@ Create, convert, analyze, and optimize cryptographic keys: - Optimize RSA keys for performance - Calculate key thumbprints -### 📦 Key Set Management +### 📦 key set management Manage collections of keys: - Merge multiple key sets - Convert all keys in a set - Analyze all keys in a set - Extract specific keys from sets -### 🔄 Key Loading +### 🔄 key loading Load keys from various sources: - Load from files (JWK, PEM, DER) - Load from URLs (JKU, X5U) - Load from X.509 certificates - Load from PKCS#12 bundles -## Usage Examples +## Usage examples -### Generate a New Symmetric Key +### Generate a new symmetric key ```bash # Generate a 256-bit symmetric key @@ -88,24 +88,24 @@ Output: {"kty":"oct","k":"Base64UrlEncodedKey..."} ``` -### Generate an RSA Key Pair +### Generate an RSA key pair ```bash # Generate a 2048-bit RSA key with usage information ./jose.phar key:generate:rsa 2048 --use sig --alg RS256 ``` -### Convert a Private Key to Public Key +### Convert a private key to public key ```bash ./jose.phar key:convert:public '{"kty":"EC","crv":"P-256","d":"...",...}' ``` -## Command Reference +## Command reference -### Key Management Commands +### Key management commands -#### Private Key To Public Key Converter +#### Private key to public key converter This command will convert a private key into a public key. It has no effect on shared keys (e.g. `oct` keys). @@ -115,7 +115,7 @@ This command will convert a private key into a public key. It has no effect on s {"kty":"EC","crv":"P-256","x":"-wdLWDWCZP6oFYl8aGVfU0MsFlckjaSVrO7hEsc8lgk","y":"rt8XDTalLMCRB5Tu9WQc2d0TOVwXXHkVDbI7cIig6r4"} ``` -#### Key Analyze +#### Key analyze The following command will analyze the key passed as argument and find issues. @@ -128,7 +128,7 @@ The parameter "kid" should be added. The key length is less than 128 bits. ``` -#### PKCS#1 Key Converter +#### PKCS#1 key converter This command will convert a RSA or EC key into PKCS#1 key. @@ -142,7 +142,7 @@ wJEHlO71ZBzZ3RM5XBdceRUNsjtwiKDqvg== -----END EC PRIVATE KEY----- ``` -#### PKCS#8 Key Converter +#### PKCS#8 key converter This command will convert a `RSA`, `EC` or `OKP` key into a PKCS#8 key. It is the counterpart of `key:convert:pkcs1`, for the projects that share keys with libraries only able to import PKCS#8 private keys, such as the npm `jose` package. @@ -167,11 +167,11 @@ jaSVrO7hEsc8lgmu3xcNNqUswJEHlO71ZBzZ3RM5XBdceRUNsjtwiKDqvg== -----END PUBLIC KEY----- ``` -{% hint style="info" %} -Unlike `key:convert:pkcs1`, this command also handles `OKP` keys (`Ed25519`, `X25519`). It supports every curve of the framework, the [Brainpool](../the-components/key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) ones included. -{% endhint %} +!!! info + + Unlike `key:convert:pkcs1`, this command also handles `OKP` keys (`Ed25519`, `X25519`). It supports every curve of the framework, the [Brainpool](../the-components/key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) ones included. -#### Key Generators +#### Key generators The key generator commands will generate a private or shared key. The following options are available: @@ -242,7 +242,7 @@ In case your secret is binary string, you will have to encode it first (Base64) ./jose.phar key:generate:from_secret "VGhpcyBpcyBteSBzZWNyZXQ=" --is_b64 ``` -#### Key Loaders +#### Key loaders The key loader commands will load keys from various sources. The following options are available: @@ -279,7 +279,7 @@ This command can load and convert a X.509 key file into a JWK. {"kty":"OKP","crv":"X25519","x":"TgTD7RS0KF3eU8HdTM6ACxu365uco3x2Cee9SBXiu2I"} ``` -#### RSA Key Optimization +#### RSA key optimization This command optimizes a RSA key by calculating additional primes (CRT). The following option is available: @@ -291,7 +291,7 @@ This command optimizes a RSA key by calculating additional primes (CRT). The fol _RSA keys generated by this framework are already optimized. This command may be needed when you import RSA keys from external sources._ _The optimization is not mandatory but highly recommended. Cryptographic operations are up to 10 times faster._ -#### Key Thumbprint +#### Key thumbprint This command will calculate the key thumbprint as per the [RFC7638](https://tools.ietf.org/html/rfc7638). The following options are available: @@ -303,9 +303,9 @@ This command will calculate the key thumbprint as per the [RFC7638](https://tool gNur2UtA8NMAoxJfgMYhJqnuWR8u-60aeRbKtZwj4DE ``` -### Keyset Management Commands +### Keyset management commands -#### Private Keys To Public Keys Converter +#### Private keys to public keys converter This command has the same effect as `key:convert:public` except that it will convert all keys in the keyset. It has no effect on shared keys (e.g. `oct` keys). @@ -315,7 +315,7 @@ This command has the same effect as `key:convert:public` except that it will con ``` -#### Key Analyze +#### Key analyze This command has the same behaviour as `key:analyze` except that it will analyze all keys in the keyset. @@ -325,7 +325,7 @@ This command has the same behaviour as `key:analyze` except that it will analyze ``` -#### Keyset Generators +#### Keyset generators The key set generator commands will generate key sets with random keys of the same type. @@ -342,13 +342,13 @@ Examples: The result of these commands is a JWKSet object. -#### Key Set Modification +#### Key set modification * `keyset:add:key`: Add a key into a key set. * `keyset:merge`: Merge several key sets into one. * `keyset:rotate`: Rotate a key set. -#### Distant Key Set Loading +#### Distant key set loading * `keyset:load:jku`: Loads a key set from an url. * `keyset:load:x5u`: Loads a key set from an url. diff --git a/console-command/phar-application.md b/docs/console-command/phar-application.md similarity index 74% rename from console-command/phar-application.md rename to docs/console-command/phar-application.md index 875c4cb..bdee355 100644 --- a/console-command/phar-application.md +++ b/docs/console-command/phar-application.md @@ -1,4 +1,4 @@ -# PHAR Application +# PHAR application ## Installation @@ -12,6 +12,6 @@ To use it, just execute the following line: ./jose.phar ``` -{% hint style="info" %} -You may need to set it as executable: `chmod +x jose.phar` -{% endhint %} +!!! info + + You may need to set it as executable: `chmod +x jose.phar` diff --git a/console-command/standalone.md b/docs/console-command/standalone.md similarity index 94% rename from console-command/standalone.md rename to docs/console-command/standalone.md index ed2a7a0..002650d 100644 --- a/console-command/standalone.md +++ b/docs/console-command/standalone.md @@ -1,4 +1,4 @@ -# Standalone Application +# Standalone application ## Installation diff --git a/console-command/symfony-console.md b/docs/console-command/symfony-console.md similarity index 100% rename from console-command/symfony-console.md rename to docs/console-command/symfony-console.md diff --git a/introduction/contributing.md b/docs/introduction/contributing.md similarity index 87% rename from introduction/contributing.md rename to docs/introduction/contributing.md index b969fd7..f5846f6 100644 --- a/introduction/contributing.md +++ b/docs/introduction/contributing.md @@ -2,9 +2,9 @@ We welcome contributions to the JWT Framework! Whether you're fixing bugs, adding features, improving documentation, or helping others, your contributions make this project better for everyone. -## Ways to Contribute +## Ways to contribute -### 🐛 Report Bugs +### 🐛 report bugs Found a bug? Please [open an issue](https://github.com/web-token/jwt-framework/issues) with: - A clear description of the problem - Steps to reproduce the issue @@ -12,14 +12,14 @@ Found a bug? Please [open an issue](https://github.com/web-token/jwt-framework/i - Your PHP version and framework version - Any relevant code snippets or error messages -### ✨ Suggest Features +### ✨ suggest features Have an idea for a new feature? We'd love to hear it! Please: - Check existing issues to avoid duplicates - Describe the use case and benefits - Propose how it might work - Consider backward compatibility -### 💻 Submit Code +### 💻 submit code If you feel comfortable writing code, you can: - Fix [issues where help is wanted](https://github.com/web-token/jwt-framework/labels/help+wanted) @@ -34,7 +34,7 @@ If you feel comfortable writing code, you can: 4. Update documentation if needed 5. Make sure all tests pass -### 📚 Improve Documentation +### 📚 improve documentation Documentation is crucial! You can help by: - Fixing typos and grammar @@ -43,14 +43,14 @@ Documentation is crucial! You can help by: - Translating documentation - Writing tutorials or guides -### 💬 Help Others +### 💬 help others - Answer questions on GitHub Discussions - Help triage issues - Review pull requests - Share your experience using the framework -## Development Setup +## Development setup ```bash # Clone the repository @@ -70,7 +70,7 @@ composer cs-check composer cs-fix ``` -## Code Guidelines +## Code guidelines - Follow **PSR-12** coding standards - Write **comprehensive tests** for new features @@ -92,17 +92,17 @@ Run the test suite: vendor/bin/phpunit ``` -## Security Issues +## Security issues -{% hint style="danger" %} -**If you discover a security vulnerability, DO NOT open a public issue.** +!!! danger -Please report security issues privately through [GitHub Security Advisories](https://github.com/web-token/jwt-framework/security/advisories). -{% endhint %} + **If you discover a security vulnerability, DO NOT open a public issue.** + + Please report security issues privately through [GitHub Security Advisories](https://github.com/web-token/jwt-framework/security/advisories). Security vulnerabilities will be addressed with the highest priority. We appreciate responsible disclosure and will acknowledge your contribution. -## Code of Conduct +## Code of conduct This project adheres to a Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainers. diff --git a/docs/introduction/pre-requisite.md b/docs/introduction/pre-requisite.md new file mode 100644 index 0000000..b74a879 --- /dev/null +++ b/docs/introduction/pre-requisite.md @@ -0,0 +1,22 @@ +# Prerequisites + +This framework needs at least: + +* PHP 8.2+ +* Extensions (required): + * openssl + +* Extensions (recommended): + * gmp or bcmath (for better performance with RSA/PSS algorithms) + * sodium (for EdDSA with Ed25519, ECDH-ES with X25519, and faster Base64URL encoding) + * curl (for loading keys from remote URLs) + +!!! info + + The `json` extension is built-in with PHP 8.2+ and no longer needs to be installed separately. + +!!! info + + When the `ext-sodium` extension is available, the framework automatically uses it for Base64URL encoding/decoding operations, providing significant performance improvements. + +Please note that cryptographic operations may be really slow, especially RSA functions. It is highly recommended to enable GMP or BCMath. diff --git a/docs/introduction/provided-features.md b/docs/introduction/provided-features.md new file mode 100644 index 0000000..7c01eae --- /dev/null +++ b/docs/introduction/provided-features.md @@ -0,0 +1,107 @@ +# Provided features + +## Supported input types + +JWS or JWE objects support every input that can be encoded into JSON: + +* `string`, `array`, `integer`, `float`... +* Objects that implement the `\JsonSerializable` interface such as `JWK` or `JWKSet` + +The [detached payload](../advanced-topics/signed-tokens/detached-payload.md) is supported. + +## Supported serialization modes + +| Serialization syntax | JWS | JWE | +| -------------------- | --- | --- | +| Compact | YES | YES | +| Flattened JSON | YES | YES | +| General JSON | YES | YES | + +## Supported compression methods + +!!! danger + + Compression is not recommended. Since version 4.0, compression support has been removed. See [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725#section-3.6) for more information. + +## Supported key types (JWK) + +| Key Type | Supported | Comment | +| -------- | --------- | ------------------------------------- | +| oct | YES | Symmetric keys | +| RSA | YES | RSA based asymmetric keys | +| EC | YES | Elliptic Curves based asymmetric keys | +| OKP | YES | Octet Key Pair based asymmetric keys | + +JWK objects support JSON Web Key Thumbprint ([RFC 7638](https://tools.ietf.org/html/rfc7638)). + +!!! info + + A `none` key type is available for the `none` algorithm. It is used to explicitly allow this unsecured algorithm. + +## Supported elliptic curves + +| Key Type | Curve | Supported | Comment | +| -------- | ---------------------------------- | --------- | ------------------------------------------------------------------------------------------------ | +| EC |P-256
P-384
P-521| YES | ECDSA signatures (`ES256`/`ES384`/`ES512`) and `ECDH-ES*`/`ECDH-SS*` key agreement | +| EC | secp256k1 | YES | `ES256K`, in the `Jose\Experimental` namespace | +| EC |BP-256
BP-384
BP-512| YES | Brainpool curves. `BP256R1`/`BP384R1`/`BP512R1` signatures are in the `Jose\Experimental` namespace | +| OKP | Ed25519 | YES | `EdDSA` signatures. SODIUM extension is required | +| OKP | X25519 | YES | `ECDH-ES*` key agreement. SODIUM extension is required | +| OKP |Ed448
X448| NO | No extension or built-in implementation available | + +!!! warning + + The Brainpool curves are not registered with IANA, and neither are their signature algorithms. The identifiers follow the convention adopted by the other implementations, so keys and tokens using them are only interoperable with the implementations sharing that convention. + +## Key sets (JWKSet) + +JWKSet is fully supported. + +## Supported signature algorithms + +| Signature Algorithm | Supported | Comment | +| ------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------- | +|HS256
HS384
HS512| YES | | +|ES256
ES384
ES512| YES | | +|RS256
RS384
RS512| YES | | +|PS256
PS384
PS512| YES | GMP or BCMath extension is highly recommended | +| EdDSA with Ed25519 curve | YES | SODIUM extension is required | +| EdDSA with Ed448 curve | NO | No extension or built-in implementation available | +| none | YES | **Please note that this is not a secured algorithm. USE IT WITH CAUTION!** | + +!!! info + + Other signature algorithms like `RS1`, `HS1`, `HS256/64`, `ES256K`, `BP256R1`, `BP384R1`, `BP512R1` and `Blake2b` are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. + +## Supported key encryption algorithms + +| Key Encryption Algorithm | Supported | | +| ----------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| dir | YES | | +|RSA1_5
RSA-OAEP
RSA-OAEP-256| YES |GMP or BCMath extension is highly recommended

Read note below!| +|ECDH-ES
ECDH-ES+A128KW
ECDH-ES+A192KW
ECDH-ES+A256KW| YES | `spomky-labs/aes-key-wrap` is required for \*KW algorithms. See the [supported curves](#supported-elliptic-curves) | +|ECDH-SS
ECDH-SS+A128KW
ECDH-SS+A192KW
ECDH-SS+A256KW| YES | `spomky-labs/aes-key-wrap` is required for \*KW algorithms. See the [supported curves](#supported-elliptic-curves) | +|A128KW
A192KW
A256KW| YES | `spomky-labs/aes-key-wrap` is required | +|PBES2-HS256+A128KW
PBES2-HS384+A192KW
PBES2-HS512+A256KW| YES | `spomky-labs/aes-key-wrap` is required | +|A128GCMKW
A192GCMKW
A256GCMKW| YES | `spomky-labs/aes-key-wrap` is required | +| ECDH-ES with X25519 curve | YES | SODIUM extension is required | +| ECDH-ES with X448 curve | NO | No extension or built-in implementation available | + +!!! info + + Other key encryption algorithms like `RSA-OAEP-384`, `RSA-OAEP-512`, and `chacha20-poly1305` are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. + +!!! danger + + The algorithms `RSA1_5` and `RSA-OAEP` are now deprecated. Please use with caution. + +## Supported content encryption algorithms + +| Content Encryption Algorithm | Supported | +| ------------------------------------------------------------ | --------- | +|A128CBC-HS256
A192CBC-HS384
A256CBC-HS512| YES | +|A128GCM
A192GCM
A256GCM| YES | + +!!! info + + Other content encryption algorithms like `A128CTR`, `A192CTR`, `A256CTR`, and various AES-CCM algorithms (e.g., `A128CCM-16-128`, `A256CCM-64-64`) are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. diff --git a/introduction/security-recommendations.md b/docs/introduction/security-recommendations.md similarity index 87% rename from introduction/security-recommendations.md rename to docs/introduction/security-recommendations.md index 82f4bf6..4789561 100644 --- a/introduction/security-recommendations.md +++ b/docs/introduction/security-recommendations.md @@ -1,4 +1,4 @@ -# Security Recommendations +# Security recommendations Signed or Encrypted Tokens are not just the next trendy/popular way to authenticate users. They provide great features but when used incorrectly they can expose your application to major security issues. @@ -6,32 +6,32 @@ Please read the following recommendations carefully. ## Algorithms -### Algorithm Choice +### Algorithm choice If all parties are able to protect their keys (e.g. private applications), symmetric algorithms are a good choice as they are faster in general. If you use public clients, you should prefer asymmetric algorithms. -### Available Algorithms +### Available algorithms This framework provides dozens of signature and encryption algorithms, but you do not need all of them. Most applications only support 1 or 2 algorithms. You should only use necessary algorithms. For example, HS384 algorithm may be avoided if you already have HS256 and HS512. -### Avoid Weak Algorithms +### Avoid weak algorithms Some algorithms are not recommended as there are known security issues: * `none`: this algorithm is not a real algorithm. It should only be used when other security means exist. An encrypted connection is certainly not enough! * `RSA1_5`: there are known attacks using this algorithm. If you can avoid its use, then do it. -## Keys And Key Sets +## Keys and key sets -### Key Size +### Key size A small key size is as secure as a password like `123456789`. You should use at least 256 bits symmetric keys and at least 2048 bits RSA keys. In any case, you MUST use a true random number generator. -### Additional Information +### Additional information It is highly recommended to set the following parameters to your key: @@ -39,13 +39,13 @@ It is highly recommended to set the following parameters to your key: * `use`: indicates the usage of the key. Either `sig` (signature/verification) or `enc` (encryption/decryption). * `alg`: the algorithm allowed to be used with this key. -### Key Rotation +### Key rotation A key is fine but may be cracked e.g. by bruteforce. Changing your keys after several days or weeks is encouraged. -## Token Creation +## Token creation -### Header Parameters +### Header parameters Broadly speaking, you should set your header parameter in the protected header. The use of the unprotected header should be limited to specific use cases. @@ -53,19 +53,19 @@ With encrypted tokens, a header parameter must appear in **exactly one** of the When using encrypted tokens, the claims `iss` and `aud` should be duplicated into the header. This will avoid unwanted decryption when tokens are sent to a wrong audience. -### Payload And Claims +### Payload and claims There is no size constraint for the payload, but when tokens are used in a web context, it should be as small as possible. When used, claims should be limited to the minimum. Does your application really need to get all the information about a user? For each context, you should choose carefully the claims you want to use. -#### Token Unique ID +#### Token unique ID A unique token ID should be set to all tokens you create. The associated claim is `jti`. This claim is highly recommended as it can prevent replay attacks. -#### Time Indicators +#### Time indicators The JWT specification introduces several claims to limit the period of validity of the tokens: @@ -75,17 +75,17 @@ The JWT specification introduces several claims to limit the period of validity These claims are not mandatory, but it is recommended to define a period of time for the token validity. When used, the expiration time should be appropriate to its context in your application. A security token with 2 weeks lifetime is something you should avoid. -#### Issuer And Audience +#### Issuer and audience The claims `iss` (issuer) and `aud` (audience) should always be set. When duplicated in the header, their values MUST be identical. -## Application Communication +## Application communication -### Secured Connection +### Secured connection Unless you use encrypted tokens, you should use a secured connection when transmitting tokens between parties. A secured communication is not only needed when transmitting tokens, but also when you exchange keys and key sets with other applications. -## Loading Process +## Loading process When you receive a token, the following steps should be followed in this order. If one failed, you **must** reject the whole token. @@ -95,15 +95,15 @@ When you receive a token, the following steps should be followed in this order. 2. Verify the signature (JWS) or decrypt the token (JWE) 3. Check the claims in the payload (if any) -{% hint style="warning" %} -Regarding header verification, it is important to verify **ALL** of them. There are known vulnerabilities on e.g. algorithm swapping (`alg` changed to `none`) or Denial Of Service (`p2c` set to a very high value for overloading the server). -{% endhint %} +!!! warning -### Unserialize The Token + Regarding header verification, it is important to verify **ALL** of them. There are known vulnerabilities on e.g. algorithm swapping (`alg` changed to `none`) or Denial Of Service (`p2c` set to a very high value for overloading the server). + +### Unserialize the token You should only use the serialization mode(s) you need. If you intend to use your tokens in a web context, then use only the Compact Serialization. If an error occurred during this process, you should consider the token as invalid. -### Check The Header +### Check the header It is strongly recommended that all header parameters are checked before the cryptographic operations (signature verification or content decryption). @@ -111,17 +111,17 @@ Please note that unknown header parameters must be ignored. If your token is ver When used, unprotected header parameters should be handled with care. They are not covered by the signature or the AAD: never let them drive a cryptographic decision, and never trust a value you only found there. -### Signature Verification / Payload Decryption +### Signature verification / payload decryption Let the component do its job. The most important step for developers is to ensure that the right key/key set is used. -### Check The Claims +### Check the claims This step is only required if the payload contains claims. When present, you should always check the `exp`, `iat`, `nbf`, `iss` and `aud` claims. Application-specific claims should also always be checked. The whole token should be rejected in case of failure. Unknown claims should be ignored. -## Stay Tuned +## Stay tuned You should subscribe to security forums and maintain a continuous technological watch. The tokens may be compromised because of malicious attacks on the algorithms, keys or other components related to the JWT (directly or indirectly). diff --git a/introduction/the-framework.md b/docs/introduction/the-framework.md similarity index 83% rename from introduction/the-framework.md rename to docs/introduction/the-framework.md index 3cc3d79..a0481b0 100644 --- a/introduction/the-framework.md +++ b/docs/introduction/the-framework.md @@ -1,4 +1,4 @@ -# The Framework +# The framework The JWT Framework is a comprehensive and modular PHP library for working with JSON Web Tokens. It provides a complete implementation of the JOSE (JSON Object Signing and Encryption) specifications, designed to be both powerful and easy to use. @@ -6,7 +6,7 @@ The JWT Framework is a comprehensive and modular PHP library for working with JS The framework follows a clean, modular architecture that allows you to use only what you need. The codebase is organized into three main packages: -### Core Library (`web-token/jwt-library`) +### Core library (`web-token/jwt-library`) The core library contains all the essential components for working with JWTs: @@ -35,7 +35,7 @@ The core library contains all the essential components for working with JWTs: - Token inspection and validation tools - Key conversion utilities -### Symfony Bundle (`web-token/jwt-bundle`) +### Symfony bundle (`web-token/jwt-bundle`) The Symfony bundle provides seamless integration with Symfony applications: @@ -46,7 +46,7 @@ The Symfony bundle provides seamless integration with Symfony applications: - **Events** - Token lifecycle events for logging and monitoring - **Controller Helpers** - Utilities for common token operations -### Experimental Features (`web-token/jwt-experimental`) +### Experimental features (`web-token/jwt-experimental`) The experimental package contains cutting-edge features and algorithms: @@ -55,15 +55,15 @@ The experimental package contains cutting-edge features and algorithms: - **Compatibility Algorithms** - Support for legacy systems (RS1, HS1, HS256/64) - **Specialized Encryption** - AES-CCM variants and ChaCha20-Poly1305 -{% hint style="warning" %} -Experimental features should be used with caution in production environments. They may change between minor versions and are primarily intended for testing, compatibility, or research purposes. -{% endhint %} +!!! warning -## Design Principles + Experimental features should be used with caution in production environments. They may change between minor versions and are primarily intended for testing, compatibility, or research purposes. + +## Design principles The framework is built on several key principles: -### 1. **Security First** +### 1. **Security first** - Follows RFC specifications strictly - Implements security best practices by default - Provides clear warnings about weak or deprecated algorithms @@ -75,7 +75,7 @@ The framework is built on several key principles: - Extensible - create custom algorithms, checkers, and serializers - Framework-agnostic core with optional Symfony integration -### 3. **Developer Experience** +### 3. **Developer experience** - Fluent, intuitive APIs - Comprehensive error messages - Extensive documentation with examples @@ -87,7 +87,7 @@ The framework is built on several key principles: - Optional GMP/BCMath for faster cryptographic operations - Caching support for remote key sets -## Package Selection Guide +## Package selection guide Choose the right package for your needs: @@ -97,9 +97,9 @@ Choose the right package for your needs: | `web-token/jwt-bundle` | **Symfony** - You're building a Symfony application (includes the library) | | `web-token/jwt-experimental` | **Advanced** - You need experimental algorithms or features | -{% hint style="info" %} -You may come across `web-token/jwt-framework` in older instructions. It is the root package of the monorepo and `replace`s the three packages above, so requiring it installs everything at once — including the experimental algorithms. Prefer `web-token/jwt-library` or `web-token/jwt-bundle` so your dependencies only contain what you actually use. -{% endhint %} +!!! info + + You may come across `web-token/jwt-framework` in older instructions. It is the root package of the monorepo and `replace`s the three packages above, so requiring it installs everything at once — including the experimental algorithms. Prefer `web-token/jwt-library` or `web-token/jwt-bundle` so your dependencies only contain what you actually use. ## Installation @@ -120,7 +120,7 @@ The Symfony bundle will be automatically registered. composer require web-token/jwt-experimental ``` -## Next Steps +## Next steps - **New to JWT?** Start with [Provided Features](provided-features.md) to see what's available - **Security conscious?** Read [Security Recommendations](security-recommendations.md) carefully diff --git a/migration/from-v1.x-to-v2.0.md b/docs/migration/from-v1.x-to-v2.0.md similarity index 93% rename from migration/from-v1.x-to-v2.0.md rename to docs/migration/from-v1.x-to-v2.0.md index cc71c7a..1467792 100644 --- a/migration/from-v1.x-to-v2.0.md +++ b/docs/migration/from-v1.x-to-v2.0.md @@ -1,13 +1,13 @@ -# Migration Guide: From v1.x to v2.0 +# Migration guide: From v1.x to v2.0 This guide will help you upgrade your application from JWT Framework v1.x to v2.0. While minor version upgrades are generally straightforward, major version upgrades involve breaking changes that require careful attention. -{% hint style="info" %} -**Estimated Migration Time:** 1-3 hours for a typical application -**Difficulty Level:** Moderate -{% endhint %} +!!! info -## Overview of Changes + **Estimated Migration Time:** 1-3 hours for a typical application + **Difficulty Level:** Moderate + +## Overview of changes Version 2.0 introduces several important changes: - Removal of static factory methods in favor of constructors @@ -15,7 +15,7 @@ Version 2.0 introduces several important changes: - Removal of unnecessary converter classes - Improved API consistency across components -## Migration Steps +## Migration steps ## Update the libraries diff --git a/migration/from-v2.x-to-v3.0.md b/docs/migration/from-v2.x-to-v3.0.md similarity index 88% rename from migration/from-v2.x-to-v3.0.md rename to docs/migration/from-v2.x-to-v3.0.md index 506b6ec..c9104d0 100644 --- a/migration/from-v2.x-to-v3.0.md +++ b/docs/migration/from-v2.x-to-v3.0.md @@ -26,9 +26,9 @@ It is highly recommended to declare this server within your application `compose composer config --json extra.symfony.endpoint '["https://api.github.com/repos/web-token/recipes/contents/index.json?ref=main", "flex://defaults"]' ``` -{% hint style="info" %} -You can adapt this command line depending on the other Flex servers you are using. -{% endhint %} +!!! info + + You can adapt this command line depending on the other Flex servers you are using. ## Upgrade the dependencies and the libraries @@ -40,6 +40,6 @@ It is now time to upgrade the libraries. In your composer.json, change all `web- You can also update all other dependencies if needed. You can list upgradable libraries by calling `composer outdated`. This step is not mandatory, but highly recommended. -{% hint style="success" %} -[Rector](https://github.com/rectorphp/rector) is a very nice tool for code upgrade. We highly recommend it. -{% endhint %} +!!! success + + [Rector](https://github.com/rectorphp/rector) is a very nice tool for code upgrade. We highly recommend it. diff --git a/migration/from-v3.x-to-v4.0.md b/docs/migration/from-v3.x-to-v4.0.md similarity index 86% rename from migration/from-v3.x-to-v4.0.md rename to docs/migration/from-v3.x-to-v4.0.md index f754f0a..184cb97 100644 --- a/migration/from-v3.x-to-v4.0.md +++ b/docs/migration/from-v3.x-to-v4.0.md @@ -10,15 +10,15 @@ First of all, you have to make sure you are using the last v3.x release. Next, you have to verify you don’t use any deprecated class, interface, method or property. If you have PHPUnit tests, [you can easily get the list of deprecation used in your application](https://symfony.com/doc/current/components/phpunit_bridge.html). -## Breaking Changes +## Breaking changes -### PSR-20 Clock — Constructor Signature Changed +### PSR-20 clock — constructor signature changed The time-based checkers now **require** a `Psr\Clock\ClockInterface` as the **first** constructor argument. In v3.x, the clock was optional and passed as the last parameter. The argument order has been changed. -{% hint style="danger" %} -This is a breaking change. You must update every instantiation of these classes. -{% endhint %} +!!! danger + + This is a breaking change. You must update every instantiation of these classes. The affected classes are: @@ -57,7 +57,7 @@ $iatChecker = new IssuedAtChecker(clock: $clock, allowedTimeDrift: 10); The internal `InternalClock` class and the Symfony Bundle service `jose.internal_clock` have been removed. You must provide your own `ClockInterface` implementation (e.g. `Symfony\Component\Clock\NativeClock`). -### JWEBuilder and JWEDecrypter — Simplified Constructor +### JWEBuilder and JWEDecrypter — simplified constructor `JWEBuilder` and `JWEDecrypter` no longer accept separate Key Encryption and Content Encryption Algorithm Managers, nor a Compression Method Manager. You must now pass a single `AlgorithmManager` containing all required algorithms. @@ -104,7 +104,7 @@ $jweBuilder = new JWEBuilder($algorithmManager); $jweDecrypter = new JWEDecrypter($algorithmManager); ``` -### JWEBuilderFactory and JWEDecrypterFactory — Simplified Signatures +### JWEBuilderFactory and JWEDecrypterFactory — simplified signatures The factory classes have also been simplified accordingly. @@ -135,7 +135,7 @@ $jweBuilder = $jweBuilderFactory->create( The same applies to `JWEDecrypterFactory`. -### Compression Support Removed +### Compression support removed Compression methods (`DEF` / Deflate) that were deprecated in v3.3 have been fully removed. This follows the security recommendation from [RFC 8725 Section 3.6](https://datatracker.ietf.org/doc/html/rfc8725#section-3.6). If your application uses JWE compression, you must remove it before upgrading. @@ -145,7 +145,7 @@ The following classes have been removed: * `Jose\Component\Encryption\Compression\Deflate` * All compression-related interfaces and classes -### Removed Dependencies +### Removed dependencies The following dependencies have been removed. If you relied on them directly, you may need to adjust your code: @@ -153,7 +153,7 @@ The following dependencies have been removed. If you relied on them directly, yo * `paragonie/constant_time_encoding` — replaced by an internal `Base64UrlSafe` utility * `psr/http-client` and `psr/http-factory` — replaced by `symfony/http-client-contracts` -### Version Bumped +### Version bumped All previous major releases of the following packages are not supported anymore. Please make sure your platform can use them. @@ -163,13 +163,13 @@ All previous major releases of the following packages are not supported anymore. * `spomky-labs/pki-framework`: 1.2.1+ (new dependency, replaces `fgrosse/phpasn1`) * `psr/clock`: 1.0+ (new dependency for PSR-20 Clock) -## Deprecated Packages +## Deprecated packages The following packages are deprecated. Please install `web-token/jwt-library` or `web-token/jwt-experimental` instead. -{% hint style="info" %} -In addition to the new library or experimental packages, you may need to require third party dependencies such as `ext-sodium`, `ext-openssl` or similar extensions. -{% endhint %} +!!! info + + In addition to the new library or experimental packages, you may need to require third party dependencies such as `ext-sodium`, `ext-openssl` or similar extensions. * `web-token/jwt-util-ecc` * `web-token/jwt-key-mgmt` @@ -184,16 +184,16 @@ In addition to the new library or experimental packages, you may need to require * `web-token/jwt-encryption-algorithm-*` * `web-token/jwt-nested-token` -## New Features +## New features -### New Checkers +### New checkers Two new generic checker classes have been added: * `Jose\Component\Checker\CallableChecker` — allows custom validation logic via a callable. Implements both `ClaimChecker` and `HeaderChecker`. * `Jose\Component\Checker\IsEqualChecker` — simple equality validation for claims and headers. Implements both `ClaimChecker` and `HeaderChecker`. -### ECDH-SS (Static-Static) Key Agreement +### ECDH-SS (Static-static) key agreement New key encryption algorithms have been added for ECDH Static-Static key agreement: @@ -202,7 +202,7 @@ New key encryption algorithms have been added for ECDH Static-Static key agreeme * `ECDH-SS+A192KW` * `ECDH-SS+A256KW` -### Code Modernization +### Code modernization Classes throughout the framework now use modern PHP 8.2+ features: @@ -211,7 +211,7 @@ Classes throughout the framework now use modern PHP 8.2+ features: * `#[Override]` attribute on overridden methods (PHP 8.3 compatibility) * Stricter type declarations -### Package Structure Reorganization +### Package structure reorganization The internal file structure has been reorganized: @@ -220,22 +220,22 @@ The internal file structure has been reorganized: * Experimental features are in `src/Experimental/` (namespace `Jose\Experimental\`) * A `src/Deprecated/` folder provides backward compatibility with old package references -{% hint style="info" %} -The PHP namespaces have not changed. If you use the public API, your code should continue to work without modification. -{% endhint %} +!!! info + + The PHP namespaces have not changed. If you use the public API, your code should continue to work without modification. ## Upgrade the dependencies and the libraries Please install PHP 8.2 or a newer version on your platform. Make sure the **OpenSSL** extension is installed (required). You may also need **Sodium** depending on the algorithms you want to use (EdDSA, ECDH-ES with X25519). -{% hint style="info" %} -The `json` and `mbstring` extensions are built-in with PHP 8.2+ and no longer need to be installed separately. -{% endhint %} +!!! info + + The `json` and `mbstring` extensions are built-in with PHP 8.2+ and no longer need to be installed separately. It is now time to upgrade the libraries. In your `composer.json`, change all `web-token/*` dependencies from `v3.x` to `v4.0`. When done, execute `composer update`. You can also update all other dependencies if needed. You can list upgradable libraries by calling `composer outdated`. This step is not mandatory, but highly recommended. -{% hint style="success" %} -[Rector](https://github.com/rectorphp/rector) is a very nice tool for code upgrade. We highly recommend it. -{% endhint %} +!!! success + + [Rector](https://github.com/rectorphp/rector) is a very nice tool for code upgrade. We highly recommend it. diff --git a/migration/from-v4.0-to-v4.1.md b/docs/migration/from-v4.0-to-v4.1.md similarity index 92% rename from migration/from-v4.0-to-v4.1.md rename to docs/migration/from-v4.0-to-v4.1.md index e78ea3f..8a1c710 100644 --- a/migration/from-v4.0-to-v4.1.md +++ b/docs/migration/from-v4.0-to-v4.1.md @@ -2,17 +2,17 @@ This is a minor version upgrade. Most applications should upgrade without any code changes. -## New Features +## New features -### Symfony 8.0 Support +### Symfony 8.0 support The Symfony Bundle now supports both Symfony 7.0 and Symfony 8.0. All Symfony component constraints have been updated accordingly. -### Sodium-based Base64URL Encoding +### Sodium-based base64URL encoding When the `ext-sodium` PHP extension is available, the framework now uses native Sodium functions for Base64URL encoding and decoding. This provides significant performance improvements without requiring any code changes on your side. -### PHP 8.5 Compatibility +### PHP 8.5 compatibility Several internal fixes have been applied to ensure forward compatibility with PHP 8.5: @@ -20,11 +20,11 @@ Several internal fixes have been applied to ensure forward compatibility with PH * `substr()` calls no longer pass `null` as the third parameter * `chr()` calls are now constrained to the 0-255 range -### Sensitive Parameter Attributes +### Sensitive parameter attributes Methods handling potentially sensitive data in `Base64UrlSafe` now use the PHP 8.2+ `#[SensitiveParameter]` attribute. This prevents sensitive values from being leaked in stack traces or debug output. -### brick/math 0.14 Support +### brick/math 0.14 support The framework now supports `brick/math` version 0.14, in addition to 0.12 and 0.13. @@ -60,7 +60,7 @@ jose: client: 'http_client' # Use a cached HTTP client ``` -## Console Commands +## Console commands Console commands have been refactored for Symfony Console 7.0+ compatibility: diff --git a/migration/from-v4.1-to-v4.2.md b/docs/migration/from-v4.1-to-v4.2.md similarity index 93% rename from migration/from-v4.1-to-v4.2.md rename to docs/migration/from-v4.1-to-v4.2.md index ea4bf33..124df5e 100644 --- a/migration/from-v4.1-to-v4.2.md +++ b/docs/migration/from-v4.1-to-v4.2.md @@ -4,9 +4,9 @@ This is a minor version upgrade. Most applications should upgrade without any co Two behaviour changes may however affect you: the [disjoint header requirement](#stricter-header-validation-on-decryption) is now enforced when an encrypted token is read, and a key set no longer discards keys sharing the same `kid`. Both are described below. -## New Features +## New features -### Brainpool Curves +### Brainpool curves The `brainpoolP256r1`, `brainpoolP384r1` and `brainpoolP512r1` curves are now supported under the `BP-256`, `BP-384` and `BP-512` identifiers. They can be used for: @@ -14,9 +14,9 @@ The `brainpoolP256r1`, `brainpoolP384r1` and `brainpoolP512r1` curves are now su * `ECDH-ES` and `ECDH-SS` key agreement, * the PEM/JWK conversions, in both directions. -{% hint style="warning" %} -Neither the curves nor the signature algorithms are registered with IANA. The identifiers follow the convention already adopted by the other implementations (gematik, jwcrypto), which is why the signature algorithms live in the `Jose\Experimental\Signature` namespace, next to `ES256K`. -{% endhint %} +!!! warning + + Neither the curves nor the signature algorithms are registered with IANA. The identifiers follow the convention already adopted by the other implementations (gematik, jwcrypto), which is why the signature algorithms live in the `Jose\Experimental\Signature` namespace, next to `ES256K`. ```php get('key-1'); // still returns the first key registered with that ID ## Deprecations -### The Hardcoded RSA1\_5 CEK Size Table +### The hardcoded RSA1\_5 CEK size table `RSA1_5` needs the size of the key expected by the content encryption algorithm to perform its implicit rejection ([GHSA-5739-39v2-5754](https://github.com/web-token/jwt-framework/security/advisories/GHSA-5739-39v2-5754)). It used to guess that size from a hardcoded table of content encryption algorithm names. @@ -147,7 +147,7 @@ public function decryptKey(JWK $key, string $encrypted_cek, array $header): stri The value is the one returned by `ContentEncryptionAlgorithm::getCEKSize()`. See [Custom Algorithm](../advanced-topics/custom-algorithm.md#the-expected-cek-size). -## Bug Fixes Worth Knowing +## Bug fixes worth knowing These fixes need no action on your side, but they may explain an error you were seeing: diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 0000000..8de097c --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} + +{#- + The favicons and the share image of the design kit (4-Web/icones, 4-Web/partage). The SVG favicon is + theme.favicon; the ICO and the Apple touch icon complete it. +-#} +{% block extrahead %} + + + + + {% set title = (page.title ~ " · " ~ config.site_name) if page and page.title and not page.is_homepage else config.site_name %} + {% set description = (page.meta.description if page and page.meta and page.meta.description else config.site_description) %} + + + + + {% if page and page.canonical_url %}{% endif %} + + + + + +{% endblock %} diff --git a/docs/overrides/partials/copyright.html b/docs/overrides/partials/copyright.html new file mode 100644 index 0000000..7fe0bb4 --- /dev/null +++ b/docs/overrides/partials/copyright.html @@ -0,0 +1,22 @@ +{#- + The footer of the Slabs guidelines: the reversed signature, the links in Calcaire, the legal line in Béton. + The signature is the drawing of the design kit, never retyped. Its links and the legal line are in + extra.slabs of mkdocs.yml. +-#} +{% set slabs = config.extra.slabs %} + diff --git a/docs/overrides/partials/logo.html b/docs/overrides/partials/logo.html new file mode 100644 index 0000000..ea4e744 --- /dev/null +++ b/docs/overrides/partials/logo.html @@ -0,0 +1,8 @@ +{#- + The project mark of JWT Framework: the Slabs monogram, the name of the project and "by Slabs". The + colour version on a light ground, the reversed one on a dark ground, from the web set of the design kit + without their metadata (tools/web-svg.py of Spomky-Labs/docs). The link around them carries the + accessible name, so the images say nothing. +-#} + + diff --git a/docs/overrides/partials/source.html b/docs/overrides/partials/source.html new file mode 100644 index 0000000..3d8f7f0 --- /dev/null +++ b/docs/overrides/partials/source.html @@ -0,0 +1,13 @@ +{#- + The link to the repository, without the stars and the forks: the theme would ask the API of GitHub for + them from the browser of the reader, and this site sends nothing to a third party. +-#} + +
+ {% set icon = config.theme.icon.repo or "fontawesome/brands/git-alt" %} + {% include ".icons/" ~ icon ~ ".svg" %} +
+
+ {{ config.repo_name }} +
+
diff --git a/the-components/algorithm-management-jwa.md b/docs/the-components/algorithm-management-jwa.md similarity index 93% rename from the-components/algorithm-management-jwa.md rename to docs/the-components/algorithm-management-jwa.md index f62b2d1..785d575 100644 --- a/the-components/algorithm-management-jwa.md +++ b/docs/the-components/algorithm-management-jwa.md @@ -1,4 +1,4 @@ -# Algorithm Management (JWA) +# Algorithm management (JWA) For each cryptographic operation, you will need at least one algorithm and one key. @@ -19,11 +19,11 @@ $algorithmManager = new AlgorithmManager([ ]); ``` -{% hint style="warning" %} -It is not possible to set the same algorithm twice in the same algorithm manager. -{% endhint %} +!!! warning -## Algorithm Manager Factory + It is not possible to set the same algorithm twice in the same algorithm manager. + +## Algorithm manager factory Your application may need several algorithm managers for several use cases. For example your application may use JWT for: diff --git a/the-components/claim-checker.md b/docs/the-components/claim-checker.md similarity index 94% rename from the-components/claim-checker.md rename to docs/the-components/claim-checker.md index d5d1b66..da8dc7c 100644 --- a/the-components/claim-checker.md +++ b/docs/the-components/claim-checker.md @@ -1,14 +1,14 @@ -# Claim Checker +# Claim checker JSON Web Tokens can be used to transport any kind of data. They are mainly used to transport claims. When you receive a token that contains claims, it is important to check the values of these claims. -## Claim Checker Manager +## Claim checker manager In the following example, we will create a manager able to check the `aud` (Audience), `iat` (Issued At), `nbf` (Not Before) and `exp` (Expiration) claims. -{% hint style="info" %} -Time-based checkers require a [PSR-20 clock](https://www.php-fig.org/psr/psr-20/). -{% endhint %} +!!! info + + Time-based checkers require a [PSR-20 clock](https://www.php-fig.org/psr/psr-20/). ```php check($claims, ['iss', 'sub', 'aud']); ``` -## Custom Claim Checker +## Custom claim checker Your application may use other claims that you will have to check, therefore custom claim checkers have to be created. @@ -89,7 +89,7 @@ final class FooChecker implements ClaimChecker All done! Now you can instantiate your class and add it to your Claim Checker Manager. -## Replicating Claims as Header Parameters +## Replicating claims as header parameters The [RFC 7519 section 5.3](https://tools.ietf.org/html/rfc7519#section-5.3) allows replicating some claims in the header. This behaviour is very useful with encrypted tokens as it helps to reject invalid tokens without decryption of the payload. @@ -97,7 +97,7 @@ The Claim Checker Manager cannot check those replicated claims, you have to crea Have a look at the `IssuedAtChecker` or the `NotBeforeChecker` classes. These checkers can be used for claim and header checks. -## Claim Checker Manager Factory +## Claim checker manager factory Your application may use JSON Web Tokens in different contexts and thus the meaning of a claim may be different. You will need several Claim Checker Managers with dedicated claim checkers. diff --git a/the-components/encrypted-tokens-jwe/README.md b/docs/the-components/encrypted-tokens-jwe/README.md similarity index 93% rename from the-components/encrypted-tokens-jwe/README.md rename to docs/the-components/encrypted-tokens-jwe/README.md index a3745a9..a2ea27f 100644 --- a/the-components/encrypted-tokens-jwe/README.md +++ b/docs/the-components/encrypted-tokens-jwe/README.md @@ -1,4 +1,4 @@ -# Encrypted Tokens (JWE) +# Encrypted tokens (JWE) A very large number of key and content encryption algorithms is provided by the library. Please refer to [this encryption algorithm table](encryption-algorithms.md) to know what algorithms are available. diff --git a/docs/the-components/encrypted-tokens-jwe/encryption-algorithms.md b/docs/the-components/encrypted-tokens-jwe/encryption-algorithms.md new file mode 100644 index 0000000..883164b --- /dev/null +++ b/docs/the-components/encrypted-tokens-jwe/encryption-algorithms.md @@ -0,0 +1,107 @@ +# Encryption algorithms + +This framework comes with several encryption algorithms. These algorithms are in the following namespaces: + +* `Jose\Component\Encryption\Algorithm\KeyEncryption`: key encryption algorithms +* `Jose\Component\Encryption\Algorithm\ContentEncryption`: content encryption algorithms + +## Main algorithms + +### Key encryption + +!!! info + + `spomky-labs/aes-key-wrap` is required for \*KW algorithms + +| Algorithm | Additional header parameter | +|---|---| +| A128KW
A192KW
A256KW | No | +| A128GCMKW
A192GCMKW
A256GCMKW | `iv`: (initialization vector) this value is the base64url-encoded representation of the 96-bit IV value used for the key encryption operation.
`tag`: (authentication tag) the value is the base64url-encoded representation of the 128-bit Authentication Tag value resulting from the key encryption operation. | +| dir | No | +| ECDH-ES
ECDH-ES+A128KW
ECDH-ES+A192KW
ECDH-ES+A256KW | `epk`: (ephemeral public key) value created by the originator. | +| ECDH-SS
ECDH-SS+A128KW
ECDH-SS+A192KW
ECDH-SS+A256KW | No | +| PBES2-HS256+A128KW
PBES2-HS384+A192KW
PBES2-HS512+A256KW | `p2s`: (PBES2 salt input) encodes a Salt Input value, which is used as part of the PBKDF2 salt value.
`p2c`: (PBES2 count) contains the PBKDF2 iteration count, represented as a positive JSON integer. | +| RSA1_5
RSA-OAEP
RSA-OAEP-256 | | + +!!! warning + + Please note that the additional header parameters **MUST** be present and **MUST** be understood. Depending on the algorithm you use, you may be required to check headers BEFORE the decryption operation. Please create a [custom Header Checker](../header-checker.md) for theses parameters. + +### Content encryption + +| Algorithm | Namespace | +| ------------------------------------------------------------ | --------------------------------------------------------------------- | +|A128GCM
A192GCM
A256GCM| `Jose\Component\Encryption\Algorithm\ContentEncryption` | +|A128CBC-HS256
A192CBC-HS384
A256CBC-HS512| `Jose\Component\Encryption\Algorithm\ContentEncryption` | + +!!! danger + + The algorithm `RSA1_5` is deprecated due to known [security vulnerability](https://en.wikipedia.org/wiki/Adaptive_chosen-ciphertext_attack). + + The algorithms `ECDH-ES*` are not recommended unless used with the `OKP` key type. + +### Curves supported by the key agreement algorithms + +The `ECDH-ES*` and `ECDH-SS*` algorithms accept: + +* `EC` keys using the `P-256`, `P-384`, `P-521`, `secp256k1`, `BP-256`, `BP-384` or `BP-512` curve, +* `OKP` keys using the `X25519` curve (SODIUM extension is required). + +The `BP-*` curves are the Brainpool curves. They are part of the core library and do not require the experimental package. Please read the [warning about their interoperability](../key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) before using them. + +## Experimental algorithms + +The following algorithms are experimental and must not be used in production unless you know what you are doing. **They are proposed for testing purpose only.** + +### Key encryption + +| Algorithm | Description | +|---|---| +| A128CTR
A192CTR
A256CTR | AES CTR based encryption | +| Chacha20+Poly1305 | *Please note that this algorithm requires OpenSSL 1.1* | +| RSA-OAEP-384
RSA-OAEP-512 | Same algorithm as RSA-OAEP-256 but with SHA-384 and SHA-512 hashing functions | + +### Content encryption + +| Algorithm | Description | +|---|---| +| A128CCM-16-128
A128CCM-16-64
A128CCM-64-128
A128CCM-64-64
A256CCM-16-128
A256CCM-16-64
A256CCM-64-128
A256CCM-64-64 | AES-CCM based algorithms | + +## How to use + +These algorithms have to be used with the [Algorithm Manager](../algorithm-management-jwa.md). + +```php +build(); // We build it ``` -## Static Key Agreement (ECDH-SS) +## Static key agreement (ECDH-SS) The `ECDH-SS*` algorithms derive the agreement key from two **static** keys: no ephemeral key pair is generated, so no `epk` header parameter is added to the token and the recipient has to know the public key of the sender. @@ -76,11 +76,11 @@ $jwe = $jweBuilder The sender key does not add a recipient, so it may be set before or after `addRecipient()`. It is checked by `build()`, once the recipients and the content encryption algorithm are known. A direct static key agreement without a sender key fails with `The sender key shall be set`. -{% hint style="info" %} -The decryption side needs both keys too, with the roles swapped. See [JWE Loading](jwe-loading.md#static-key-agreement). -{% endhint %} +!!! info + + The decryption side needs both keys too, with the roles swapped. See [JWE Loading](jwe-loading.md#static-key-agreement). -## Header Parameters +## Header parameters [RFC 7516 section 7.2.1](https://datatracker.ietf.org/doc/html/rfc7516#section-7.2.1) requires the header parameter names of the shared protected header, the shared unprotected header and the per-recipient headers to be **disjoint**. The builder rejects a token that would repeat a parameter across two of those locations. diff --git a/the-components/encrypted-tokens-jwe/jwe-loading.md b/docs/the-components/encrypted-tokens-jwe/jwe-loading.md similarity index 87% rename from the-components/encrypted-tokens-jwe/jwe-loading.md rename to docs/the-components/encrypted-tokens-jwe/jwe-loading.md index b6dde79..f3bdb1d 100644 --- a/the-components/encrypted-tokens-jwe/jwe-loading.md +++ b/docs/the-components/encrypted-tokens-jwe/jwe-loading.md @@ -1,4 +1,4 @@ -# JWE Loading +# JWE loading Encrypted tokens are loaded by a serializer or the serializer manager and decrypted by the `JWEDecrypter` object. This JWEDecrypter object requires an algorithm manager with both key encryption and content encryption algorithms. @@ -24,9 +24,9 @@ $jweDecrypter = new JWEDecrypter($algorithmManager); Now we can try to deserialize and decrypt the input we receive. We will continue with the result we got during the JWE creation section. -{% hint style="danger" %} -We do not check header parameters here, but it is very important to do it. This step is described in the [Header Checker](../header-checker.md) section. -{% endhint %} +!!! danger + + We do not check header parameters here, but it is very important to do it. This step is described in the [Header Checker](../header-checker.md) section. ```php decryptUsingKey($jwe, $jwk, 0); OK so if no exception is thrown, then your token is loaded and the payload correctly decrypted. -## JWELoader Object +## JWELoader object To avoid duplication of code lines, you can create a `JWELoader` object. This object contains a serializer, a decrypter and an optional header checker (highly recommended). @@ -82,7 +82,7 @@ $jwe = $jweLoader->loadAndDecryptWithKey($token, $key, $recipient); In case you use a key set, you can use the method `loadAndDecryptWithKeySet`. -## JWELoaderFactory Object +## JWELoaderFactory object The `JWELoaderFactory` object is able to create `JWELoader` objects on demand. It requires the following factories: @@ -108,7 +108,7 @@ $jweLoader = $jweLoaderFactory->create( ); ``` -## Header Validation +## Header validation [RFC 7516 section 7.2.1](https://datatracker.ietf.org/doc/html/rfc7516#section-7.2.1) requires the header parameter names of the three locations — shared protected header, shared unprotected header and per-recipient header — to be **disjoint**. The `JWEDecrypter` enforces it and rejects a token that repeats a parameter across two locations. @@ -117,13 +117,13 @@ Two consequences are worth knowing: * the protected header always wins. An unprotected parameter can never override an integrity protected one, `alg` and `enc` included; * `alg` and `enc` are never read from the **shared unprotected** header. That header is not covered by the AAD and, unlike the per-recipient header, nothing requires those parameters to be located there. Put them in the shared protected header. -{% hint style="warning" %} -This is enforced since 4.2. Tokens built like the [RFC 7520](https://datatracker.ietf.org/doc/html/rfc7520) sections 5.11 and 5.12 vectors are still parsed, but are no longer decrypted. See the [migration guide](../../migration/from-v4.1-to-v4.2.md#stricter-header-validation-on-decryption). -{% endhint %} +!!! warning + + This is enforced since 4.2. Tokens built like the [RFC 7520](https://datatracker.ietf.org/doc/html/rfc7520) sections 5.11 and 5.12 vectors are still parsed, but are no longer decrypted. See the [migration guide](../../migration/from-v4.1-to-v4.2.md#stricter-header-validation-on-decryption). This validation is not a substitute for the [Header Checker](../header-checker.md): it only checks where the parameters are, not what they contain. -## Static Key Agreement +## Static key agreement The `ECDH-SS*` algorithms derive the agreement key from two **static** keys, so no `epk` header parameter is present in the token and the decrypter must be given both keys. @@ -138,7 +138,7 @@ $success = $jweDecrypter->decryptUsingKey( ); ``` -## Understanding Failures +## Understanding failures When a token cannot be loaded or decrypted, the loaders and the serializer manager chain the last error met along the way as the previous exception: @@ -173,7 +173,7 @@ $success = $jweDecrypter->decryptUsingKeySet( ); ``` -{% hint style="info" %} -That callable is not declared in the signature of `decryptUsingKeySet()` yet — it is read with `func_num_args()`/`func_get_arg(5)` so that classes extending the decrypter keep working. It will become part of the signature in 5.0. -{% endhint %} +!!! info + + That callable is not declared in the signature of `decryptUsingKeySet()` yet — it is read with `func_num_args()`/`func_get_arg(5)` so that classes extending the decrypter keep working. It will become part of the signature in 5.0. diff --git a/the-components/examples/README.md b/docs/the-components/examples/README.md similarity index 71% rename from the-components/examples/README.md rename to docs/the-components/examples/README.md index 9b29b1b..fd30cf2 100644 --- a/the-components/examples/README.md +++ b/docs/the-components/examples/README.md @@ -6,11 +6,11 @@ This section provides practical, copy-paste ready examples for common JWT operat composer require web-token/jwt-library ``` -{% hint style="info" %} -These examples use the **standalone PHP API** (no framework). If you use Symfony, refer to the [Symfony Bundle](../../the-symfony-bundle/symfony-bundle.md) section for a more integrated approach. -{% endhint %} +!!! info -## Available Examples + These examples use the **standalone PHP API** (no framework). If you use Symfony, refer to the [Symfony Bundle](../../the-symfony-bundle/symfony-bundle.md) section for a more integrated approach. + +## Available examples * [Signed Tokens (JWS)](signed-tokens.md) — Create and verify signed JWTs using HMAC, RSA, and Elliptic Curve algorithms. * [Encrypted Tokens (JWE)](encrypted-tokens.md) — Encrypt and decrypt JWTs using RSA-OAEP, ECDH-ES, and password-based encryption. diff --git a/the-components/examples/encrypted-tokens.md b/docs/the-components/examples/encrypted-tokens.md similarity index 91% rename from the-components/examples/encrypted-tokens.md rename to docs/the-components/examples/encrypted-tokens.md index 10525b8..0c5c9e2 100644 --- a/the-components/examples/encrypted-tokens.md +++ b/docs/the-components/examples/encrypted-tokens.md @@ -1,6 +1,6 @@ -# Encrypted Tokens (JWE) Examples +# Encrypted tokens (JWE) examples -## Encrypt a Token with RSA-OAEP +## Encrypt a token with RSA-OAEP RSA-OAEP is widely supported. The sender encrypts with the recipient's public key; only the recipient can decrypt with their private key. @@ -47,7 +47,7 @@ $jwe = $jweBuilder $token = (new CompactSerializer())->serialize($jwe, 0); ``` -### Decrypt the Token +### Decrypt the token ```php getPayload(), true); --- -## Encrypt a Token with ECDH-ES (Elliptic Curve) +## Encrypt a token with ECDH-ES (Elliptic curve) ECDH-ES performs a key agreement directly, producing smaller tokens than RSA. @@ -116,7 +116,7 @@ $jwe = $jweBuilder $token = (new CompactSerializer())->serialize($jwe, 0); ``` -### Decrypt the Token +### Decrypt the token ```php getPayload(), true); --- -## Encrypt a Token with a Password (PBES2) +## Encrypt a token with a password (PBES2) Password-based encryption is useful when sharing tokens with users who only have a passphrase. -{% hint style="warning" %} -`spomky-labs/aes-key-wrap` is required for PBES2 algorithms: -```bash -composer require spomky-labs/aes-key-wrap -``` -{% endhint %} +!!! warning + + `spomky-labs/aes-key-wrap` is required for PBES2 algorithms: + ```bash + composer require spomky-labs/aes-key-wrap + ``` ```php serialize($jwe, 0); ``` -### Decrypt with the Same Password +### Decrypt with the same password ```php getPayload(), true); --- -## Encrypt a Token with a Shared Key (A256KW) +## Encrypt a token with a shared key (A256KW) AES Key Wrap uses a symmetric key, useful for server-to-server communication. -{% hint style="warning" %} -`spomky-labs/aes-key-wrap` is required: -```bash -composer require spomky-labs/aes-key-wrap -``` -{% endhint %} +!!! warning + + `spomky-labs/aes-key-wrap` is required: + ```bash + composer require spomky-labs/aes-key-wrap + ``` ```php loadAndDecryptWithKey($token, $privateKey, $recipient); $payload = json_decode($jwe->getPayload(), true); ``` -{% hint style="info" %} -After decryption, you should also validate the claims (`exp`, `iss`, `aud`, etc.) contained in the payload. See the [Claim and Header Validation](validation.md) page. -{% endhint %} +!!! info + + After decryption, you should also validate the claims (`exp`, `iss`, `aud`, etc.) contained in the payload. See the [Claim and Header Validation](validation.md) page. diff --git a/the-components/examples/signed-tokens.md b/docs/the-components/examples/signed-tokens.md similarity index 93% rename from the-components/examples/signed-tokens.md rename to docs/the-components/examples/signed-tokens.md index 5ab2a5a..570cabf 100644 --- a/the-components/examples/signed-tokens.md +++ b/docs/the-components/examples/signed-tokens.md @@ -1,6 +1,6 @@ -# Signed Tokens (JWS) Examples +# Signed tokens (JWS) examples -## Sign a Token with HMAC (HS256) +## Sign a token with HMAC (HS256) The simplest approach: a shared secret key is used to both sign and verify the token. @@ -42,7 +42,7 @@ $token = (new CompactSerializer())->serialize($jws); // eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL215LWFwcC... ``` -### Verify the Token +### Verify the token ```php verifyWithKey($jws, $jwk, 0); --- -## Sign a Token with RSA (RS256) +## Sign a token with RSA (RS256) RSA keys allow the signer and the verifier to use different keys: a private key to sign, a public key to verify. @@ -106,7 +106,7 @@ $jws = $jwsBuilder $token = (new CompactSerializer())->serialize($jws); ``` -### Verify with the Public Key +### Verify with the public key ```php verifyWithKey($jws, $publicKey, 0); --- -## Sign a Token with Elliptic Curve (ES256) +## Sign a token with elliptic curve (ES256) ECDSA keys are smaller and faster than RSA for the same security level. @@ -169,7 +169,7 @@ $jws = $jwsBuilder $token = (new CompactSerializer())->serialize($jws); ``` -### Verify with the Public Key +### Verify with the public key ```php verifyWithKey($jws, $publicKey, 0); --- -## Sign a Token with EdDSA (Ed25519) +## Sign a token with EdDSA (Ed25519) EdDSA with Ed25519 provides high performance and strong security. Requires the `sodium` extension. @@ -277,6 +277,6 @@ $jws = $jwsLoader->loadAndVerifyWithKey($token, $jwk, $signature); $payload = json_decode($jws->getPayload(), true); ``` -{% hint style="info" %} -After verifying the signature, you should also validate the claims (`exp`, `iss`, `aud`, etc.). See the [Claim and Header Validation](validation.md) page. -{% endhint %} +!!! info + + After verifying the signature, you should also validate the claims (`exp`, `iss`, `aud`, etc.). See the [Claim and Header Validation](validation.md) page. diff --git a/the-components/examples/validation.md b/docs/the-components/examples/validation.md similarity index 90% rename from the-components/examples/validation.md rename to docs/the-components/examples/validation.md index 204fff1..3c23dca 100644 --- a/the-components/examples/validation.md +++ b/docs/the-components/examples/validation.md @@ -1,12 +1,12 @@ -# Claim and Header Validation +# Claim and header validation After verifying a signature or decrypting a token, you **must** validate the claims and headers before trusting the token content. -{% hint style="danger" %} -Never use a token payload without validating its claims. An expired or wrongly-issued token must be rejected. -{% endhint %} +!!! danger -## Validate Claims + Never use a token payload without validating its claims. An expired or wrongly-issued token must be rejected. + +## Validate claims The `ClaimCheckerManager` verifies claims contained in the token payload. @@ -42,16 +42,16 @@ $claimCheckerManager->check($payload, ['iss', 'aud', 'exp']); // Throws an exception if any check fails ``` -{% hint style="info" %} -The `NativeClock` class is from the `symfony/clock` component. You can use any `Psr\Clock\ClockInterface` implementation. -```bash -composer require symfony/clock -``` -{% endhint %} +!!! info + + The `NativeClock` class is from the `symfony/clock` component. You can use any `Psr\Clock\ClockInterface` implementation. + ```bash + composer require symfony/clock + ``` --- -## Allow a Time Drift +## Allow a time drift Network latency or clock skew between servers may cause valid tokens to be rejected. You can allow a small time drift (in seconds): @@ -65,7 +65,7 @@ $claimCheckerManager = new ClaimCheckerManager([ --- -## Custom Claim Validation with CallableChecker +## Custom claim validation with CallableChecker For application-specific claims, use the `CallableChecker`: @@ -113,7 +113,7 @@ $claimCheckerManager->check($payload, ['type']); --- -## Complete Example: Sign, Verify, and Validate +## Complete example: Sign, verify, and validate A full example combining token creation, verification, and claim validation: diff --git a/the-components/header-checker.md b/docs/the-components/header-checker.md similarity index 63% rename from the-components/header-checker.md rename to docs/the-components/header-checker.md index 8e0e62d..387d338 100644 --- a/the-components/header-checker.md +++ b/docs/the-components/header-checker.md @@ -1,26 +1,26 @@ -# Header Checker +# Header checker When you receive a JWT (JWS or JWE), it is important to check **ALL** header parameters **BEFORE** any other action. In case something goes wrong, the token should be rejected. -{% hint style="warning" %} -This is a strong recommendation as there are known vulnerabilities on tokens that are processed without header verification. -{% endhint %} +!!! warning -{% hint style="info" %} -Please note that some algorithms may use additional header parameters. Please read carefully the details on the [signature](signed-tokens-jws/signature-algorithms.md) or [encryption](encrypted-tokens-jwe/encryption-algorithms.md) algorithms page. -{% endhint %} + This is a strong recommendation as there are known vulnerabilities on tokens that are processed without header verification. + +!!! info + + Please note that some algorithms may use additional header parameters. Please read carefully the details on the [signature](signed-tokens-jws/signature-algorithms.md) or [encryption](encrypted-tokens-jwe/encryption-algorithms.md) algorithms page. The header parameters are checked by a Header Checker Manager. This manager can contain several header checkers. -{% hint style="success" %} -The header parameter `crit` (critical) is always checked. -{% endhint %} +!!! success + + The header parameter `crit` (critical) is always checked. -{% hint style="info" %} -Even if the cryptographic process will check the `alg`/`enc` header parameters, it is useful to check them beforehand to reject invalid tokens earlier. -{% endhint %} +!!! info -## Header Checker Manager + Even if the cryptographic process will check the `alg`/`enc` header parameters, it is useful to check them beforehand to reject invalid tokens earlier. + +## Header checker manager To create a header checker manager, you will need to add header checkers and at least one token type. @@ -62,17 +62,27 @@ In the following example, an exception will be thrown if the `alg`, `enc` or `cr $headerCheckerManager->check($jwt, 0, ['alg', 'enc', 'crit']); ``` -## Provided Header Checker Objects +## Provided header checker objects The library provides several header checker classes you can instantiate and use at will. They are all located in the namespace `Jose\Component\Checker`. -{% hint style="info" %} -For time-based parameter checker classes, a PSR-20 clock object is mandatory. -{% endhint %} +!!! info + + For time-based parameter checker classes, a PSR-20 clock object is mandatory. -
ClassHeaderTime-based
AlgorithmCheckeralgNo
AudienceCheckeraud
This is a replicated claim as per the RFC7516 section 5.3
No
ExpirationTimeCheckerexpYes
IssuedAtCheckeriatYes
IssuerCheckerissNo
NotBeforeCheckernbfYes
UnencodedPayloadCheckerb64
See unencoded payload for more information.
No
CallableCheckerGeneric object that can execute a callable for checking a particular parameterNo
IsEqualCheckerGeneric object that can compare a particular parameter to a predefined valueNo
+| Class | Header | Time-based | +|---|---|---| +| AlgorithmChecker | `alg` | No | +| AudienceChecker | `aud`
This is a replicated claim as per the [RFC7516 section 5.3](https://tools.ietf.org/html/rfc7519#section-5.3) | No | +| ExpirationTimeChecker | `exp` | **Yes** | +| IssuedAtChecker | `iat` | **Yes** | +| IssuerChecker | `iss` | No | +| NotBeforeChecker | `nbf` | **Yes** | +| UnencodedPayloadChecker | `b64`
See [unencoded payload](../advanced-topics/signed-tokens/unencoded-payload.md) for more information. | No | +| CallableChecker | Generic object that can execute a callable for checking a particular parameter | No | +| IsEqualChecker | Generic object that can compare a particular parameter to a predefined value | No | -## Header Checker Manager Factory +## Header checker manager factory The Header Checker Manager Factory will help you create as many Header Checker Managers as you need to fit your application requirements. @@ -94,14 +104,13 @@ $headerCheckerManagerForSignatures = $headerCheckerManagerFactory->create(['sign $headerCheckerManagerForEncryption = $headerCheckerManagerFactory->create(['key_encryption_alg']); ``` -## Custom Header Checker +## Custom header checker With the previous examples, we will only check the `alg` (algorithm) header parameter. But your application may use other header parameters e.g. `cty`, `typ`... If you need, you can create your own header checker. It must implement the interface `Jose\Component\Checker\HeaderChecker`. In the following example, we will check that the protected header parameter `custom` is an array with value `foo` or `bar`. -{% code title="Acme\Checker\CustomChecker.php" %} -```php +```php title="Acme\Checker\CustomChecker.php" toPublic(); json_encode($jwk); ``` -## Generate A New Key +## Generate a new key This framework is able to create private and public keys on the fly using the `JWKFactory`. 4 types of keys are supported: @@ -43,11 +43,11 @@ This framework is able to create private and public keys on the fly using the `J * `EC` : Elliptic Curve key pair * `OKP`: Octet key pair -{% hint style="info" %} -The `none` algorithm needs a key of type `none`. This is a specific key type that must only be used with this algorithm. -{% endhint %} +!!! info + + The `none` algorithm needs a key of type `none`. This is a specific key type that must only be used with this algorithm. -### Octet String +### Octet string The following example will show you how to create an `oct` key. @@ -83,7 +83,7 @@ $jwk = JWKFactory::createFromSecret( ); ``` -### RSA Key Pair +### RSA key pair The following example will show you how to create a `RSA` key. @@ -102,7 +102,7 @@ $private_key = JWKFactory::createRSAKey( ]); ``` -### Elliptic Curve Key Pair +### Elliptic curve key pair The following example will show you how to create a `EC` key. @@ -123,11 +123,11 @@ The supported curves are: The Brainpool curves can be used for ECDSA signatures — through the `BP256R1`, `BP384R1` and `BP512R1` [experimental algorithms](../signed-tokens-jws/signature-algorithms.md#experimental-algorithms) — for `ECDH-ES`/`ECDH-SS` key agreement, and for the PEM/JWK conversions in both directions. -{% hint style="warning" %} -The Brainpool curves are **not registered with IANA**. The identifiers `BP-256`, `BP-384` and `BP-512` follow the convention already adopted by the other implementations, so keys and tokens using them are only interoperable with the implementations sharing that convention. -{% endhint %} +!!! warning -#### Octet Key Pair + The Brainpool curves are **not registered with IANA**. The identifiers `BP-256`, `BP-384` and `BP-512` follow the convention already adopted by the other implementations, so keys and tokens using them are only interoperable with the implementations sharing that convention. + +#### Octet key pair The following example will show you how to create a `OKP` key. @@ -144,7 +144,7 @@ The supported curves are: * `Ed25519` for signature/verification only * `X25519` for encryption/decryption only -### None Key +### None key The `none` key type is a special type used only for the `none` algorithm. @@ -156,9 +156,9 @@ use Jose\Component\KeyManagement\JWKFactory; $key = JWKFactory::createNoneKey(); ``` -## Create Key From External Sources +## Create key from external sources -### From Values +### From values In case you already have key values, you can create a key by passing those values as an argument: @@ -177,7 +177,7 @@ $key = JWKFactory::createFromValues([ ]); ``` -### From A Key File +### From a key file You can convert a PKCS#1 or PKCS#8 key file into a JWK. The following method supports PEM and DER formats. Encrypted keys are also supported. @@ -195,7 +195,7 @@ $key = JWKFactory::createFromKeyFile( ); ``` -### From A PKCS#12 Certificate +### From a PKCS#12 certificate You can convert a PKCS#12 Certificate into a JWK. Encrypted certificates are also supported. @@ -213,7 +213,7 @@ $key = JWKFactory::createFromPKCS12CertificateFile( ); ``` -### From A X.509 Certificate +### From a X.509 certificate You can convert a X.509 Certificate into a JWK. @@ -230,6 +230,6 @@ $key = JWKFactory::createFromCertificateFile( ); ``` -{% hint style="info" %} -Please note that X.509 certificates only contain public keys. -{% endhint %} +!!! info + + Please note that X.509 certificates only contain public keys. diff --git a/the-components/key-jwk-and-key-set-jwkset/key-set-management.md b/docs/the-components/key-jwk-and-key-set-jwkset/key-set-management.md similarity index 77% rename from the-components/key-jwk-and-key-set-jwkset/key-set-management.md rename to docs/the-components/key-jwk-and-key-set-jwkset/key-set-management.md index 133df9b..5ad83dd 100644 --- a/the-components/key-jwk-and-key-set-jwkset/key-set-management.md +++ b/docs/the-components/key-jwk-and-key-set-jwkset/key-set-management.md @@ -1,4 +1,4 @@ -# Key Set (JWKSet) +# Key set (JWKSet) You can create a JWKSet object using three static methods: @@ -8,9 +8,9 @@ You can create a JWKSet object using three static methods: Below are all methods available for a JWKSet object. The variable `$jwkset` is a valid JWKSet object. -{% hint style="warning" %} -Please note a JWKSet object is an immutable object. When you add keys, you get a new JWKSet object. -{% endhint %} +!!! warning + + Please note a JWKSet object is an immutable object. When you add keys, you get a new JWKSet object. ```php $jwk) { json_encode($jwkset); ``` -## Duplicate Key IDs +## Duplicate key IDs [RFC 7517 section 4.5](https://datatracker.ietf.org/doc/html/rfc7517#section-4.5) explicitly allows a key set to hold several keys sharing the same `kid`, typically an RSA key and an EC key that the application considers as equivalent alternatives. @@ -70,10 +70,10 @@ $jwkset->get('key-1'); // The first key registered with that ID * `get()` and `has()` look the key ID up and return the first key carrying it. * `without('key-1')` removes **every** key carrying that ID. -{% hint style="info" %} -When several keys share a `kid`, prefer `selectKey()` over `get()`: it also takes the key usage and the algorithm into account, which is precisely what tells those keys apart. -{% endhint %} +!!! info + + When several keys share a `kid`, prefer `selectKey()` over `get()`: it also takes the key usage and the algorithm into account, which is precisely what tells those keys apart. + +!!! warning -{% hint style="warning" %} -Before 4.2, the keys were indexed by `kid` and the last key registered with a given ID silently replaced the previous one. See the [migration guide](../../migration/from-v4.1-to-v4.2.md#duplicate-key-ids-in-a-key-set). -{% endhint %} + Before 4.2, the keys were indexed by `kid` and the last key registered with a given ID silently replaced the previous one. See the [migration guide](../../migration/from-v4.1-to-v4.2.md#duplicate-key-ids-in-a-key-set). diff --git a/the-components/signed-tokens-jws/README.md b/docs/the-components/signed-tokens-jws/README.md similarity index 93% rename from the-components/signed-tokens-jws/README.md rename to docs/the-components/signed-tokens-jws/README.md index 1dc0336..117c85c 100644 --- a/the-components/signed-tokens-jws/README.md +++ b/docs/the-components/signed-tokens-jws/README.md @@ -1,4 +1,4 @@ -# Signed Tokens (JWS) +# Signed tokens (JWS) A very large number of signature algorithms is provided by the library. Please refer to [this signature algorithm table](signature-algorithms.md) to know what algorithms are available. diff --git a/the-components/signed-tokens-jws/jws-creation.md b/docs/the-components/signed-tokens-jws/jws-creation.md similarity index 85% rename from the-components/signed-tokens-jws/jws-creation.md rename to docs/the-components/signed-tokens-jws/jws-creation.md index 37e5d9a..3765eb9 100644 --- a/the-components/signed-tokens-jws/jws-creation.md +++ b/docs/the-components/signed-tokens-jws/jws-creation.md @@ -1,4 +1,4 @@ -# JWS Creation +# JWS creation Now that you have an algorithm manager and a key, it is time to create your first signed token. @@ -46,15 +46,15 @@ $jws = $jwsBuilder ->build(); // We build it ``` -{% hint style="info" %} -The `addSignature()` method accepts a third optional parameter for unprotected headers: -```php -->addSignature($jwk, ['alg' => 'HS256'], ['kid' => 'key-2023']) -``` -where the second parameter is the protected header and the third is the unprotected header. -{% endhint %} +!!! info -## Already Encoded Payloads + The `addSignature()` method accepts a third optional parameter for unprotected headers: + ```php + ->addSignature($jwk, ['alg' => 'HS256'], ['kid' => 'key-2023']) + ``` + where the second parameter is the protected header and the third is the unprotected header. + +## Already encoded payloads Sometimes the payload you hold is already Base64Url encoded. GNAP request signing is such a case: the payload is the Base64Url encoded SHA-256 hash of the request body. @@ -72,9 +72,9 @@ The value is validated as strictly as the `CompactSerializer` validates a token Like `withPayload()`, the method accepts a second argument to mark the payload as [detached](../../advanced-topics/signed-tokens/detached-payload.md). -{% hint style="warning" %} -An encoded payload contradicts the [unencoded payload option](../../advanced-topics/signed-tokens/unencoded-payload.md) (`b64` set to `false`), which removes the encoding altogether. The combination is rejected whichever order the two are set in. -{% endhint %} +!!! warning + + An encoded payload contradicts the [unencoded payload option](../../advanced-topics/signed-tokens/unencoded-payload.md) (`b64` set to `false`), which removes the encoding altogether. The combination is rejected whichever order the two are set in. ## Serialization diff --git a/the-components/signed-tokens-jws/jws-loading.md b/docs/the-components/signed-tokens-jws/jws-loading.md similarity index 89% rename from the-components/signed-tokens-jws/jws-loading.md rename to docs/the-components/signed-tokens-jws/jws-loading.md index 6210281..02c660b 100644 --- a/the-components/signed-tokens-jws/jws-loading.md +++ b/docs/the-components/signed-tokens-jws/jws-loading.md @@ -1,8 +1,8 @@ -# JWS Loading +# JWS loading Signed tokens are loaded by a serializer or the serializer manager and verified by the `JWSVerifier` object. This JWSVerifier object just requires an algorithm manager. -## Serializer And Verifier +## Serializer and verifier In the following example, we will try to load a signed token. We will only use the `HS256` algorithm. @@ -26,9 +26,9 @@ $jwsVerifier = new JWSVerifier( Now we can deserialize the input we receive and check the signature using our key. We will continue with the data we got in the JWS creation section. -{% hint style="danger" %} -We do not check header parameters here, but it is very important to do it. This step is described in the [Header Checker section](../header-checker.md). -{% endhint %} +!!! danger + + We do not check header parameters here, but it is very important to do it. This step is described in the [Header Checker section](../header-checker.md). ```php verifyWithKey($jws, $jwk, 0); The method `verifyWithKey` returns a boolean. If true, then your token signature is valid. You can then check the claims (if any) using the claim checker manager. -## JWSLoader Object +## JWSLoader object To avoid duplication of code lines, you can create a `JWSLoader` object. This object contains a serializer, a verifier and an optional header checker (highly recommended). @@ -88,7 +88,7 @@ $jws = $jwsLoader->loadAndVerifyWithKey($token, $jwk, $signature, $payload); In case you use a key set, you can use the method `loadAndVerifyWithKeySet`. -## JWSLoaderFactory Object +## JWSLoaderFactory object The `JWSLoaderFactory` object is able to create `JWSLoader` objects on demand. It requires the following factories: @@ -114,7 +114,7 @@ $jwsLoader = $jwsLoaderFactory->create( ); ``` -## Understanding Failures +## Understanding failures When a token cannot be loaded or verified, the loaders and the serializer manager chain the last error met along the way as the previous exception. The message of the outer exception stays generic on purpose — it is the one you may safely show — while the cause tells you what actually happened: @@ -149,7 +149,7 @@ $isVerified = $jwsVerifier->verifyWithKeySet( ); ``` -{% hint style="info" %} -That callable is not declared in the signature of `verifyWithKeySet()` yet — it is read with `func_num_args()`/`func_get_arg(5)` so that classes extending the verifier keep working. It will become part of the signature in 5.0. -{% endhint %} +!!! info + + That callable is not declared in the signature of `verifyWithKeySet()` yet — it is read with `func_num_args()`/`func_get_arg(5)` so that classes extending the verifier keep working. It will become part of the signature in 5.0. diff --git a/docs/the-components/signed-tokens-jws/signature-algorithms.md b/docs/the-components/signed-tokens-jws/signature-algorithms.md new file mode 100644 index 0000000..f873b84 --- /dev/null +++ b/docs/the-components/signed-tokens-jws/signature-algorithms.md @@ -0,0 +1,56 @@ +# Signature algorithms + +This framework comes with several signature algorithms. These algorithms are in the following namespace: `Jose\Component\Signature\Algorithm`. + +| Algorithm | Description | +|---|---| +| HS256
HS384
HS512 | HMAC with SHA-2 Functions | +| ES256
ES384
ES512 | Elliptic Curve Digital Signature Algorithm (ECDSA) | +| RS256
RS384
RS512 | RSASSA-PKCS1 v1_5 | +| PS256
PS384
PS512 | RSASSA-PSS | +| EdDSA (*only with the* Ed25519 *curve*) | Edwards-curve Digital Signature Algorithm (EdDSA) | +| none | **Not a secure algorithm. Please use with caution** | + +### Experimental algorithms + +The following signature algorithms are experimental and must not be used in production unless you know what you are doing. **They are proposed for testing purpose only.** + +They are provided through the package `web-token/jwt-experimental`. + +| Algorithm | Description | +| --------- | ------------------------------------------------------------------------------ | +| RS1 | RSASSA-PKCS1 v1\_5 with SHA-1 hashing function | +| HS1 | HMAC with SHA-1 hashing function | +| HS256/64 | HMAC with SHA-256 truncated to 64 bits | +| ES256K | Elliptic curve secp256k1 support | +| BP256R1 | ECDSA using the brainpoolP256r1 curve (`BP-256`) and SHA-256 | +| BP384R1 | ECDSA using the brainpoolP384r1 curve (`BP-384`) and SHA-384 | +| BP512R1 | ECDSA using the brainpoolP512r1 curve (`BP-512`) and SHA-512 | +| Blake2b | Blake2b MAC algorithm. Sodium extension required | + +!!! warning + + The Brainpool algorithms and their curves are **not registered with IANA**. The identifiers `BP256R1`/`BP384R1`/`BP512R1` and `BP-256`/`BP-384`/`BP-512` follow the convention already adopted by the other implementations, so tokens using them are only interoperable with the implementations sharing that convention. + + The curves themselves are part of the core library: [creating a Brainpool key](../key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) or using one with `ECDH-ES`/`ECDH-SS` does not require the experimental package. + +## How to use + +These algorithms have to be used with the [Algorithm Manager](../algorithm-management-jwa.md). They do not need any arguments. + +Example: + +```php +get(AlgorithmManagerFactory::class); $algorithmManager = $algorithmManagerFactory->create(['RS256', 'HS512']); ``` -## Custom Algorithm +## Custom algorithm This factory handles all algorithms services tagged with `jose.algorithm`. @@ -28,7 +28,7 @@ services: Your algorithm will be available through the algorithm manager factory service and the alias `FOO`. -## PBES2-\* Algorithms +## PBES2-\* algorithms When installed, the `PBES2-*` algorithms are available through the algorithm manager factory. They have the default configuration i.e. salt size = 62 bits and count = 4096. If these values does not fit your needs, you can create a new algorithm service with your own values: diff --git a/the-symfony-bundle/configuration-helper.md b/docs/the-symfony-bundle/configuration-helper.md similarity index 99% rename from the-symfony-bundle/configuration-helper.md rename to docs/the-symfony-bundle/configuration-helper.md index b6c3d96..25b21b3 100644 --- a/the-symfony-bundle/configuration-helper.md +++ b/docs/the-symfony-bundle/configuration-helper.md @@ -1,4 +1,4 @@ -# Configuration Helper +# Configuration helper When you want to create keys/key sets, JWS loader/verifier... services, you have to create a dedicated `jose` section in your configuration. It may confuse your users to configure your bundle and the Jose Framework bundle. Sometimes, you may also want to be sure that the configuration is correctly defined. Lastly, the configuration size increases with numerous details, options or service IDs and it becomes difficult to read or modify. diff --git a/the-symfony-bundle/encrypted-tokens/README.md b/docs/the-symfony-bundle/encrypted-tokens/README.md similarity index 95% rename from the-symfony-bundle/encrypted-tokens/README.md rename to docs/the-symfony-bundle/encrypted-tokens/README.md index 25b860d..bb295ce 100644 --- a/the-symfony-bundle/encrypted-tokens/README.md +++ b/docs/the-symfony-bundle/encrypted-tokens/README.md @@ -1,4 +1,4 @@ -# Encrypted Tokens +# Encrypted tokens Encryption algorithms are automatically handled by the Algorithm Manager Factory. diff --git a/the-symfony-bundle/encrypted-tokens/jwe-creation.md b/docs/the-symfony-bundle/encrypted-tokens/jwe-creation.md similarity index 94% rename from the-symfony-bundle/encrypted-tokens/jwe-creation.md rename to docs/the-symfony-bundle/encrypted-tokens/jwe-creation.md index 123bef5..4620d6d 100644 --- a/the-symfony-bundle/encrypted-tokens/jwe-creation.md +++ b/docs/the-symfony-bundle/encrypted-tokens/jwe-creation.md @@ -1,6 +1,6 @@ # JWE creation -## JWE Builder Factory Service +## JWE builder factory service A `JWEBuilderFactory` is available as a service in your application container: @@ -19,7 +19,7 @@ $jweBuilder = $jweBuilderFactory->create(['A256GCMKW', 'A256CBC-HS256']); You can now use the JWEBuilder as explained in the JWE Creation section. -## JWE Builder As Service +## JWE builder as service There is also another way to create a JWEBuilder object: using the bundle configuration. @@ -39,7 +39,7 @@ With the previous configuration, the bundle will create a public JWE Builder ser $jweBuilder = $container->get('jose.jwe_builder.builder1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/encrypted-tokens/jwe-decryption.md b/docs/the-symfony-bundle/encrypted-tokens/jwe-decryption.md similarity index 90% rename from the-symfony-bundle/encrypted-tokens/jwe-decryption.md rename to docs/the-symfony-bundle/encrypted-tokens/jwe-decryption.md index 76bc870..f897b0c 100644 --- a/the-symfony-bundle/encrypted-tokens/jwe-decryption.md +++ b/docs/the-symfony-bundle/encrypted-tokens/jwe-decryption.md @@ -1,6 +1,6 @@ # JWE decryption -## JWE Decrypter Factory Service +## JWE decrypter factory service A `JWEDecrypterFactory` is available as a service in your application container: @@ -19,11 +19,11 @@ $jweDecrypter = $jweDecrypterFactory->create(['HS256']); You can now use the JWEDecrypter as explained in the JWE Creation section. -{% hint style="info" %} -Reminder: it is important to check the token headers. See the checker section of this documentation. -{% endhint %} +!!! info -## JWE Decrypter As Service + Reminder: it is important to check the token headers. See the checker section of this documentation. + +## JWE decrypter as service There is also another way to create a JWEDecrypter object: using the bundle configuration. @@ -43,7 +43,7 @@ With the previous configuration, the bundle will create a public JWE Decrypter s $jweDecrypter = $container->get('jose.jwe_decrypter.decrypter1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. @@ -58,7 +58,7 @@ jose: tag_name2: {attribute1: 'foo'} ``` -## JWE Loader Service +## JWE loader service The [`JWELoaderFactory`](../../the-components/encrypted-tokens-jwe/jwe-loading.md) is available as a public service. You can retrieve it using the container or inject it into your services. It will help you to create `JWELoader` objects on demand. diff --git a/the-symfony-bundle/encrypted-tokens/jwe-serializers.md b/docs/the-symfony-bundle/encrypted-tokens/jwe-serializers.md similarity index 93% rename from the-symfony-bundle/encrypted-tokens/jwe-serializers.md rename to docs/the-symfony-bundle/encrypted-tokens/jwe-serializers.md index f74f4b3..d97d734 100644 --- a/the-symfony-bundle/encrypted-tokens/jwe-serializers.md +++ b/docs/the-symfony-bundle/encrypted-tokens/jwe-serializers.md @@ -1,6 +1,6 @@ # JWE serializers -## JWE Serializer Manager Factory Service +## JWE serializer manager factory service A `JWESerializerManagerFactory` is available as a service in your application container: @@ -25,7 +25,7 @@ Available JWE serialization modes are: * `jwe_json_general` * `jwe_json_flattened` -## JWE Serializer Manager As Service +## JWE serializer manager as service There is also another way to create a JWESerializerManager object: using the bundle configuration. @@ -45,7 +45,7 @@ With the previous configuration, the bundle will create a public JWE Serializer $jweSerializerManager = $container->get('jose.jwe_serializer.serializer1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/events.md b/docs/the-symfony-bundle/events.md similarity index 96% rename from the-symfony-bundle/events.md rename to docs/the-symfony-bundle/events.md index aa8bae5..2576414 100644 --- a/the-symfony-bundle/events.md +++ b/docs/the-symfony-bundle/events.md @@ -33,8 +33,7 @@ All events can be found in the class `Jose\Bundle\JoseFramework\Event\Events`. ## Example: -{% code title="App\EventSubscriber\JwsSubscriber.php" %} -```php +```php title="App\EventSubscriber\JwsSubscriber.php" get(ClaimCheckerManagerFactory::class) $claimCheckerManager = $claimCheckerManagerFactory->create([...]); ``` -## Checker Manager Services +## Checker manager services You can create Header and Claim Checker Managers using the bundle configuration. @@ -36,7 +36,7 @@ jose: With the previous configuration, the bundle will create public Header and Claim Checker Managers named `jose.header_checker.checker1` and `jose.claim_checker.checker1` with selected checkers. -## Custom Header Or Claim Checker +## Custom header or claim checker Some claim or header checkers are provided by this framework, but it is important to create custom checkers that fit your application requirements. @@ -56,7 +56,7 @@ services These checkers will be loaded by the factories and you will be able to create a header or a claim checker manager using the aliases `foo` or `bar`. -## Custom Tags +## Custom tags You can add custom tags and attributes to the header and claim checker managers. diff --git a/the-symfony-bundle/key-and-key-set-management/README.md b/docs/the-symfony-bundle/key-and-key-set-management/README.md similarity index 96% rename from the-symfony-bundle/key-and-key-set-management/README.md rename to docs/the-symfony-bundle/key-and-key-set-management/README.md index 51f9913..8c8b3ee 100644 --- a/the-symfony-bundle/key-and-key-set-management/README.md +++ b/docs/the-symfony-bundle/key-and-key-set-management/README.md @@ -1,4 +1,4 @@ -# Key and Key Set Management +# Key and key set management We recommend you to load these objects through environment variables. With Symfony, an environment variables processor is provided: diff --git a/the-symfony-bundle/key-and-key-set-management/key-management-jwk.md b/docs/the-symfony-bundle/key-and-key-set-management/key-management-jwk.md similarity index 93% rename from the-symfony-bundle/key-and-key-set-management/key-management-jwk.md rename to docs/the-symfony-bundle/key-and-key-set-management/key-management-jwk.md index 79b23f8..98f8598 100644 --- a/the-symfony-bundle/key-and-key-set-management/key-management-jwk.md +++ b/docs/the-symfony-bundle/key-and-key-set-management/key-management-jwk.md @@ -1,6 +1,6 @@ -# Key Management (JWK) +# Key management (JWK) -## Keys As Services +## Keys as services When the component is installed, you will be able to define your keys in your application configuration and load your keys from several sources or formats. All these methods have the following option: @@ -21,7 +21,7 @@ The key will be available as a container service with the ID `jose.key.key_name` As any other configuration values, you can use environment variables. -### From A Shared Secret +### From a shared secret This method will directly get a shared secret. @@ -36,7 +36,7 @@ jose: alg: 'RS512' ``` -### From A JWK Object +### From a JWK object This method will directly load a JWK object. @@ -48,7 +48,7 @@ jose: value: '{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"}' ``` -### From A X509 Certificate File +### From a X509 certificate file This method will load a X509 Certificate file. @@ -63,7 +63,7 @@ jose: alg: 'RS256' ``` -### From A X509 Certificate +### From a X509 certificate This method will load a key from a X509 Certificate. @@ -78,7 +78,7 @@ jose: alg: 'RS256' ``` -### From A PKCS#1/PKCS#8 Key File +### From a PKCS#1/PKCS#8 key file This method will load a key from a PKCS#1 or PKCS#8 key file. @@ -94,7 +94,7 @@ jose: alg: 'RS256' ``` -### From A Key In A Key Set +### From a key in a key set This method will retrieve a key from a JWKSet service. @@ -107,7 +107,7 @@ jose: index: 0 # Use key at index 0 ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md b/docs/the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md similarity index 91% rename from the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md rename to docs/the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md index 575e76b..038669b 100644 --- a/the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md +++ b/docs/the-symfony-bundle/key-and-key-set-management/key-set-management-jwkset.md @@ -1,6 +1,6 @@ -# Key Set Management (JWKSet) +# Key set management (JWKSet) -## Key Sets As Services +## Key sets as services All these methods have the following common option: @@ -21,7 +21,7 @@ The key set will be available as a container service with the ID `jose.key_set.k As any other configuration values, you can use environment variables. -### From A JWKSet Object +### From a JWKSet object This method will directly get a JWKSet object. @@ -33,7 +33,7 @@ jose: value: '{"keys":[{"kty":"oct","k":"dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g"},{"kty":"oct","k":"bwIAv5Nn-fo8p4LCEvM4IR9eLXgzJRs8jXCLb3xR0tDJGiZ46KheO4ip6htFKyN2aqJqlNi9-7hB6I1aLLy1IRT9-vcBoCSGu977cNAUuRLkRp7vo8s6MsxhB8WvQBDRZghV7jIYaune-3vbE7iDU2AESr8BUtorckLoO9uW__fIabaa3hJMMQIHCzYQbJKZvlCRCKWMk2H_zuS4JeDFTvyZH1skJYF_TET1DrCZHMPicw-Yk3_m2P-ilC-yidPPoVzeU8Jj3tQ6gtX3975qiQW7pt2qbgjKAuq2wsz_9hxLBtMB5rQPafFoxop7O4BklvZ9-ECcK6dfI2CAx9_tjQ"}]}' ``` -### Distant Key Sets +### Distant key sets You can load key sets shared by a distant service (e.g. Google, Microsoft, Okta...). You must install and enable the [Symfony Http Client](https://symfony.com/doc/current/http_client.html). @@ -51,11 +51,11 @@ jose: * It is **highly recommended** to use a caching layer for your HTTP client and thus avoid unnecessary calls to the key set endpoint (e.g. Symfony's `CachingHttpClient`). * The **connection must be secured** and certificate verification should not be disabled. -{% hint style="warning" %} -The `UrlKeySetFactory::enabledCache()` method is deprecated since v4.1 and will be removed in v5.0. Please configure caching at the HTTP client level instead. -{% endhint %} +!!! warning -#### From A JKU (JWK Url) + The `UrlKeySetFactory::enabledCache()` method is deprecated since v4.1 and will be removed in v5.0. Please configure caching at the HTTP client level instead. + +#### From a JKU (JWK URL) The following example will allow you to load a key set from a distant URI. The key set must be a JWKSet object. @@ -67,7 +67,7 @@ jose: url: 'https://login.microsoftonline.com/common/discovery/keys' ``` -#### From A X5U (X509 Certificates Url) +#### From a X5U (X509 certificates URL) The following example will allow you to load a key set from a distant URI. The key set must be a list of X509 certificates. @@ -79,7 +79,7 @@ jose: url: 'https://www.googleapis.com/oauth2/v1/certs' ``` -## Shared Key Sets +## Shared key sets It can be interesting to share your key sets through an Url. This can easily achieved by adding a dedicated controller. This controller is automatically created by the bundle. @@ -107,7 +107,7 @@ jose: Now when you go to the URL `http://128.0.0.1:8000/certs`, you will get your key set. -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/signed-tokens/README.md b/docs/the-symfony-bundle/signed-tokens/README.md similarity index 96% rename from the-symfony-bundle/signed-tokens/README.md rename to docs/the-symfony-bundle/signed-tokens/README.md index 161708f..e15834e 100644 --- a/the-symfony-bundle/signed-tokens/README.md +++ b/docs/the-symfony-bundle/signed-tokens/README.md @@ -1,4 +1,4 @@ -# Signed Tokens +# Signed tokens Signature algorithms are automatically handled by the Algorithm Manager Factory. diff --git a/the-symfony-bundle/signed-tokens/jws-creation.md b/docs/the-symfony-bundle/signed-tokens/jws-creation.md similarity index 94% rename from the-symfony-bundle/signed-tokens/jws-creation.md rename to docs/the-symfony-bundle/signed-tokens/jws-creation.md index 616c21a..f14d1ad 100644 --- a/the-symfony-bundle/signed-tokens/jws-creation.md +++ b/docs/the-symfony-bundle/signed-tokens/jws-creation.md @@ -1,6 +1,6 @@ # JWS creation -## JWS Builder Factory Service +## JWS builder factory service A `JWSBuilderFactory` is available as a service in your application container: @@ -19,7 +19,7 @@ $jwsBuilder = $jwsBuilderFactory->create(['HS256']); You can now use the JWSBuilder as explained in the JWS Creation section. -## JWS Builder As Service +## JWS builder as service There is also another way to create a JWSBuilder object: using the bundle configuration. @@ -39,7 +39,7 @@ With the previous configuration, the bundle will create a public JWS Builder ser $jwsBuilder = $container->get('jose.jws_builder.builder1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/signed-tokens/jws-serializers.md b/docs/the-symfony-bundle/signed-tokens/jws-serializers.md similarity index 93% rename from the-symfony-bundle/signed-tokens/jws-serializers.md rename to docs/the-symfony-bundle/signed-tokens/jws-serializers.md index 17f1deb..cc1fb39 100644 --- a/the-symfony-bundle/signed-tokens/jws-serializers.md +++ b/docs/the-symfony-bundle/signed-tokens/jws-serializers.md @@ -1,6 +1,6 @@ # JWS serializers -## JWS Serializer Manager Factory Service +## JWS serializer manager factory service A `JWSSerializerManagerFactory` is available as a service in your application container: @@ -25,7 +25,7 @@ Available JWS serialization modes are: * `jws_json_general` * `jws_json_flattened` -## JWS Serializer Manager As Service +## JWS serializer manager as service There is also another way to create a JWSSerializerManager object: using the bundle configuration. @@ -45,7 +45,7 @@ With the previous configuration, the bundle will create a public JWS Serializer $jwsSerializerManager = $container->get('jose.jws_serializer.serializer1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. diff --git a/the-symfony-bundle/signed-tokens/jws-verification.md b/docs/the-symfony-bundle/signed-tokens/jws-verification.md similarity index 90% rename from the-symfony-bundle/signed-tokens/jws-verification.md rename to docs/the-symfony-bundle/signed-tokens/jws-verification.md index ccb79f2..f605828 100644 --- a/the-symfony-bundle/signed-tokens/jws-verification.md +++ b/docs/the-symfony-bundle/signed-tokens/jws-verification.md @@ -1,6 +1,6 @@ # JWS verification -## JWS Verifier Factory Service +## JWS verifier factory service A `JWSVerifierFactory` is available as a service in your application container: @@ -19,11 +19,11 @@ $jwsVerifier = $jwsVerifierFactory->create(['HS256']); You can now use the JWSVerifier as explained in the JWS Creation section. -{% hint style="info" %} -Reminder: it is important to check the token headers. See the checker section of this documentation. -{% endhint %} +!!! info -## JWS Verifier As Service + Reminder: it is important to check the token headers. See the checker section of this documentation. + +## JWS verifier as service There is also another way to create a JWSVerifier object: using the bundle configuration. @@ -43,7 +43,7 @@ With the previous configuration, the bundle will create a public JWS Verifier se $jwsVerifier = $container->get('jose.jws_verifier.verifier1'); ``` -## Custom Tags +## Custom tags You can add custom tags and attributes to the services you create. @@ -58,7 +58,7 @@ jose: tag_name2: {attribute1: 'foo'} ``` -## JWS Loader Service +## JWS loader service The [`JWSLoaderFactory`](../../the-components/signed-tokens-jws/jws-loading.md) is available as a public service. You can retrieve it using the container or inject it into your services. It will help you to create `JWSLoader` objects on demand. diff --git a/the-symfony-bundle/symfony-bundle.md b/docs/the-symfony-bundle/symfony-bundle.md similarity index 83% rename from the-symfony-bundle/symfony-bundle.md rename to docs/the-symfony-bundle/symfony-bundle.md index df5cc83..7d20894 100644 --- a/the-symfony-bundle/symfony-bundle.md +++ b/docs/the-symfony-bundle/symfony-bundle.md @@ -1,41 +1,41 @@ -# Symfony Bundle +# Symfony bundle The JWT Framework Symfony Bundle provides seamless integration of the JWT Framework into your Symfony applications. It leverages Symfony's dependency injection container to automatically configure and wire all services, making it easy to work with JSON Web Tokens in your Symfony projects. -## Why Use the Symfony Bundle? +## Why use the Symfony bundle? The bundle offers several advantages for Symfony developers: -### 🎯 **Zero Configuration** +### 🎯 **Zero configuration** - Auto-discovery of algorithms and services - Automatic service registration - Sensible defaults out of the box -### 🔧 **Configuration-Driven** +### 🔧 **Configuration-driven** - YAML-based configuration for all components - Type-safe configuration validation - Environment variable support -### 📊 **Developer Tools** +### 📊 **Developer tools** - Symfony Profiler integration - Debug toolbar with token inspection - Data collectors for performance monitoring -### 🔄 **Event System** +### 🔄 **Event system** - Token lifecycle events - Hook into token creation and validation - Perfect for logging and auditing -### 🏭 **Factory Services** +### 🏭 **Factory services** - Pre-configured factories for common operations - Easy creation of builders, loaders, and validators - Support for multiple configurations ## Installation -{% hint style="info" %} -The bundle supports Symfony 7.0 and Symfony 8.0. -{% endhint %} +!!! info + + The bundle supports Symfony 7.0 and Symfony 8.0. ### With Symfony Flex (Recommended) @@ -45,9 +45,9 @@ The bundle is automatically detected and installed when using Symfony Flex: composer require web-token/jwt-bundle ``` -{% hint style="info" %} -As the recipes are third party recipes not officially supported by Symfony, you will be asked whether to execute the recipe or not. When applied, the recipes will display a message: `Web Token Framework is ready.` -{% endhint %} +!!! info + + As the recipes are third party recipes not officially supported by Symfony, you will be asked whether to execute the recipe or not. When applied, the recipes will display a message: `Web Token Framework is ready.` After installation, the bundle is automatically registered and ready to use! @@ -55,8 +55,7 @@ After installation, the bundle is automatically registered and ready to use! If you're not using Symfony Flex, you need to register the bundle manually: -{% code title="config/bundles.php" %} -```php +```php title="config/bundles.php" ['all' => true], ]; ``` -{% endcode %} -## Quick Start Example +## Quick start example After installation, you can configure a simple JWS service: -{% code title="config/packages/jose.yaml" %} -```yaml +```yaml title="config/packages/jose.yaml" jose: # Configure the algorithm manager jws: @@ -83,7 +80,6 @@ jose: my_jws_verifier: signature_algorithms: ['HS256'] ``` -{% endcode %} Then use it in your services: @@ -105,17 +101,17 @@ class TokenService } ``` -## Bundle Features +## Bundle features The bundle provides comprehensive support for all JWT Framework components. Explore the documentation based on your needs: -### Core Features +### Core features * **[Algorithm Management](algorithm-management.md)** - Configure signature and encryption algorithms * **[Key and Key Set Management](key-and-key-set-management/README.md)** - Manage cryptographic keys via configuration * **[Header and Claim Checkers](header-and-claim-checker-management.md)** - Validate token headers and claims -### Token Operations +### Token operations * **[Signed Tokens (JWS)](signed-tokens/README.md)** - Create and verify signed tokens * [JWS Serializers](signed-tokens/jws-serializers.md) @@ -127,12 +123,12 @@ The bundle provides comprehensive support for all JWT Framework components. Expl * [JWE Creation](encrypted-tokens/jwe-creation.md) * [JWE Decryption](encrypted-tokens/jwe-decryption.md) -### Advanced Features +### Advanced features * **[Configuration Helper](configuration-helper.md)** - Programmatic configuration * **[Events](events.md)** - Token lifecycle events and listeners -## Service Auto-wiring +## Service auto-wiring All services created through the bundle configuration are automatically available for auto-wiring. You can inject them by type or by name: @@ -151,7 +147,7 @@ class MyService } ``` -## Configuration Reference +## Configuration reference For a complete configuration reference, run: @@ -159,7 +155,7 @@ For a complete configuration reference, run: php bin/console config:dump-reference jose ``` -## Next Steps +## Next steps - **New to the bundle?** Start with [Algorithm Management](algorithm-management.md) - **Need to sign tokens?** Check [Signed Tokens](signed-tokens/README.md) diff --git a/introduction/pre-requisite.md b/introduction/pre-requisite.md deleted file mode 100644 index 6f563f9..0000000 --- a/introduction/pre-requisite.md +++ /dev/null @@ -1,22 +0,0 @@ -# Prerequisites - -This framework needs at least: - -* PHP 8.2+ -* Extensions (required): - * openssl - -* Extensions (recommended): - * gmp or bcmath (for better performance with RSA/PSS algorithms) - * sodium (for EdDSA with Ed25519, ECDH-ES with X25519, and faster Base64URL encoding) - * curl (for loading keys from remote URLs) - -{% hint style="info" %} -The `json` extension is built-in with PHP 8.2+ and no longer needs to be installed separately. -{% endhint %} - -{% hint style="info" %} -When the `ext-sodium` extension is available, the framework automatically uses it for Base64URL encoding/decoding operations, providing significant performance improvements. -{% endhint %} - -Please note that cryptographic operations may be really slow, especially RSA functions. It is highly recommended to enable GMP or BCMath. diff --git a/introduction/provided-features.md b/introduction/provided-features.md deleted file mode 100644 index e3c8d2e..0000000 --- a/introduction/provided-features.md +++ /dev/null @@ -1,107 +0,0 @@ -# Provided Features - -## Supported Input Types - -JWS or JWE objects support every input that can be encoded into JSON: - -* `string`, `array`, `integer`, `float`... -* Objects that implement the `\JsonSerializable` interface such as `JWK` or `JWKSet` - -The [detached payload](../advanced-topics/signed-tokens/detached-payload.md) is supported. - -## Supported Serialization Modes - -| Serialization syntax | JWS | JWE | -| -------------------- | --- | --- | -| Compact | YES | YES | -| Flattened JSON | YES | YES | -| General JSON | YES | YES | - -## Supported Compression Methods - -{% hint style="danger" %} -Compression is not recommended. Since version 4.0, compression support has been removed. See [RFC 8725](https://datatracker.ietf.org/doc/html/rfc8725#section-3.6) for more information. -{% endhint %} - -## Supported Key Types (JWK) - -| Key Type | Supported | Comment | -| -------- | --------- | ------------------------------------- | -| oct | YES | Symmetric keys | -| RSA | YES | RSA based asymmetric keys | -| EC | YES | Elliptic Curves based asymmetric keys | -| OKP | YES | Octet Key Pair based asymmetric keys | - -JWK objects support JSON Web Key Thumbprint ([RFC 7638](https://tools.ietf.org/html/rfc7638)). - -{% hint style="info" %} -A `none` key type is available for the `none` algorithm. It is used to explicitly allow this unsecured algorithm. -{% endhint %} - -## Supported Elliptic Curves - -| Key Type | Curve | Supported | Comment | -| -------- | ---------------------------------- | --------- | ------------------------------------------------------------------------------------------------ | -| EC |

P-256

P-384

P-521

| YES | ECDSA signatures (`ES256`/`ES384`/`ES512`) and `ECDH-ES*`/`ECDH-SS*` key agreement | -| EC | secp256k1 | YES | `ES256K`, in the `Jose\Experimental` namespace | -| EC |

BP-256

BP-384

BP-512

| YES | Brainpool curves. `BP256R1`/`BP384R1`/`BP512R1` signatures are in the `Jose\Experimental` namespace | -| OKP | Ed25519 | YES | `EdDSA` signatures. SODIUM extension is required | -| OKP | X25519 | YES | `ECDH-ES*` key agreement. SODIUM extension is required | -| OKP |

Ed448

X448

| NO | No extension or built-in implementation available | - -{% hint style="warning" %} -The Brainpool curves are not registered with IANA, and neither are their signature algorithms. The identifiers follow the convention adopted by the other implementations, so keys and tokens using them are only interoperable with the implementations sharing that convention. -{% endhint %} - -## Key Sets (JWKSet) - -JWKSet is fully supported. - -## Supported Signature Algorithms - -| Signature Algorithm | Supported | Comment | -| ------------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------- | -|

HS256

HS384

HS512

| YES | | -|

ES256

ES384

ES512

| YES | | -|

RS256

RS384

RS512

| YES | | -|

PS256

PS384

PS512

| YES | GMP or BCMath extension is highly recommended | -| EdDSA with Ed25519 curve | YES | SODIUM extension is required | -| EdDSA with Ed448 curve | NO | No extension or built-in implementation available | -| none | YES | **Please note that this is not a secured algorithm. USE IT WITH CAUTION!** | - -{% hint style="info" %} -Other signature algorithms like `RS1`, `HS1`, `HS256/64`, `ES256K`, `BP256R1`, `BP384R1`, `BP512R1` and `Blake2b` are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. -{% endhint %} - -## Supported Key Encryption Algorithms - -| Key Encryption Algorithm | Supported | | -| ----------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| dir | YES | | -|

RSA1_5

RSA-OAEP

RSA-OAEP-256

| YES |

GMP or BCMath extension is highly recommended

Read note below!

| -|

ECDH-ES

ECDH-ES+A128KW

ECDH-ES+A192KW

ECDH-ES+A256KW

| YES | `spomky-labs/aes-key-wrap` is required for \*KW algorithms. See the [supported curves](#supported-elliptic-curves) | -|

ECDH-SS

ECDH-SS+A128KW

ECDH-SS+A192KW

ECDH-SS+A256KW

| YES | `spomky-labs/aes-key-wrap` is required for \*KW algorithms. See the [supported curves](#supported-elliptic-curves) | -|

A128KW

A192KW

A256KW

| YES | `spomky-labs/aes-key-wrap` is required | -|

PBES2-HS256+A128KW

PBES2-HS384+A192KW

PBES2-HS512+A256KW

| YES | `spomky-labs/aes-key-wrap` is required | -|

A128GCMKW

A192GCMKW

A256GCMKW

| YES | `spomky-labs/aes-key-wrap` is required | -| ECDH-ES with X25519 curve | YES | SODIUM extension is required | -| ECDH-ES with X448 curve | NO | No extension or built-in implementation available | - -{% hint style="info" %} -Other key encryption algorithms like `RSA-OAEP-384`, `RSA-OAEP-512`, and `chacha20-poly1305` are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. -{% endhint %} - -{% hint style="danger" %} -The algorithms `RSA1_5` and `RSA-OAEP` are now deprecated. Please use with caution. -{% endhint %} - -## Supported Content Encryption Algorithms - -| Content Encryption Algorithm | Supported | -| ------------------------------------------------------------ | --------- | -|

A128CBC-HS256

A192CBC-HS384

A256CBC-HS512

| YES | -|

A128GCM

A192GCM

A256GCM

| YES | - -{% hint style="info" %} -Other content encryption algorithms like `A128CTR`, `A192CTR`, `A256CTR`, and various AES-CCM algorithms (e.g., `A128CCM-16-128`, `A256CCM-64-64`) are also available in the `Jose\Experimental` namespace. These algorithms should be used for testing purposes only or for compatibility with old systems. -{% endhint %} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..4b63837 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,262 @@ +site_name: JWT Framework +site_description: JSON Web Tokens for PHP and Symfony. Signature (JWS), encryption (JWE) and keys (JWK), after RFC 7515 to RFC 7519. +site_url: https://web-token.spomky-labs.com/ +site_author: Spomky-Labs +repo_url: https://github.com/web-token/jwt-framework +repo_name: web-token/jwt-framework +edit_uri: https://github.com/web-token/jwt-doc/edit/4.2/docs/ + +docs_dir: docs +site_dir: site + +# The GitBook table of contents. It stays while GitBook serves the domain, and the navigation below says the +# same thing in the words of MkDocs. +exclude_docs: | + SUMMARY.md + +theme: + name: material + custom_dir: docs/overrides + language: en + # Space Grotesk and IBM Plex Mono are served from assets/fonts, declared in slabs.css: the theme loads + # no font of its own. + font: false + icon: + repo: fontawesome/brands/github + edit: material/pencil-outline + annotation: material/plus-circle + favicon: assets/brand/favicon.svg + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Follow the system theme + - media: "(prefers-color-scheme: light)" + scheme: default + primary: custom + accent: custom + toggle: + icon: material/weather-sunny + name: Switch to the dark theme + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: custom + accent: custom + toggle: + icon: material/weather-night + name: Switch to the light theme + features: + - navigation.instant + - navigation.instant.progress + - navigation.tracking + - navigation.sections + - navigation.indexes + - navigation.top + - navigation.footer + - toc.follow + - search.suggest + - search.highlight + - search.share + - content.code.copy + - content.code.annotate + - content.tabs.link + - content.tooltips + - content.action.edit + +# The tokens of the Slabs design kit, as they are, then the rules that apply them. +extra_css: + - assets/stylesheets/slabs-tokens.css + - assets/stylesheets/slabs.css + +plugins: + - search + # Every asset is served from this site. Should a page ever reference an external one, it is downloaded + # at build time: nothing a reader loads reaches a third party. + - privacy + - git-revision-date-localized: + enable_creation_date: false + type: date + fallback_to_build_date: true + # GitBook published the console commands under console-commands/ and the benchmarks under benchmarks/; + # the directories are console-command/ and benchmark/. + - redirects: + redirect_maps: + console-commands/console.md: console-command/console.md + console-commands/standalone.md: console-command/standalone.md + console-commands/phar-application.md: console-command/phar-application.md + console-commands/symfony-console.md: console-command/symfony-console.md + benchmarks/benchmarks.md: benchmark/benchmarks.md + benchmarks/result-table.md: benchmark/result-table.md + +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - tables + - toc: + title: On this page + permalink: true + permalink_title: Link to this section + toc_depth: 3 + - pymdownx.betterem + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.highlight: + anchor_linenums: true + # A PHP block of these pages starts without " + + + + + + Page not found · JWT Framework + + + + + + + +
+

Looking for this page in the latest version…

+
+ +

Page not found

+

This page has moved or never existed. Search, or start from one of these:

+
+ + +
+ +
+
+ + diff --git a/site-root/CNAME b/site-root/CNAME new file mode 100644 index 0000000..163ae4e --- /dev/null +++ b/site-root/CNAME @@ -0,0 +1 @@ +web-token.spomky-labs.com diff --git a/the-components/encrypted-tokens-jwe/encryption-algorithms.md b/the-components/encrypted-tokens-jwe/encryption-algorithms.md deleted file mode 100644 index 83f1d3c..0000000 --- a/the-components/encrypted-tokens-jwe/encryption-algorithms.md +++ /dev/null @@ -1,93 +0,0 @@ -# Encryption Algorithms - -This framework comes with several encryption algorithms. These algorithms are in the following namespaces: - -* `Jose\Component\Encryption\Algorithm\KeyEncryption`: key encryption algorithms -* `Jose\Component\Encryption\Algorithm\ContentEncryption`: content encryption algorithms - -## Main Algorithms - -### Key Encryption - -{% hint style="info" %} -`spomky-labs/aes-key-wrap` is required for \*KW algorithms -{% endhint %} - -
AlgorithmAdditional header parameter

A128KW

A192KW

A256KW

No

A128GCMKW

A192GCMKW

A256GCMKW

iv: (initialization vector) this value is the base64url-encoded representation of the 96-bit IV value used for the key encryption operation.
tag: (authentication tag) the value is the base64url-encoded representation of the 128-bit Authentication Tag value resulting from the key encryption operation.
dirNo

ECDH-ES

ECDH-ES+A128KW

ECDH-ES+A192KW

ECDH-ES+A256KW

epk: (ephemeral public key) value created by the originator.

ECDH-SS

ECDH-SS+A128KW

ECDH-SS+A192KW

ECDH-SS+A256KW

No

PBES2-HS256+A128KW

PBES2-HS384+A192KW

PBES2-HS512+A256KW

p2s: (PBES2 salt input) encodes a Salt Input value, which is used as part of the PBKDF2 salt value.
p2c: (PBES2 count) contains the PBKDF2 iteration count, represented as a positive JSON integer.

RSA1_5

RSA-OAEP

RSA-OAEP-256

- -{% hint style="warning" %} -Please note that the additional header parameters **MUST** be present and **MUST** be understood. Depending on the algorithm you use, you may be required to check headers BEFORE the decryption operation. Please create a [custom Header Checker](../header-checker.md) for theses parameters. -{% endhint %} - -### Content Encryption - -| Algorithm | Namespace | -| ------------------------------------------------------------ | --------------------------------------------------------------------- | -|

A128GCM

A192GCM

A256GCM

| `Jose\Component\Encryption\Algorithm\ContentEncryption` | -|

A128CBC-HS256

A192CBC-HS384

A256CBC-HS512

| `Jose\Component\Encryption\Algorithm\ContentEncryption` | - -{% hint style="danger" %} -The algorithm `RSA1_5` is deprecated due to known [security vulnerability](https://en.wikipedia.org/wiki/Adaptive_chosen-ciphertext_attack). - -The algorithms `ECDH-ES*` are not recommended unless used with the `OKP` key type. -{% endhint %} - -### Curves Supported By The Key Agreement Algorithms - -The `ECDH-ES*` and `ECDH-SS*` algorithms accept: - -* `EC` keys using the `P-256`, `P-384`, `P-521`, `secp256k1`, `BP-256`, `BP-384` or `BP-512` curve, -* `OKP` keys using the `X25519` curve (SODIUM extension is required). - -The `BP-*` curves are the Brainpool curves. They are part of the core library and do not require the experimental package. Please read the [warning about their interoperability](../key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) before using them. - -## Experimental Algorithms - -The following algorithms are experimental and must not be used in production unless you know what you are doing. They are proposed for testing purpose only. - -### Key Encryption - -
AlgorithmDescription

A128CTR

A192CTR

A256CTR

AES CTR based encryption
Chacha20+Poly1305Please note that this algorithm requires OpenSSL 1.1

RSA-OAEP-384

RSA-OAEP-512

Same algorithm as RSA-OAEP-256 but with SHA-384 and SHA-512 hashing functions
- -### Content Encryption - -
AlgorithmDescription

A128CCM-16-128

A128CCM-16-64

A128CCM-64-128

A128CCM-64-64

A256CCM-16-128

A256CCM-16-64

A256CCM-64-128

A256CCM-64-64

AES-CCM based algorithms
- -## How To Use - -These algorithms have to be used with the [Algorithm Manager](../algorithm-management-jwa.md). - -```php -AlgorithmDescription

HS256

HS384

HS512

HMAC with SHA-2 Functions

ES256

ES384

ES512

Elliptic Curve Digital Signature Algorithm (ECDSA)

RS256

RS384

RS512

RSASSA-PKCS1 v1_5

PS256

PS384

PS512

RSASSA-PSSEdDSA (only with the Ed25519 curve)Edwards-curve Digital Signature Algorithm (EdDSA)noneNot a secure algorithm. Please use with caution - -### Experimental Algorithms - -The following signature algorithms are experimental and must not be used in production unless you know what you are doing. They are proposed for testing purpose only. - -They are provided through the package `web-token/jwt-experimental`. - -| Algorithm | Description | -| --------- | ------------------------------------------------------------------------------ | -| RS1 | RSASSA-PKCS1 v1\_5 with SHA-1 hashing function | -| HS1 | HMAC with SHA-1 hashing function | -| HS256/64 | HMAC with SHA-256 truncated to 64 bits | -| ES256K | Elliptic curve secp256k1 support | -| BP256R1 | ECDSA using the brainpoolP256r1 curve (`BP-256`) and SHA-256 | -| BP384R1 | ECDSA using the brainpoolP384r1 curve (`BP-384`) and SHA-384 | -| BP512R1 | ECDSA using the brainpoolP512r1 curve (`BP-512`) and SHA-512 | -| Blake2b | Blake2b MAC algorithm. Sodium extension required | - -{% hint style="warning" %} -The Brainpool algorithms and their curves are **not registered with IANA**. The identifiers `BP256R1`/`BP384R1`/`BP512R1` and `BP-256`/`BP-384`/`BP-512` follow the convention already adopted by the other implementations, so tokens using them are only interoperable with the implementations sharing that convention. - -The curves themselves are part of the core library: [creating a Brainpool key](../key-jwk-and-key-set-jwkset/key-management.md#elliptic-curve-key-pair) or using one with `ECDH-ES`/`ECDH-SS` does not require the experimental package. -{% endhint %} - -## How To Use - -These algorithms have to be used with the [Algorithm Manager](../algorithm-management-jwa.md). They do not need any arguments. - -Example: - -```php -