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/.gitbook/assets/fido2 (1).jpeg b/.gitbook/assets/fido2 (1).jpeg deleted file mode 100644 index 64b2450..0000000 Binary files a/.gitbook/assets/fido2 (1).jpeg and /dev/null differ diff --git a/.gitbook/assets/images.jpeg b/.gitbook/assets/images.jpeg deleted file mode 100644 index 10ad08d..0000000 Binary files a/.gitbook/assets/images.jpeg and /dev/null differ diff --git a/.gitbook/assets/login (1).png b/.gitbook/assets/login (1).png deleted file mode 100644 index 39ddb60..0000000 Binary files a/.gitbook/assets/login (1).png and /dev/null differ diff --git a/.gitbook/assets/master (1).jpg b/.gitbook/assets/master (1).jpg deleted file mode 100644 index fef008e..0000000 Binary files a/.gitbook/assets/master (1).jpg and /dev/null differ diff --git a/.gitbook/assets/registration (1).png b/.gitbook/assets/registration (1).png deleted file mode 100644 index 95bd5e2..0000000 Binary files a/.gitbook/assets/registration (1).png and /dev/null differ diff --git a/.gitbook/assets/registration.png b/.gitbook/assets/registration.png deleted file mode 100644 index 95bd5e2..0000000 Binary files a/.gitbook/assets/registration.png and /dev/null differ diff --git a/.gitbook/assets/webauthn-android-fennec.png b/.gitbook/assets/webauthn-android-fennec.png deleted file mode 100644 index 8645abb..0000000 Binary files a/.gitbook/assets/webauthn-android-fennec.png and /dev/null differ 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..745da95 --- /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: 5.2 is served at /5.2/, and the +# alias "latest" follows the highest version, which is what the root of the site redirects to. A branch is +# named 5.2, or 5.2.x like the maintenance branches of a library; 5.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: 5.2.x publishes 5.2, 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 267228d..65a6fcf 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,52 @@ ---- -description: >- - Seamless Integration with PHP Applications: Our Webauthn Documentation and - Tutorials -layout: landing ---- +# The documentation of WebAuthn Framework -# WebAuthn: Strong Authentication for your PHP applications +The public documentation of [WebAuthn Framework](https://github.com/web-auth/webauthn-framework), WebAuthn +and passkeys for PHP and Symfony: the ceremonies, the pure PHP library, the Symfony bundle and the Symfony +UX components. -Welcome to WebAuthn, the solution for strong authentication on the web. With WebAuthn, you can offer your users a secure, simple, and convenient authentication experience that seamlessly integrates with your PHP or Symfony application. +Published with [MkDocs](https://www.mkdocs.org/) and +[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) on GitHub Pages: +**** -At Spomky-Labs, we are committed to making the web safer for all users. We provide free, libre, and open-source libraries and a Symfony bundle to help you implement strong authentication solutions for your websites and applications. +## Writing -[WebAuthn](https://www.w3.org/TR/webauthn-2/) is an open-source technology developed by the [World Wide Web Consortium (W3C)](https://www.w3.org/) and is compatible with all modern web browsers. With WebAuthn and PHP, you can offer your users secure authentication without requiring them to remember complex passwords or provide sensitive personal information. +```bash +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 +``` -WebAuthn is supported by the FIDO Alliance, an organization working to promote open and interoperable strong authentication standards. You can learn more about the FIDO Alliance by visiting their website at [https://fidoalliance.org/](https://fidoalliance.org/). +`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. -Explore our documentation to learn more about WebAuthn and how it can help you implement strong authentication in your PHP or Symfony application. +## Where things are + +| 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 | + +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). + +## One branch per version + +A branch of this repository is a version of the packages: `5.2`, `5.3`, `5.4`, the three minor versions +the library maintains. 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 made on each of them. + +```bash +git switch -c 5.5 5.4 # from the version it continues +# in mkdocs.yml, point edit_uri at the new branch +git push -u origin 5.5 # publishes it, and moves "latest" and the default branch +``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..a595605 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,17 @@ +--- +description: >- + Seamless Integration with PHP Applications: Our Webauthn Documentation and + Tutorials +--- + +# WebAuthn: Strong authentication for your PHP applications + +Welcome to WebAuthn, the solution for strong authentication on the web. With WebAuthn, you can offer your users a secure, simple, and convenient authentication experience that seamlessly integrates with your PHP or Symfony application. + +At Spomky-Labs, we are committed to making the web safer for all users. We provide free, libre, and open-source libraries and a Symfony bundle to help you implement strong authentication solutions for your websites and applications. + +[WebAuthn](https://www.w3.org/TR/webauthn-2/) is an open-source technology developed by the [World Wide Web Consortium (W3C)](https://www.w3.org/) and is compatible with all modern web browsers. With WebAuthn and PHP, you can offer your users secure authentication without requiring them to remember complex passwords or provide sensitive personal information. + +WebAuthn is supported by the FIDO Alliance, an organization working to promote open and interoperable strong authentication standards. You can learn more about the FIDO Alliance by visiting their website at [https://fidoalliance.org/](https://fidoalliance.org/). + +Explore our documentation to learn more about WebAuthn and how it can help you implement strong authentication in your PHP or Symfony application. diff --git a/SUMMARY.md b/docs/SUMMARY.md similarity index 100% rename from SUMMARY.md rename to docs/SUMMARY.md 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-webauthn-framework-couleur.svg b/docs/assets/brand/marque-webauthn-framework-couleur.svg new file mode 100644 index 0000000..d6ff459 --- /dev/null +++ b/docs/assets/brand/marque-webauthn-framework-couleur.svg @@ -0,0 +1 @@ + diff --git a/docs/assets/brand/marque-webauthn-framework-inverse.svg b/docs/assets/brand/marque-webauthn-framework-inverse.svg new file mode 100644 index 0000000..a949130 --- /dev/null +++ b/docs/assets/brand/marque-webauthn-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/.gitbook/assets/FIDO_Passkey_mark_A_black-e1702581853266.png b/docs/assets/images/fido-passkey-mark-a-black-e1702581853266.png similarity index 100% rename from .gitbook/assets/FIDO_Passkey_mark_A_black-e1702581853266.png rename to docs/assets/images/fido-passkey-mark-a-black-e1702581853266.png diff --git a/.gitbook/assets/fido2.jpeg b/docs/assets/images/fido2.jpeg similarity index 100% rename from .gitbook/assets/fido2.jpeg rename to docs/assets/images/fido2.jpeg diff --git a/.gitbook/assets/images (1).jpeg b/docs/assets/images/images-1.jpeg similarity index 100% rename from .gitbook/assets/images (1).jpeg rename to docs/assets/images/images-1.jpeg diff --git a/.gitbook/assets/login.png b/docs/assets/images/login.png similarity index 100% rename from .gitbook/assets/login.png rename to docs/assets/images/login.png diff --git a/.gitbook/assets/master.jpg b/docs/assets/images/master.jpg similarity index 100% rename from .gitbook/assets/master.jpg rename to docs/assets/images/master.jpg diff --git a/.gitbook/assets/registration (2).png b/docs/assets/images/registration-2.png similarity index 100% rename from .gitbook/assets/registration (2).png rename to docs/assets/images/registration-2.png diff --git a/.gitbook/assets/webauthn-android-fennec (1).png b/docs/assets/images/webauthn-android-fennec-1.png similarity index 100% rename from .gitbook/assets/webauthn-android-fennec (1).png rename to docs/assets/images/webauthn-android-fennec-1.png 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/migration/from-v5.x-to-v6.0.md b/docs/migration/from-v5.x-to-v6.0.md similarity index 97% rename from migration/from-v5.x-to-v6.0.md rename to docs/migration/from-v5.x-to-v6.0.md index a8f9303..17f94cc 100644 --- a/migration/from-v5.x-to-v6.0.md +++ b/docs/migration/from-v5.x-to-v6.0.md @@ -4,9 +4,9 @@ description: Step-by-step guide for migrating from 5.x to 6.0 # From 5.x to 6.0 -{% hint style="info" %} -This page is subject to changes as the version 6.0.0 is not available at the time of writing. -{% endhint %} +!!! info + + This page is subject to changes as the version 6.0.0 is not available at the time of writing. This project follows the [Semantic Versioning principles](https://semver.org) and, contrary to upgrade a minor version (where the middle number changes) where no difficulty should be encountered, upgrade a major version (where the first number changes) is subject to significant modifications. @@ -44,7 +44,7 @@ webauthn: allow_subdomains: false ``` -### Options Storage +### Options storage `options_storage` option on the controller or firewall levels are deprecated. Please use the top level configuration key 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..e261fcf --- /dev/null +++ b/docs/overrides/partials/logo.html @@ -0,0 +1,8 @@ +{#- + The project mark of WebAuthn 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/prerequisites/credential-source-repository.md b/docs/prerequisites/credential-source-repository.md similarity index 82% rename from prerequisites/credential-source-repository.md rename to docs/prerequisites/credential-source-repository.md index 18fb409..387dc2e 100644 --- a/prerequisites/credential-source-repository.md +++ b/docs/prerequisites/credential-source-repository.md @@ -2,9 +2,9 @@ description: Authenticator details and how to manage them --- -# Credential Source +# Credential source -## Credential Source Class +## Credential source class After the registration of an authenticator, you will get a `Webauthn\PublicKeyCredentialSource` object. This object contains all the credential data needed to perform user authentication: @@ -22,8 +22,7 @@ After the registration of an authenticator, you will get a `Webauthn\PublicKeyCr * **uvInitialized**: Whether user verification was initialized (since 5.1) * **otherUI**: Optional additional UI hints (array) -{% code lineNumbers="true" %} -```php +```php linenums="1" transports; // ['usb', 'nfc'] // Get the descriptor for authentication $descriptor = $credentialSource->getPublicKeyCredentialDescriptor(); ``` -{% endcode %} -## Credential Source Repository +## Credential source repository Since 4.6.0 and except if you use the Symfony bundle, there is no interface to implement or abstract class to extend, making it easy to integrate into your application. @@ -57,16 +55,15 @@ Your repository needs to provide two main operations: 1. **Save a credential** after registration or counter update 2. **Find credentials** by credential ID or user handle -{% hint style="success" %} -Whatever database you use (MySQL, PostgreSQL, MongoDB…), it is not necessary to create foreign key relationships between your users and the Credential Sources. The `userHandle` property is sufficient to link credentials to users. -{% endhint %} +!!! success -### Repository Example + Whatever database you use (MySQL, PostgreSQL, MongoDB…), it is not necessary to create foreign key relationships between your users and the Credential Sources. The `userHandle` property is sufficient to link credentials to users. + +### Repository example Here's a simple example using an array storage (for demonstration purposes): -{% code lineNumbers="true" %} -```php +```php linenums="1" add(RSA\RS256::create()); $attestationStatementSupportManager->add(PackedAttestationStatementSupport::create($coseAlgorithmManager)); ``` -{% endcode %} -{% hint style="warning" %} -The Android SafetyNet Attestation API is deprecated. Full turndown is planned in June 2024. More information at [https://developer.android.com/training/safetynet/deprecation-timeline](https://developer.android.com/training/safetynet/deprecation-timeline) +!!! warning -It is not described on this page, but available in the previous versions of the documentation. -{% endhint %} + The Android SafetyNet Attestation API is deprecated. Full turndown is planned in June 2024. More information at [https://developer.android.com/training/safetynet/deprecation-timeline](https://developer.android.com/training/safetynet/deprecation-timeline) -## Metadata Statement Repository + It is not described on this page, but available in the previous versions of the documentation. + +## Metadata statement repository Then, you must prepare an Metadata Statement Repository. This service will manage all Metadata Statements depending on their sources (local storage or distant service). @@ -71,11 +69,11 @@ Your Metadata Statement Repository must implement the interface `Webauthn\Metada * `findOneByAAGUID(string $aaguid)`: this method retrieves the `MetadataStatement` object with AAGUID. It shall return `null` in case of the absence of the MDS. -{% hint style="warning" %} -The library does not provide any Metadata Statement Repository. It is up to you to select the MDS suitable for your application and store them in your database. -{% endhint %} +!!! warning + + The library does not provide any Metadata Statement Repository. It is up to you to select the MDS suitable for your application and store them in your database. -## Status Report Repository +## Status report repository To prevent the use of rogue MDS or deprecated by the manufacturers, Status Reports may be used. The Status Report Repository is a simple service that implements the interface `Webauthn\MetadataService\StatusReportRepository` with a unique method: @@ -83,14 +81,13 @@ To prevent the use of rogue MDS or deprecated by the manufacturers, Status Repor The verification's success or failure depends on the state reported in the most recent Status Report. -## Certificate Chain Validator +## Certificate chain validator When an attestation statement is received, a certificate chain is constructed. This chain combines certificates from the Metadata Service (MDS), which are trusted, with those provided by the authenticator, which are untrusted. The Certificate Chain Validator is responsible for this task. The library provides the class `Webauthn\MetadataService\CertificateChain\PhpCertificateChainValidator` that requires a Symfony Http Client ([for CRL verification](https://en.wikipedia.org/wiki/Certificate_revocation_list)) and a PSR-20 Clock. -{% code lineNumbers="true" %} -```php +```php linenums="1" enableMetadataStatementSupport( $certificateChainValidator, ); ``` -{% endcode %} -## Requesting Attestation Statement +## Requesting attestation statement By default, no Attestation Statement is asked to the Authenticators (type = `none`). To change this behavior, you just have to set the corresponding parameter in the `Webauthn\PublicKeyCredentialCreationOptions` object. @@ -134,8 +128,7 @@ There are 3 conveyance modes available using PHP constants provided by the class * `ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT`: the Relying Party prefers an attestation conveyance yielding verifiable attestation statements, but allows the client to decide how to obtain such attestation statements. * `ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT`: the Relying Party wants to receive the attestation statement as generated by the authenticator. -{% code lineNumbers="true" %} -```php +```php linenums="1" setAlgorithmManager($algorithmManager); ``` -{% endcode %} diff --git a/pure-php/advanced-behaviours/authenticator-counter.md b/docs/pure-php/advanced-behaviours/authenticator-counter.md similarity index 92% rename from pure-php/advanced-behaviours/authenticator-counter.md rename to docs/pure-php/advanced-behaviours/authenticator-counter.md index ce3c255..22fb159 100644 --- a/pure-php/advanced-behaviours/authenticator-counter.md +++ b/docs/pure-php/advanced-behaviours/authenticator-counter.md @@ -1,4 +1,4 @@ -# Authenticator Counter +# Authenticator counter The authenticators may have an internal counter. This feature is very helpful to detect cloned devices. @@ -8,13 +8,11 @@ It is therefore required to go deeper in the protection of your application by l To do so , you have to create a custom Counter Checker and inject it to your Authenticator Assertion Response Validator. The checker must implement the interface `Webauthn\Counter\CounterChecker`. -{% code lineNumbers="true" %} -```php +```php linenums="1" setCounterChecker($customCounterChecker); ``` -{% endcode %} diff --git a/pure-php/advanced-behaviours/authenticator-selection-criteria.md b/docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md similarity index 88% rename from pure-php/advanced-behaviours/authenticator-selection-criteria.md rename to docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md index 2a06ee7..9d36749 100644 --- a/pure-php/advanced-behaviours/authenticator-selection-criteria.md +++ b/docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md @@ -1,4 +1,4 @@ -# Authenticator Selection Criteria +# Authenticator selection criteria By default, any type of authenticator can be used by your users and interact with you application. In certain circumstances, you may need to select specific authenticators e.g. when user verification is required. @@ -6,9 +6,9 @@ The Webauthn API and this library allow you to define a set of options to disall The class `Webauthn\AuthenticatorSelectionCriteria` is designed for this purpose. It is used when generating the `Webauthn\PublicKeyCredentialCreationOptions` object. -## Available Criteria +## Available criteria -### Authenticator Attachment Modality +### Authenticator attachment modality You can indicate if the authenticator must be attached to the client (platform authenticator i.e. it is usually not removable from the client device) or must be detached (roaming authenticator). @@ -20,15 +20,15 @@ Possible values are: A primary use case for platform authenticators is to register a particular client device as a "trusted device" for future authentication. This gives the user the convenience benefit of not needing a roaming authenticator, e.g., the user will not have to dig around in their pocket for their key fob or phone. -### Resident Key +### Resident key With this criterion, a Public Key Credential Source will be stored in the authenticator, client or client device. Such storage requires an authenticator capable to store such a resident credential. -{% hint style="info" %} -A resident key shall be created if you want to [authenticate users without username](authentication-without-username.md). -{% endhint %} +!!! info -### User Verification + A resident key shall be created if you want to [authenticate users without username](authentication-without-username.md). + +### User verification [Please refer to this page](user-verification.md). @@ -36,8 +36,7 @@ A resident key shall be created if you want to [authenticate users without usern With this example, with require the user verification (PIN, fingerprint...), a resident key and an authenticator embedded onto a device. This is typacally what you will require for Windows Hello or Face ID authentication. -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\PublicKeyCredentialCreationOptions; @@ -55,4 +54,3 @@ $publicKeyCredentialCreationOptions = authenticatorSelection: $authenticatorSelectionCriteria ); ``` -{% endcode %} diff --git a/pure-php/advanced-behaviours/dealing-with-localhost.md b/docs/pure-php/advanced-behaviours/dealing-with-localhost.md similarity index 81% rename from pure-php/advanced-behaviours/dealing-with-localhost.md rename to docs/pure-php/advanced-behaviours/dealing-with-localhost.md index 7e9bf6f..eb66a09 100644 --- a/pure-php/advanced-behaviours/dealing-with-localhost.md +++ b/docs/pure-php/advanced-behaviours/dealing-with-localhost.md @@ -1,4 +1,4 @@ -# Cross Origin Authentication +# Cross origin authentication ## Default behavior @@ -9,22 +9,21 @@ By default, the current host is used, and the origin parameter returned in the W * **Subdomain authentication**: Some deployments use a wildcard subdomain approach (e.g., `tenant1.example.com`, `tenant2.example.com`), but strict origin checking will reject authentication if the relying party ID does not match exactly. * **Development and testing environments**: Local development often uses `http://localhost`, which is not allowed by default due to the HTTPS requirement. -## Customizing Allowed Origins +## Customizing allowed origins To handle these cases, you can explicitly define a list of allowed origins when configuring the WebAuthn authentication process using the method `setAllowedOrigins`. -{% hint style="info" %} -The method `setSecuredRelyingPartyId` is deprecated since 5.2.0 -{% endhint %} +!!! info -### Allowing Multiple Origins + The method `setSecuredRelyingPartyId` is deprecated since 5.2.0 + +### Allowing multiple origins If your application supports multiple domains, specify them explicitly: This ensures that authentication requests coming from any of these origins are accepted. -{% code lineNumbers="true" %} -```php +```php linenums="1" setAllowedOrigins([ 'https://old-acme.com' ]); ``` -{% endcode %} -### Allowing Subdomains +### Allowing subdomains To allow authentication from subdomains dynamically, enable the `allowSubdomains` flag: -{% code lineNumbers="true" %} -```php +```php linenums="1" setAllowedOrigins(['https://acme.com'], true); ``` -{% endcode %} With this setting, authentication requests from `https://sub.acme.com` will be considered valid. -{% hint style="danger" %} -Do not use TLD and the sub domain flag together! `$csmFactory->setSecuredRelyingPartyId(['com'], true);` -{% endhint %} +!!! danger + + Do not use TLD and the sub domain flag together! `$csmFactory->setSecuredRelyingPartyId(['com'], true);` -### Supporting Native Applications +### Supporting native applications For native applications, origins are different from traditional web URLs. If your application integrates with mobile apps, ensure that relevant origins are included: -{% code lineNumbers="true" %} -```php +```php linenums="1" setAllowedOrigins([ 'ios:bundle-id://your.bundle.id', ]); ``` -{% endcode %} This configuration allows authentication responses from web browsers and native applications. -### Enabling `http://localhost` for Development +### Enabling `http://localhost` for development During local development, you may need to allow `http://localhost`. Since WebAuthn generally requires HTTPS, this is an exception for local testing. To allow it: -{% code lineNumbers="true" %} -```php +```php linenums="1" setAllowedOrigins([ 'http://localhost.com', ]); ``` -{% endcode %} Be aware that this should only be enabled in non-production environments. -## Top Origin Validation (Cross-Origin iframes) +## Top origin validation (cross-origin iframes) When your WebAuthn authentication is embedded in a cross-origin iframe (e.g., `auth.example.com` is loaded inside an iframe on `app.example.com`), the browser includes a `topOrigin` field in the client data. By default, the library **does not validate** this field. If you need to restrict which top-level origins are allowed to embed your authentication page, you must explicitly enable top origin validation by providing a `TopOriginValidator` implementation. -### Using the Built-in Host Validator +### Using the built-in host validator The `HostTopOriginValidator` performs a strict comparison between the top origin and a given host: -{% code lineNumbers="true" %} -```php +```php linenums="1" enableTopOriginValidator( new HostTopOriginValidator('app.example.com') ); ``` -{% endcode %} -### Using a Custom Validator +### Using a custom validator For more advanced scenarios (e.g., allowing multiple top origins), implement the `TopOriginValidator` interface: -{% code lineNumbers="true" %} -```php +```php linenums="1" enableTopOriginValidator( new AllowedTopOriginsValidator([ 'https://app.example.com', @@ -180,13 +167,12 @@ $csmFactory->enableTopOriginValidator( ]) ); ``` -{% endcode %} -{% hint style="warning" %} -If you do not call `enableTopOriginValidator()`, the top origin is **not validated**. Enable it if your authentication page can be embedded in cross-origin iframes and you want to control which parent origins are permitted. -{% endhint %} +!!! warning + + If you do not call `enableTopOriginValidator()`, the top origin is **not validated**. Enable it if your authentication page can be embedded in cross-origin iframes and you want to control which parent origins are permitted. -## Security Considerations +## Security considerations When modifying allowed origins, ensure that: diff --git a/pure-php/advanced-behaviours/debugging.md b/docs/pure-php/advanced-behaviours/debugging.md similarity index 88% rename from pure-php/advanced-behaviours/debugging.md rename to docs/pure-php/advanced-behaviours/debugging.md index dfae766..377be3f 100644 --- a/pure-php/advanced-behaviours/debugging.md +++ b/docs/pure-php/advanced-behaviours/debugging.md @@ -2,16 +2,15 @@ WebAuthn operations can be complex to debug due to the cryptographic operations and browser interactions involved. This page explains how to enable logging to troubleshoot issues during development or monitor your application in production. -## PSR-3 Logger Integration +## PSR-3 logger integration The WebAuthn library supports [PSR-3 compatible loggers](https://www.php-fig.org/psr/psr-3/) for debugging and monitoring. You can use any PSR-3 logger implementation such as Monolog, Symfony Logger, or others. -### Setting Up a Logger +### Setting up a logger Several WebAuthn classes implement the `Webauthn\MetadataService\CanLogData` interface, allowing you to inject a PSR-3 logger: -{% code lineNumbers="true" %} -```php +```php linenums="1" setLogger($logger); $assertionValidator = AuthenticatorAssertionResponseValidator::create($requestCSM); $assertionValidator->setLogger($logger); ``` -{% endcode %} -## What Gets Logged +## What gets logged The WebAuthn library logs various events during authentication and registration: -### Registration (Attestation) Events +### Registration (attestation) events * Challenge generation and validation * Client data validation @@ -49,7 +47,7 @@ The WebAuthn library logs various events during authentication and registration: * Certificate chain validation (when using attestation) * Errors during any step -### Authentication (Assertion) Events +### Authentication (assertion) events * Challenge validation * Credential lookup @@ -58,7 +56,7 @@ The WebAuthn library logs various events during authentication and registration: * User presence and verification checks * Errors during any step -## Log Levels +## Log levels The library uses standard PSR-3 log levels: @@ -68,7 +66,7 @@ The library uses standard PSR-3 log levels: * **ERROR**: Error messages for failures * **CRITICAL**: Critical errors requiring immediate attention -## Example Log Output +## Example log output Here's what typical log messages look like: @@ -86,14 +84,13 @@ In case of errors: [2025-01-15 10:31:12] webauthn.CRITICAL: Signature verification failed ``` -## Development vs Production +## Development vs production -### Development Environment +### Development environment Enable verbose DEBUG logging to see all operations: -{% code lineNumbers="true" %} -```php +```php linenums="1" pushHandler( new StreamHandler('php://stdout', LogLevel::DEBUG) ); ``` -{% endcode %} -### Production Environment +### Production environment In production, log only warnings and errors to avoid performance impact: -{% code lineNumbers="true" %} -```php +```php linenums="1" pushHandler( ) ); ``` -{% endcode %} -## Common Issues and Log Messages +## Common issues and log messages ### "Challenge mismatch" @@ -154,41 +148,39 @@ $logger->pushHandler( **Solution**: Check your Relying Party ID configuration matches your domain. -### "User not present" or "User not verified" +### "User not present" or "user not verified" **Cause**: User verification requirements not met. **Solution**: Check your authenticator selection criteria configuration. -## Additional Debugging Tools +## Additional debugging tools -### Browser Developer Tools +### Browser developer tools Modern browsers provide WebAuthn debugging in developer tools: * **Chrome/Edge**: DevTools → Application → WebAuthn * **Firefox**: about:config → search for "webauthn" -### Network Inspector +### Network inspector Monitor the network requests to see: * Options requests (registration/authentication) * Response data being sent back * HTTP errors or timeouts -### Test Authenticators +### Test authenticators Use virtual authenticators for testing: -{% code lineNumbers="true" %} -```javascript +```javascript linenums="1" // Chrome DevTools Protocol // Enable virtual authenticator in Chrome DevTools → Application → WebAuthn // Add Virtual Authenticator → Configure options ``` -{% endcode %} -## See Also +## See also * [Symfony Bundle Debugging](../../symfony-bundle/advanced-behaviors/debugging.md) - Framework-specific logging * [Authenticator Counter](authenticator-counter.md) - Detect cloned authenticators diff --git a/pure-php/advanced-behaviours/extensions.md b/docs/pure-php/advanced-behaviours/extensions.md similarity index 88% rename from pure-php/advanced-behaviours/extensions.md rename to docs/pure-php/advanced-behaviours/extensions.md index 89b3e65..73a469d 100644 --- a/pure-php/advanced-behaviours/extensions.md +++ b/docs/pure-php/advanced-behaviours/extensions.md @@ -2,8 +2,7 @@ The following example is totally fictive. We will add an extension input `loc=true` to the request option object. -{% code lineNumbers="true" %} -```php +```php linenums="1" add(new LocationExtensionOutputChecker()); $csmFactory = new CeremonyStepManagerFactory(); $csmFactory->setExtensionOutputCheckerHandler($eocHandler); ``` -{% endcode %} diff --git a/pure-php/advanced-behaviours/user-verification.md b/docs/pure-php/advanced-behaviours/user-verification.md similarity index 85% rename from pure-php/advanced-behaviours/user-verification.md rename to docs/pure-php/advanced-behaviours/user-verification.md index d6a4088..6c91db1 100644 --- a/pure-php/advanced-behaviours/user-verification.md +++ b/docs/pure-php/advanced-behaviours/user-verification.md @@ -1,11 +1,10 @@ -# User Verification +# User verification You can indicate the user verification requirements during the ceremonies by setting the value in your options. ### Authenticator registration -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\PublicKeyCredentialCreationOptions; @@ -18,12 +17,10 @@ $publicKeyCredentialCreationOptions = ) ; ``` -{% endcode %} -### User Authentication +### User authentication -{% code lineNumbers="true" %} -```php +```php linenums="1" // Public Key Credential Request Options use Webauthn\PublicKeyCredentialRequestOptions; @@ -32,4 +29,3 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_DISCOURAGED ); ``` -{% endcode %} diff --git a/pure-php/authenticate-your-users.md b/docs/pure-php/authenticate-your-users.md similarity index 83% rename from pure-php/authenticate-your-users.md rename to docs/pure-php/authenticate-your-users.md index 92859a4..4adfd67 100644 --- a/pure-php/authenticate-your-users.md +++ b/docs/pure-php/authenticate-your-users.md @@ -1,8 +1,8 @@ -# Authenticate Your Users +# Authenticate your users During this step, your application will send a challenge to the list of registered devices of the user. The security token will resolve this challenge by adding information and digitally signing the data. -## Assertion Request +## Assertion request To perform a user authentication using a security device, you need to instantiate a `Webauthn\PublicKeyCredentialRequestOptions` object. @@ -20,19 +20,18 @@ Optionally, you can customize the following parameters: The `PublicKeyCredentialRequestOptions` object is designed to be easily serialized into a JSON object. This will ease the integration into an HTML page or through an API endpoint. -{% hint style="info" %} -The timeout default value is set to `null`. If you want to set a value, please read the following recommended behavior showed in the specification: +!!! info -* If the user verification is `discouraged`, timeout should be between 30 and 180 seconds -* If the user verification is `preferred` or `required`, the range is 300 to 600 seconds (5 to 10 minutes) -{% endhint %} + The timeout default value is set to `null`. If you want to set a value, please read the following recommended behavior showed in the specification: -### Allowed Credentials + * If the user verification is `discouraged`, timeout should be between 30 and 180 seconds + * If the user verification is `preferred` or `required`, the range is 300 to 600 seconds (5 to 10 minutes) + +### Allowed credentials The user trying to authenticate must have registered at least one device. For this user, you have to get all `Webauthn\PublicKeyCredentialDescriptor` associated to his account. -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\PublicKeyCredentialSource; use Webauthn\PublicKeyCredentialDescriptor; @@ -50,20 +49,18 @@ $allowedCredentials = array_map( $registeredAuthenticators ); ``` -{% endcode %} -{% hint style="info" %} -For usernameless authentication, please read the [dedicated page](advanced-behaviours/authentication-without-username.md). In this case no Public Key Credential Descriptors should be passed to the options. -{% endhint %} +!!! info + + For usernameless authentication, please read the [dedicated page](advanced-behaviours/authentication-without-username.md). In this case no Public Key Credential Descriptors should be passed to the options. + +!!! danger -{% hint style="danger" %} -To prevent username enumeration, when the authentication process is performed using the username, it is highly recommended to continue the ceremony even if the user entity does not exist. The allowed credential list should be set with random data. -{% endhint %} + To prevent username enumeration, when the authentication process is performed using the username, it is highly recommended to continue the ceremony even if the user entity does not exist. The allowed credential list should be set with random data. ### Example -{% code lineNumbers="true" %} -```php +```php linenums="1" deserialize($data, PublicKeyCredential::class, 'json'); ``` -{% endcode %} -### Response Verification +### Response verification Now we have a fully loaded Public Key Credential object, but we need now to make sure that: @@ -201,8 +189,7 @@ Now we have a fully loaded Public Key Credential object, but we need now to make The first is easy to perform: -{% code lineNumbers="true" %} -```php +```php linenums="1" response instanceof AuthenticatorAssertionResponse) { //e.g. process here with a redirection to the public key login/MFA page. } ``` -{% endcode %} The second step is the verification against the Public Key Assertion Options we created earlier. The Authenticator Assertion Response Validator service (variable `$authenticatorAssertionResponseValidator`) will check everything for you. -{% code lineNumbers="true" %} -```php +```php linenums="1" check( $publicKeyCredentialSourceRepository->saveCredentialSource($publicKeyCredentialSource); ``` -{% endcode %} If no exception is thrown, the response is valid and you can continue the authentication of the user. diff --git a/pure-php/authenticator-registration.md b/docs/pure-php/authenticator-registration.md similarity index 81% rename from pure-php/authenticator-registration.md rename to docs/pure-php/authenticator-registration.md index 0527451..ca3a8f2 100644 --- a/pure-php/authenticator-registration.md +++ b/docs/pure-php/authenticator-registration.md @@ -1,10 +1,10 @@ -# Register Authenticators +# Register authenticators During this step, your application will send a challenge to the device. The device will resolve this challenge by adding information and digitally signing the data. The application will check the response from the device and get its credential ID. This ID will be used for further authentication requests. -## Creation Request +## Creation request To associate a device to a user, you need to instantiate a `Webauthn\PublicKeyCredentialCreationOptions` object. @@ -25,8 +25,7 @@ Optionally, you can customize the following parameters: Let’s see an example of the `PublicKeyCredentialCreationOptions` object. The following example is a possible Public Key Creation page for a dummy user "@cypher-Angel-3000". -{% code lineNumbers="true" %} -```php +```php linenums="1" serialize( ); ``` -{% hint style="warning" %} -It is important to store the user entity and the options object (e.g. in the session) for the next step. The data will be needed to check the response from the device. -{% endhint %} +!!! warning + + It is important to store the user entity and the options object (e.g. in the session) for the next step. The data will be needed to check the response from the device. You can change the default values for each and all options -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\AuthenticatorSelectionCriteria; $publicKeyCredentialCreationOptions = @@ -98,16 +95,14 @@ $publicKeyCredentialCreationOptions = ) ; ``` -{% endcode %} -### Public Key Credential Parameters +### Public key credential parameters The argument `pubKeyCredParams` contains a list of `Webauthn\PublicKeyCredentialParameters` objects that refer to COSE algorithms. The authenticators must use one of the algorithms in this list, respecting the order of preference on this list. An empty list corresponds to the default algorithms that are `ES256` and `RS256` (in this order). Those two algorithms are required by the specification. -{% code lineNumbers="true" %} -```php +```php linenums="1" use Cose\Algorithms; use Webauthn\PublicKeyCredentialParameters; @@ -119,13 +114,12 @@ $publicKeyCredentialParametersList = [ PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ED256), // Less interesting algorithm ]; ``` -{% endcode %} -{% hint style="warning" %} -Customizing this list may lead to unexpected behavior. Please use with caution. -{% endhint %} +!!! warning -### Authenticator Selection + Customizing this list may lead to unexpected behavior. Please use with caution. + +### Authenticator selection Please read detail [on this page](advanced-behaviours/authenticator-selection-criteria.md). @@ -133,12 +127,11 @@ Please read detail [on this page](advanced-behaviours/authenticator-selection-cr Please read detail [on this page](advanced-behaviours/attestation-and-metadata-statement.md). -### Exclude Credentials +### Exclude credentials When the user already registered authenticators, you can pass a list of `Webauthn\PublicKeyCredentialDescriptor` objects as argument to avoid registering multiple times the same authenticator. -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialDescriptor; @@ -157,20 +150,18 @@ $publicKeyCredentialCreationOptions = ) ; ``` -{% endcode %} -{% hint style="danger" %} -To avoid username enumeration, you should not set any excluded credential when the authenticator registration is performed for anonymous users. If this value is present, it indicates that the username exists and has registered credentials. +!!! danger + + To avoid username enumeration, you should not set any excluded credential when the authenticator registration is performed for anonymous users. If this value is present, it indicates that the username exists and has registered credentials. -On the contrary when adding new authenticators to already authenticated users, this value should be set. -{% endhint %} + On the contrary when adding new authenticators to already authenticated users, this value should be set. -## Creation Response +## Creation response What you receive must be a JSON object that looks like as follow: -{% code lineNumbers="true" %} -```javascript +```javascript linenums="1" { "id":"KVb8CnwDjpgAo[…]op61BTLaa0tczXvz4JrQ23usxVHA8QJZi3L9GZLsAtkcVvWObA", "type":"public-key", @@ -181,19 +172,19 @@ What you receive must be a JSON object that looks like as follow: } } ``` -{% endcode %} There are two steps to perform with this object: * [Load the data](input-loading.md) * [Verify it with the creation options set above](input-validation.md) -### Data Loading +### Data loading -Now that all components are set, we can load the data we receive using the [_Serializer_](webauthn-server.md#the-serializer) (variable `$serializer`). +Now that all components are set, we can load the data we receive using the [_Serializer_](input-loading.md#the-serializer) (variable `$serializer`). + +```php linenums="1" hl_lines="1" +<?php - declare(strict_types=1); use Webauthn\PublicKeyCredential; @@ -214,11 +205,11 @@ $publicKeyCredential = $serializer->deserialize( PublicKeyCredential::class, 'json' ); - +``` If no exception is thrown, you can go to the next step: the verification. -### Response Verification +### Response verification Now we have a fully loaded Public Key Credential object, but we need now to make sure that: @@ -227,8 +218,7 @@ Now we have a fully loaded Public Key Credential object, but we need now to make The first step is easy to perform: -{% code lineNumbers="true" %} -```php +```php linenums="1" response instanceof AuthenticatorAttestationResponse) //e.g. process here with a redirection to the public key creation page. } ``` -{% endcode %} The second step is the verification against @@ -248,8 +237,7 @@ The second step is the verification against The Authenticator Attestation Response Validator service (variable `$authenticatorAttestationResponseValidator`) will check everything for you: challenge, origin, attestation statement and much more. -{% code lineNumbers="true" %} -```php +```php linenums="1" check( 'my-application.com' ); ``` -{% endcode %} If no exception is thrown, the response is valid. You can store the Public Key Credential Source (`$publicKeyCredentialSource`). -{% hint style="info" %} -The way you store and associate these objects to the user is out of scope of this library. -{% endhint %} +!!! info + + The way you store and associate these objects to the user is out of scope of this library. diff --git a/pure-php/input-loading.md b/docs/pure-php/input-loading.md similarity index 79% rename from pure-php/input-loading.md rename to docs/pure-php/input-loading.md index cedbaa5..72282c1 100644 --- a/pure-php/input-loading.md +++ b/docs/pure-php/input-loading.md @@ -1,14 +1,14 @@ -# Input Loading +# Input loading In general, the data you receive is an encoded JSON object. The library provides utilities to convert the string into objects. To do so, you will need an Attestation Statement Support Manager and a Serializer. -## Attestation Statement Support Manager +## Attestation statement support manager Authenticator Responses may contain an Attestation Statement. This attestation holds data regarding the authenticator depending on several factors such as its manufacturer and model, what you asked in the options, the capabilities of the browser or what the user allowed. -### Supported Attestation Statement Types +### Supported attestation statement types The following attestation types are supported. Note that you should only use the `none` one unless you have specific needs described in [the dedicated page](../webauthn-in-a-nutshell/attestation-and-metadata-statement.md). @@ -19,8 +19,7 @@ The following attestation types are supported. Note that you should only use the * `trusted platform module`: for devices with built-in security chips. * `apple`: for Apple devices -{% code lineNumbers="true" %} -```php +```php linenums="1" add(NoneAttestationStatementSupport::create()); ``` -{% endcode %} -## The Serializer +## The serializer To convert Authenticator Responses from the encoded string to an object, you will need a Serializer. It only needs the Attestation Statement Support Manager created above. -{% code lineNumbers="true" %} -```php +```php linenums="1" create(); ``` -{% endcode %} -{% hint style="danger" %} -Please do not use `json_encode` or `json_decode` to convert this data either way. -{% endhint %} +!!! danger -## Loading Data + Please do not use `json_encode` or `json_decode` to convert this data either way. + +## Loading data In general, the data you receive looks like as follows. -{% code lineNumbers="true" %} -```json +```json linenums="1" { "id":"KVb8CnwDjpgAo[…]op61BTLaa0tczXvz4JrQ23usxVHA8QJZi3L9GZLsAtkcVvWObA", "type":"public-key", @@ -71,14 +66,14 @@ In general, the data you receive looks like as follows. } } ``` -{% endcode %} -{% hint style="info" %} -Only this type of input is supported. If you receive other forms of data, please contact us. -{% endhint %} +!!! info + + Only this type of input is supported. If you receive other forms of data, please contact us. + +```php linenums="1" hl_lines="1" +<?php - declare(strict_types=1); use Webauthn\PublicKeyCredential; @@ -89,10 +84,10 @@ $publicKeyCredential = $serializer->deserialize( PublicKeyCredential::class, 'json' ); - +``` If the data is correctly loaded, the variable `$publicKeyCredential` will be an instance of `Webauthn\PublicKeyCredential`. An exception is thrown in case of an error. -{% hint style="danger" %} -At this stage, the data is not verified. -{% endhint %} +!!! danger + + At this stage, the data is not verified. diff --git a/pure-php/input-validation.md b/docs/pure-php/input-validation.md similarity index 57% rename from pure-php/input-validation.md rename to docs/pure-php/input-validation.md index 050bb8f..b824c9b 100644 --- a/pure-php/input-validation.md +++ b/docs/pure-php/input-validation.md @@ -1,13 +1,12 @@ -# Input Validation +# Input validation The loaded data needs to be verified. The library will perform several actions to make sure the input you received is valid. This verification process is performed by a Ceremony Step Manager (CSM). The Webauthn Specification distinguish two types of ceremonies [described in this page](../webauthn-in-a-nutshell/ceremonies.md). -## Ceremony Step Manager Factory +## Ceremony step manager factory To facilitate the creation of the CSM, a default factory is included. This factory requires no external services to function. -{% code lineNumbers="true" %} -```php +```php linenums="1" creationCeremony(); $requestCSM = $csmFactory->requestCeremony(); ``` -{% endcode %} -{% hint style="info" %} -You can customize its behavior to fit the specific needs of your application by modifying the provided factory. Please refer to the dedicated pages for more information. +!!! info -* [Counter Checker](advanced-behaviours/authenticator-counter.md) -* [Attestation Statement Support Manager](advanced-behaviours/attestation-and-metadata-statement.md#attestation-statement-support-manager) -* [Extension Output Checker Handler](advanced-behaviours/extensions.md) -* [Algorithm Manager](advanced-behaviours/authenticator-algorithms.md) -* [Metadata Statement Repository](advanced-behaviours/attestation-and-metadata-statement.md#metadata-statement-repository) -* [Status Report Repository](advanced-behaviours/attestation-and-metadata-statement.md#status-report-repository) -* [Certification Chain Validator](advanced-behaviours/attestation-and-metadata-statement.md#certificate-chain-validator) -{% endhint %} + You can customize its behavior to fit the specific needs of your application by modifying the provided factory. Please refer to the dedicated pages for more information. + + * [Counter Checker](advanced-behaviours/authenticator-counter.md) + * [Attestation Statement Support Manager](advanced-behaviours/attestation-and-metadata-statement.md#attestation-statement-support-manager) + * [Extension Output Checker Handler](advanced-behaviours/extensions.md) + * [Algorithm Manager](advanced-behaviours/authenticator-algorithms.md) + * [Metadata Statement Repository](advanced-behaviours/attestation-and-metadata-statement.md#metadata-statement-repository) + * [Status Report Repository](advanced-behaviours/attestation-and-metadata-statement.md#status-report-repository) + * [Certification Chain Validator](advanced-behaviours/attestation-and-metadata-statement.md#certificate-chain-validator) These CSM services are meant to be used by Response Validators. On a similar way, there are two types of validators: * **Authenticator Attestation Response Validator**: used during the creation ceremony * **Authenticator Assertion Response Validator**: used during the request ceremony -## Response Validators +## Response validators The Authenticator Attestation Response Validator and Authenticator Assertion Response Validator services are directly used when receiving Authenticator Responses in order to [register authenticators](authenticator-registration.md) or [authenticate users](authenticate-your-users.md). -{% hint style="info" %} -All null values below correspond to deprecated parameters. They will be removed in 5.0.0 -{% endhint %} +!!! info + + All null values below correspond to deprecated parameters. They will be removed in 5.0.0 -{% code lineNumbers="true" %} -```php +```php linenums="1" check( // 5. Store the credential $credentialRepository->saveCredentialSource($credentialSource); ``` -{% endcode %} -### Authentication Flow +### Authentication flow -{% code lineNumbers="true" %} -```php +```php linenums="1" check( // 5. Update credential (counter may have changed) $credentialRepository->saveCredentialSource($updatedCredential); ``` -{% endcode %} -## Minimal Setup Example +## Minimal setup example Here's a minimal working example with in-memory storage: -{% code lineNumbers="true" %} -```php +```php linenums="1" console.log('Registration failure')) ; ``` -{% endcode %} -### Creation Profile +### Creation profile The `default` [creation profile](../configuration-references.md#creation-profiles) is used. You can change it using the dedicated option. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -137,9 +127,8 @@ webauthn: … profile: custom_profile ``` -{% endcode %} -### Response Handlers +### Response handlers You can customize the responses returned by the controllers by using custom handlers. This could be useful when you want to return additional information to your application. @@ -149,12 +138,11 @@ There are 3 types of responses and handlers: * Success, * Failure. -#### Creation Options Handler +#### Creation options handler This handler is called during the registration of a authenticator and has to implement the interface `Webauthn\Bundle\Security\Handler\CreationOptionsHandler`. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -163,14 +151,12 @@ webauthn: … options_handler: … # Your handler here ``` -{% endcode %} -#### Success Handler +#### Success handler This handler is called when a client sends a valid assertion from the authenticator. This handler shall implement the interface `Webauthn\Bundle\Security\Handler\SuccessHandler`. The default handler is `Webauthn\Bundle\Service\DefaultSuccessHandler`. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -179,14 +165,12 @@ webauthn: … success_handler: … # Your handler here ``` -{% endcode %} -#### Failure Handler +#### Failure handler This handler is called when an error occurred during the process. This handler shall implement the interface `Webauthn\Bundle\Security\Handler\SuccessHandler`. The default handler is `Webauthn\Bundle\Service\DefaultFailureHandler`. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -195,4 +179,3 @@ webauthn: … failure_handler: … # Your handler here ``` -{% endcode %} diff --git a/symfony-bundle/advanced-behaviors/user-verification.md b/docs/symfony-bundle/advanced-behaviors/user-verification.md similarity index 85% rename from symfony-bundle/advanced-behaviors/user-verification.md rename to docs/symfony-bundle/advanced-behaviors/user-verification.md index a6ae284..00bae3f 100644 --- a/symfony-bundle/advanced-behaviors/user-verification.md +++ b/docs/symfony-bundle/advanced-behaviors/user-verification.md @@ -1,9 +1,8 @@ -# User Verification +# User verification The easiest way to manage the user verification requirements in your Symfony application is by using the creation and request profiles. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: … creation_profiles: @@ -18,4 +17,3 @@ webauthn: rp_id: 'example.com' user_verification: !php/const Webauthn\AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED ``` -{% endcode %} diff --git a/symfony-bundle/configuration-references.md b/docs/symfony-bundle/configuration-references.md similarity index 74% rename from symfony-bundle/configuration-references.md rename to docs/symfony-bundle/configuration-references.md index 31d674b..b2c8051 100644 --- a/symfony-bundle/configuration-references.md +++ b/docs/symfony-bundle/configuration-references.md @@ -1,4 +1,4 @@ -# Configuration References +# Configuration references ## Configuration @@ -8,8 +8,7 @@ You may also need to adjust other parameters. If you don’t use Flex, hereafter an example of configuration file: -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: # logger: null # PSR-3 compatible logging service credential_repository: 'Webauthn\Bundle\Repository\DummyPublicKeyCredentialSourceRepository' # CREATE YOUR REPOSITORY AND CHANGE THIS! @@ -61,28 +60,26 @@ webauthn: # status_report_repository: 'App\Repository\StatusReportRepository' # certificate_chain_checker: 'App\Security\CertificateChainChecker' ``` -{% endcode %} -### Creation Profiles +### Creation profiles -{% hint style="success" %} -If you don't create the `creation_profiles` section, a `default` profile is set. -{% endhint %} +!!! success -#### Relying Party (rp) + If you don't create the `creation_profiles` section, a `default` profile is set. + +#### Relying party (rp) The relying Party corresponds to your application. Please refer [to this page](../prerequisites/the-relying-party.md) for more information. -{% hint style="warning" %} -The parameter `id` is optional but highly recommended. -{% endhint %} +!!! warning + + The parameter `id` is optional but highly recommended. -#### Challenge Length +#### Challenge length By default, the length of the challenge is 32 bytes. You may need to select a smaller or higher length. This length can be configured for each profile: -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -90,14 +87,12 @@ webauthn: name: 'ACME Webauthn Server' challenge_length: 16 ``` -{% endcode %} #### Timeout The default timeout is set to 60 seconds (60 000 milliseconds). You can change this value as follows: -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -105,23 +100,21 @@ webauthn: name: 'ACME Webauthn Server' timeout: 30000 ``` -{% endcode %} -{% hint style="info" %} -For v4.0+, the timeout is set to `null`. The values recommended by the specification are as follow: +!!! info -* If the user verification is `discouraged`, timeout should be between 30 and 180 seconds -* If the user verification is `preferred` or `required`, the range is 300 to 600 seconds (5 to 10 minutes) + For v4.0+, the timeout is set to `null`. The values recommended by the specification are as follow: -These behaviors are not necessarily followed by the web browsers. -{% endhint %} + * If the user verification is `discouraged`, timeout should be between 30 and 180 seconds + * If the user verification is `preferred` or `required`, the range is 300 to 600 seconds (5 to 10 minutes) -#### Authenticator Selection Criteria + These behaviors are not necessarily followed by the web browsers. + +#### Authenticator selection criteria This set of options allows you to select authenticators depending on their capabilities. The values are described in [the advanced concepts](../pure-php/advanced-behaviours/authenticator-selection-criteria.md) of the protocol. -{% code title="app/config/webauthn.yaml" lineNumbers="true" fullWidth="false" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -132,22 +125,20 @@ webauthn: require_resident_key: true user_verification: !php/const Webauthn\AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED ``` -{% endcode %} -#### Public Key Credential Parameters +#### Public key credential parameters This option indicates the algorithms allowed for your application. By default, a large list of algorithms is defined, but you can add custom algorithms or reduce the list. -{% hint style="info" %} -The order is important. Preferred algorithms go first. -{% endhint %} +!!! info + + The order is important. Preferred algorithms go first. -{% hint style="warning" %} -It is not recommended changing the default list unless you exactly know what you are doing. -{% endhint %} +!!! warning -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml + It is not recommended changing the default list unless you exactly know what you are doing. + +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -157,18 +148,16 @@ webauthn: - !php/const Cose\Algorithms::COSE_ALGORITHM_ES256 - !php/const Cose\Algorithms::COSE_ALGORITHM_RS256 ``` -{% endcode %} -#### Attestation Conveyance +#### Attestation conveyance If you need the [attestation of the authenticator](../webauthn-in-a-nutshell/attestation-and-metadata-statement.md), you can specify the preference regarding attestation conveyance during credential generation. -{% hint style="warning" %} -The use of Attestation Statements is generally not recommended unless you REALLY need this information. -{% endhint %} +!!! warning + + The use of Attestation Statements is generally not recommended unless you REALLY need this information. -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -176,18 +165,16 @@ webauthn: name: 'ACME Webauthn Server' attestation_conveyance: !php/const Webauthn\PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT ``` -{% endcode %} #### Extensions You can set as many extensions as you want in the profile. Please also [refer to this page](../webauthn-in-a-nutshell/extensions.md) for more information. -{% hint style="info" %} -The example below is totally fictive. Some extensions are [defined in the specification](https://www.w3.org/TR/webauthn/#sctn-defined-extensions) but the support depends on the authenticators, on the browsers and on the relying parties (your applications). -{% endhint %} +!!! info -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml + The example below is totally fictive. Some extensions are [defined in the specification](https://www.w3.org/TR/webauthn/#sctn-defined-extensions) but the support depends on the authenticators, on the browsers and on the relying parties (your applications). + +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: creation_profiles: acme: @@ -197,31 +184,26 @@ webauthn: loc: true txAuthSimple: 'Please add your new authenticator' ``` -{% endcode %} -### Request Profiles +### Request profiles + +!!! success -{% hint style="success" %} -If you don't create the `creation_profiles` section, a `default` profile is set. -{% endhint %} + If you don't create the `creation_profiles` section, a `default` profile is set. The parameters for the request profiles (i.e. the authentication) are very similar to the creation profiles. The only difference is that you don’t need all the detail of the Relying Party, but only its ID (i.e. its domain). -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: request_profiles: acme: rp_id: 'example.com' ``` -{% endcode %} Please note that all parameters are optional. The following configuration is perfectly valid. However, and as mentioned above, the parameter `id` is highly recommended. -{% code title="app/config/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="app/config/webauthn.yaml" linenums="1" webauthn: request_profiles: acme: ~ ``` -{% endcode %} diff --git a/symfony-bundle/entities-with-doctrine-1.md b/docs/symfony-bundle/entities-with-doctrine-1.md similarity index 92% rename from symfony-bundle/entities-with-doctrine-1.md rename to docs/symfony-bundle/entities-with-doctrine-1.md index 2990e83..076119d 100644 --- a/symfony-bundle/entities-with-doctrine-1.md +++ b/docs/symfony-bundle/entities-with-doctrine-1.md @@ -1,6 +1,6 @@ -# User Entity Repository +# User entity repository -## User Entity +## User entity Your application certainly has a user repository. Good news: there is no need to modify it! @@ -10,8 +10,7 @@ This repository should be declared as a Symfony service and shall implement `Web Hereafter an example where the application User Repository is injected. This repository uses Doctrine and provides `findOneBy*` methods. -{% code title="src/Repository/WebauthnUserEntityRepository.php" lineNumbers="true" %} -```php +```php title="src/Repository/WebauthnUserEntityRepository.php" linenums="1" <?php + We must override the method `saveCredentialSource` because we may receive `Webauthn\PublicKeyCredentialSource` objects instead of `App\Entity\WebauthnCredential`. + +```php title="App/Repository/WebauthnCredentialRepository.php" linenums="1" hl_lines="7" +use App\Entity\WebauthnCredential; -use Doctrine\Persistence\ManagerRegistry; +use App\Entity\WebauthnCredential; +use Doctrine\Persistence\ManagerRegistry; use Webauthn\Bundle\Repository\DoctrineCredentialSourceRepository; use Webauthn\PublicKeyCredentialSource; @@ -144,18 +143,15 @@ final class WebauthnCredentialRepository extends DoctrineCredentialSourceReposit parent::saveCredentialSource($publicKeyCredentialSource); } } - - +``` This repository should be declared as a Symfony service. -{% hint style="info" %} -With Symfony autowiring and autoconfiguration, this is usually done automatically -{% endhint %} +!!! info + + With Symfony autowiring and autoconfiguration, this is usually done automatically -{% code title="config/services.yaml" %} -```yaml +```yaml title="config/services.yaml" services: App\Repository\WebauthnCredentialRepository: ~ ``` -{% endcode %} diff --git a/symfony-bundle/firewall.md b/docs/symfony-bundle/firewall.md similarity index 89% rename from symfony-bundle/firewall.md rename to docs/symfony-bundle/firewall.md index 3f9671f..4b3202f 100644 --- a/symfony-bundle/firewall.md +++ b/docs/symfony-bundle/firewall.md @@ -4,14 +4,13 @@ description: How to register and authenticate my users? # Firewall -## Security Bundle +## Security bundle To authenticate or register your users with Symfony, the best and easiest way is to use the Security Bundle. First, install that bundle and follow the instructions given by [the official documentation](https://symfony.com/doc/current/security.html). At the end of the installation and configuration, you should have a `config/packages/security.yaml` file that looks like as follow: -{% code title="config/packages/security.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/security.yaml" linenums="1" security: providers: default: @@ -22,7 +21,6 @@ security: path: 'logout' ... ``` -{% endcode %} ## Controller @@ -88,7 +86,7 @@ Below an example of a login form using the [Stimulus Controller](../symfony-ux/i {% endblock %} ``` -## Login Authenticator +## Login authenticator Next, we need a Symfony Authenticator to handle login form submissions. With Webauthn, we use a dedicated Passport that shall contain a specific Badge. @@ -145,12 +143,11 @@ final class LoginAuthenticator extends WebauthnAuthenticator } ``` -## Security Configuration +## Security configuration To enable the user authentication, you just have to declare the Symfony Authenticator if the appropriate firewall (here `main`). -{% code title="config/packages/security.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/security.yaml" linenums="1" security: providers: default: @@ -164,16 +161,14 @@ security: - { path: ^/login, roles: PUBLIC_ACCESS, requires_channel: 'https'} - { path: ^/logout, roles: PUBLIC_ACCESS} ``` -{% endcode %} -{% hint style="info" %} -**Multiple User Providers (v5.2.1+):** The bundle now supports multiple user providers. If your application uses multiple providers, you can configure them in the security configuration and the WebauthnBadge will work correctly with all of them. -{% endhint %} +!!! info + + **Multiple User Providers (v5.2.1+):** The bundle now supports multiple user providers. If your application uses multiple providers, you can configure them in the security configuration and the WebauthnBadge will work correctly with all of them. Also, you need to define the credential options endpoint. -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -181,13 +176,12 @@ webauthn: login: options_path: '/login/webauthn/options' ``` -{% endcode %} -## User Registration +## User registration TO BE WRITTEN -## Authentication Attributes +## Authentication attributes The security token returned by the firewall sets some attributes depending on the assertion and the capabilities of the authenticator. The attributes are: @@ -196,10 +190,8 @@ The security token returned by the firewall sets some attributes depending on th You can then set constraints to the access controls. In the example below, the /admin path can be reached by users with the role `ROLE_ADMIN` and that **have been verified** during the ceremony. -{% code title="config/packages/security.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/security.yaml" linenums="1" security: access_control: - { path: ^/admin, roles: [ROLE_ADMIN, IS_USER_VERIFIED]} ``` -{% endcode %} diff --git a/symfony-bundle/the-symfony-way.md b/docs/symfony-bundle/the-symfony-way.md similarity index 62% rename from symfony-bundle/the-symfony-way.md rename to docs/symfony-bundle/the-symfony-way.md index 70bcd6c..eef67d4 100644 --- a/symfony-bundle/the-symfony-way.md +++ b/docs/symfony-bundle/the-symfony-way.md @@ -1,12 +1,12 @@ -# Bundle Installation +# Bundle installation This framework provides a Symfony bundle that will help you to use the components within your Symfony application. -{% hint style="info" %} -Implementations for other frameworks and web applications exist. +!!! info -As an example if you use Laravel, you may be interested in [this project: https://github.com/asbiin/laravel-webauthn](https://github.com/asbiin/laravel-webauthn) -{% endhint %} + Implementations for other frameworks and web applications exist. + + As an example if you use Laravel, you may be interested in [this project: https://github.com/asbiin/laravel-webauthn](https://github.com/asbiin/laravel-webauthn) ## Installation @@ -16,16 +16,15 @@ As an example if you use Laravel, you may be interested in [this project: https: composer require web-auth/webauthn-symfony-bundle ``` -{% hint style="info" %} -As the recipes are third party ones not officially supported by Symfony, you will be asked to execute the recipe or not. When applied, the recipes will prompt a message such as `WebAuthn Framework is ready.` -{% endhint %} +!!! info + + As the recipes are third party ones not officially supported by Symfony, you will be asked to execute the recipe or not. When applied, the recipes will prompt a message such as `WebAuthn Framework is ready.` ### Without Symfony Flex If you don't use Symfony Flex, you must register the bundle and route manually. -{% code title="config/bundles.php" lineNumbers="true" %} -```php +```php title="config/bundles.php" linenums="1" ['all' => true], ]; ``` -{% endcode %} -{% code title="config/routes/webauthn.php" lineNumbers="true" %} -```php +```php title="config/routes/webauthn.php" linenums="1" import('.', 'webauthn'); }; ``` -{% endcode %} ## Repositories diff --git a/symfony-ux/additional-authenticators.md b/docs/symfony-ux/additional-authenticators.md similarity index 82% rename from symfony-ux/additional-authenticators.md rename to docs/symfony-ux/additional-authenticators.md index a53f988..8962378 100644 --- a/symfony-ux/additional-authenticators.md +++ b/docs/symfony-ux/additional-authenticators.md @@ -1,8 +1,8 @@ -# Additional Authenticators +# Additional authenticators Users can register multiple authenticators to their account for backup purposes or to use different devices. This page explains how to manage multiple authenticators per user. -## Why Multiple Authenticators? +## Why multiple authenticators? Allowing users to register multiple authenticators provides several benefits: @@ -11,12 +11,11 @@ Allowing users to register multiple authenticators provides several benefits: * **Device upgrades**: Smoothly transition when replacing devices * **Shared accounts**: Family members can each use their own authenticator (if your security policy allows) -## Listing User Authenticators +## Listing user authenticators Display all authenticators registered to the current user: -{% code title="src/Controller/SecurityController.php" lineNumbers="true" %} -```php +```php title="src/Controller/SecurityController.php" linenums="1" {% endblock %} ``` -{% endcode %} -## Adding Additional Authenticators +## Adding additional authenticators Allow authenticated users to register additional authenticators: -{% code title="templates/security/add_authenticator.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/security/add_authenticator.html.twig" linenums="1" {% extends 'base.html.twig' %} {% block body %} @@ -135,18 +130,16 @@ Allow authenticated users to register additional authenticators: {% endblock %} ``` -{% endcode %} -{% hint style="info" %} -The controller configuration for `add_device` should use `CurrentUserEntityGuesser` to automatically get the authenticated user. See the [User Registration](user-registration.md#configuration-for-existing-users) page for configuration details. -{% endhint %} +!!! info -## Removing Authenticators + The controller configuration for `add_device` should use `CurrentUserEntityGuesser` to automatically get the authenticated user. See the [User Registration](user-registration.md#configuration-for-existing-users) page for configuration details. + +## Removing authenticators Allow users to remove authenticators they no longer use: -{% code title="src/Controller/SecurityController.php" lineNumbers="true" %} -```php +```php title="src/Controller/SecurityController.php" linenums="1" #[Route('/security/authenticators/{id}/remove', name: 'app_remove_authenticator')] #[IsGranted('ROLE_USER')] public function removeAuthenticator( @@ -177,23 +170,21 @@ public function removeAuthenticator( return $this->redirectToRoute('app_list_authenticators'); } ``` -{% endcode %} -{% hint style="warning" %} -**Important Security Considerations:** +!!! warning + + **Important Security Considerations:** -* Always verify the authenticator belongs to the current user before removal -* Prevent users from removing their last authenticator (would lock them out) -* Consider requiring re-authentication before removal for sensitive accounts -* Log authenticator additions and removals for security auditing -{% endhint %} + * Always verify the authenticator belongs to the current user before removal + * Prevent users from removing their last authenticator (would lock them out) + * Consider requiring re-authentication before removal for sensitive accounts + * Log authenticator additions and removals for security auditing -## Naming Authenticators +## Naming authenticators Allow users to give friendly names to their authenticators for easier management: -{% code title="src/Entity/WebauthnCredential.php" lineNumbers="true" %} -```php +```php title="src/Entity/WebauthnCredential.php" linenums="1" use Doctrine\ORM\Mapping as ORM; use Webauthn\PublicKeyCredentialSource; @@ -231,12 +222,10 @@ class WebauthnCredential extends PublicKeyCredentialSource // ... other methods } ``` -{% endcode %} Then allow users to set names during or after registration: -{% code title="templates/security/add_authenticator.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/security/add_authenticator.html.twig" linenums="1"
Register New Authenticator
``` -{% endcode %} -## Best Practices +## Best practices -### Encourage Backup Authenticators +### Encourage backup authenticators Prompt users to register a backup authenticator after their first registration: -{% code lineNumbers="true" %} -```twig +```twig linenums="1" {% if credential_count == 1 %}
Add a backup authenticator! @@ -270,9 +257,8 @@ Prompt users to register a backup authenticator after their first registration:
{% endif %} ``` -{% endcode %} -### Authenticator Metadata +### Authenticator metadata Display useful information about each authenticator: @@ -281,14 +267,14 @@ Display useful information about each authenticator: * **Registration date**: Track when each authenticator was added * **AAGUID**: Identify the authenticator model (if available) -### Security Recommendations +### Security recommendations * **Minimum authenticators**: Consider requiring at least 2 authenticators for privileged accounts * **Maximum authenticators**: Limit to prevent abuse (e.g., 10 per user) * **Inactive authenticators**: Automatically remove authenticators not used for a long period * **Notification**: Email users when authenticators are added or removed -## See Also +## See also * [User Registration](user-registration.md) - Register the first authenticator * [User Authentication](integration.md) - Authenticate with any registered authenticator diff --git a/symfony-ux/installation.md b/docs/symfony-ux/installation.md similarity index 87% rename from symfony-ux/installation.md rename to docs/symfony-ux/installation.md index c824be3..ee3a143 100644 --- a/symfony-ux/installation.md +++ b/docs/symfony-ux/installation.md @@ -7,7 +7,7 @@ The [Symfony UX Initiative](https://symfony.com/ux) enables high-interaction app Before installing the Stimulus Controller, you need: 1. **Symfony UX configured** - Follow the [official Symfony UX documentation](https://symfony.com/doc/current/frontend/ux.html) -2. **WebAuthn Bundle installed** - See [Symfony Bundle Installation](the-symfony-way.md) +2. **WebAuthn Bundle installed** - See [Symfony Bundle Installation](../symfony-bundle/the-symfony-way.md) ## Installation @@ -22,16 +22,15 @@ This command will automatically: * Register the Stimulus controller via Symfony Flex * Configure AssetMapper to import the necessary JavaScript files -{% hint style="info" %} -**No build step required!** The package works with Symfony AssetMapper, so you don't need Node.js, npm, yarn, or any JavaScript build tools. The browser imports the JavaScript files directly. -{% endhint %} +!!! info -## Verify Installation + **No build step required!** The package works with Symfony AssetMapper, so you don't need Node.js, npm, yarn, or any JavaScript build tools. The browser imports the JavaScript files directly. + +## Verify installation Check that the Stimulus controller is properly registered in your `assets/controllers.json`: -{% code title="assets/controllers.json" lineNumbers="true" %} -```json +```json title="assets/controllers.json" linenums="1" { "controllers": { "@web-auth/webauthn-stimulus": { @@ -40,9 +39,8 @@ Check that the Stimulus controller is properly registered in your `assets/contro } } ``` -{% endcode %} -## What's Included +## What's included The package provides a Stimulus controller that handles: @@ -52,17 +50,16 @@ The package provides a Stimulus controller that handles: * **Error handling** - Gracefully handles common WebAuthn errors * **Browser autofill** - Supports conditional UI for passkey selection -## Basic Usage +## Basic usage Once installed, you can use the Stimulus controller in your Twig templates with two simple functions: 1. **`stimulus_controller()`** - Attach the controller to your form 2. **`stimulus_action()`** - Trigger WebAuthn operations on button clicks -### Registration Form Example +### Registration form example -{% code title="templates/registration/register.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/registration/register.html.twig" linenums="1"
``` -{% endcode %} -### Authentication Form Example +### Authentication form example -{% code title="templates/security/login.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/security/login.html.twig" linenums="1"
``` -{% endcode %} -## Controller Options +## Controller options The Stimulus controller accepts various configuration options: -### Registration Options +### Registration options | Option | Type | Required | Description | |--------|------|----------|-------------| | `creationOptionsUrl` | string | Yes | URL endpoint that returns `PublicKeyCredentialCreationOptions` | | `creationResultField` | string | Yes | CSS selector for hidden field to store attestation response | -### Authentication Options +### Authentication options | Option | Type | Required | Description | |--------|------|----------|-------------| @@ -128,12 +122,11 @@ The Stimulus controller accepts various configuration options: | `requestResultField` | string | Yes | CSS selector for hidden field to store assertion response | | `useBrowserAutofill` | boolean | No | Enable conditional UI for browser autofill (default: false) | -## Testing Your Installation +## Testing your installation Create a simple test page to verify everything works: -{% code title="templates/test/webauthn.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/test/webauthn.html.twig" linenums="1" {% extends 'base.html.twig' %} {% block body %} @@ -191,11 +184,10 @@ Create a simple test page to verify everything works: {% endif %} {% endblock %} ``` -{% endcode %} ## Troubleshooting -### Controller Not Found +### Controller not found If you see "Controller '@web-auth/webauthn-stimulus' not found": @@ -203,7 +195,7 @@ If you see "Controller '@web-auth/webauthn-stimulus' not found": 2. Clear Symfony cache: `php bin/console cache:clear` 3. Check that AssetMapper is properly configured in `config/packages/asset_mapper.yaml` -### JavaScript Errors +### JavaScript errors If you see console errors: @@ -212,7 +204,7 @@ If you see console errors: 3. Verify WebAuthn routes are properly configured in `config/routes/webauthn.yaml` 4. Ensure the controller is loaded by checking the browser's network tab -### Assets Not Loading +### Assets not loading If the Stimulus controller doesn't load: @@ -225,7 +217,7 @@ If the Stimulus controller doesn't load: 2. Check that Stimulus is properly configured in your application -## HTTPS Requirement +## HTTPS requirement WebAuthn **requires HTTPS** to function. For development, use the Symfony CLI for automatic HTTPS: @@ -236,7 +228,7 @@ symfony serve For production, ensure HTTPS is properly configured on your web server. -## Next Steps +## Next steps Now that the Stimulus Controller is installed, proceed to: @@ -244,9 +236,9 @@ Now that the Stimulus Controller is installed, proceed to: 2. **[User Authentication](integration.md)** - Implement login flows 3. **[Additional Authenticators](additional-authenticators.md)** - Allow users to register backup devices -## See Also +## See also * [Symfony UX Documentation](https://symfony.com/doc/current/frontend/ux.html) - Official UX guide * [Stimulus Documentation](https://stimulus.hotwired.dev/) - Stimulus framework reference -* [WebAuthn Bundle](the-symfony-way.md) - Backend configuration +* [WebAuthn Bundle](../symfony-bundle/the-symfony-way.md) - Backend configuration * [JavaScript Integration](../prerequisites/javascript.md) - Manual JavaScript implementation diff --git a/symfony-ux/integration.md b/docs/symfony-ux/integration.md similarity index 98% rename from symfony-ux/integration.md rename to docs/symfony-ux/integration.md index 2f26e7c..7ab533b 100644 --- a/symfony-ux/integration.md +++ b/docs/symfony-ux/integration.md @@ -1,4 +1,4 @@ -# User Authentication +# User authentication With the version 5.2 of the bundle, the login process is very similar to the username/password login. @@ -54,7 +54,7 @@ The `requestResultField` parameter corresponds to the selector to the hidden fie The behavior after the login is managed by your Symfony Security Authenticator. -### Browser Autofill +### Browser autofill When authenticators are available on the device and the browser is aware of them, you can simplify the way users will sign in. When this feature is enabled, the user will see the list of available authenticators when focusing on the username field. Selecting an account in the list will automatically perform the authentication actions. There is a simple option to enable this feature: diff --git a/symfony-ux/user-registration.md b/docs/symfony-ux/user-registration.md similarity index 78% rename from symfony-ux/user-registration.md rename to docs/symfony-ux/user-registration.md index d2e9796..00026c3 100644 --- a/symfony-ux/user-registration.md +++ b/docs/symfony-ux/user-registration.md @@ -1,20 +1,19 @@ -# User Registration +# User registration The Symfony UX WebAuthn package provides Stimulus controllers to simplify the registration of new authenticators. This page explains how to register a new user with WebAuthn or add authenticators to an existing user account. -## New User Registration +## New user registration When registering a new user, you need to collect basic information (username, display name) and then register their first authenticator. -### Registration Form +### Registration form Your registration form needs: 1. A **username field** for the user's identifier 2. A **display name field** (optional, can default to username) 3. A **hidden field** for the attestation response -{% code title="templates/registration/register.html.twig" lineNumbers="true" %} -```twig +```twig title="templates/registration/register.html.twig" linenums="1"
``` -{% endcode %} -### Bundle Configuration +### Bundle configuration Configure the bundle to handle new user registration: -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: credential_repository: 'App\Repository\WebauthnCredentialRepository' user_repository: 'App\Repository\UserRepository' @@ -72,14 +69,12 @@ webauthn: result_path: '/register/webauthn' user_entity_guesser: 'App\Guesser\NewUserEntityGuesser' ``` -{% endcode %} -### User Entity Guesser +### User entity guesser Create a guesser to handle new user registration: -{% code lineNumbers="true" %} -```php +```php linenums="1" ``` -{% endcode %} -### Configuration for Existing Users +### Configuration for existing users -{% code title="config/packages/webauthn.yaml" lineNumbers="true" %} -```yaml +```yaml title="config/packages/webauthn.yaml" linenums="1" webauthn: controllers: enabled: true @@ -157,13 +148,12 @@ webauthn: result_path: '/devices/add' user_entity_guesser: 'Webauthn\Bundle\Security\Guesser\CurrentUserEntityGuesser' ``` -{% endcode %} -{% hint style="success" %} -The `CurrentUserEntityGuesser` automatically retrieves the currently authenticated user from the Symfony security context. No custom guesser needed! -{% endhint %} +!!! success + + The `CurrentUserEntityGuesser` automatically retrieves the currently authenticated user from the Symfony security context. No custom guesser needed! -## Stimulus Controller Options +## Stimulus controller options The registration controller accepts the following options: @@ -173,12 +163,11 @@ The registration controller accepts the following options: | `creationResultField` | string | Yes | CSS selector for the hidden attestation field | | `autoRegister` | boolean | No | Automatically trigger registration on page load (default: false) | -### Auto-Register Example +### Auto-register example Automatically start the registration process when the page loads: -{% code lineNumbers="true" %} -```twig +```twig linenums="1" {{ stimulus_controller('@web-auth/webauthn-stimulus', { autoRegister: true, @@ -187,18 +176,16 @@ Automatically start the registration process when the page loads: } ) }} ``` -{% endcode %} -{% hint style="warning" %} -Use `autoRegister` carefully as it immediately prompts the user for biometric authentication or security key interaction. -{% endhint %} +!!! warning + + Use `autoRegister` carefully as it immediately prompts the user for biometric authentication or security key interaction. -## Error Handling +## Error handling Handle registration errors in your custom event listeners: -{% code lineNumbers="true" %} -```javascript +```javascript linenums="1" document.addEventListener('webauthn:registration:error', (event) => { console.error('Registration failed:', event.detail); alert('Could not register authenticator: ' + event.detail.message); @@ -208,9 +195,8 @@ document.addEventListener('webauthn:registration:success', (event) => { console.log('Registration successful!'); }); ``` -{% endcode %} -## See Also +## See also * [User Authentication](integration.md) - Authenticate users with registered authenticators * [Additional Authenticators](additional-authenticators.md) - Manage multiple authenticators diff --git a/the-project/contributing.md b/docs/the-project/contributing.md similarity index 100% rename from the-project/contributing.md rename to docs/the-project/contributing.md diff --git a/the-project/installation.md b/docs/the-project/installation.md similarity index 100% rename from the-project/installation.md rename to docs/the-project/installation.md diff --git a/the-project/the-project.md b/docs/the-project/the-project.md similarity index 83% rename from the-project/the-project.md rename to docs/the-project/the-project.md index 370be55..19d3414 100644 --- a/the-project/the-project.md +++ b/docs/the-project/the-project.md @@ -10,7 +10,7 @@ The complete specification can be found on [the W3C dedicated page](https://www. This framework contains PHP libraries and Symfony bundle to allow developers to integrate that authentication mechanism into their web applications. -## Class, Constant and Property Names +## Class, constant and property names Naming things may be complicated. That’s why the following rule applies on the whole framework: the name of classes, constants and properties are identical to the ones you will find in the specification. @@ -46,15 +46,15 @@ As an example, the [section 5.2.2 “Web Authentication Assertion”](https://ww * Supported (not fully tested) * appid extension (compatibility with FIDO U2F authenticator -## Compatible Authenticators +## Compatible authenticators As of January 2023, our framework supports every authenticator with full feature and algorithm support, achieving a 100% success rate across all tests. Official FIDO Alliance testing tools have validated its compliance. We ensure continuous conformity through rigorous unit and functional testing throughout the development process. -{% hint style="info" %} -See [https://github.com/herrjemand/awesome-webauthn/pull/61](https://github.com/herrjemand/awesome-webauthn/pull/61) and [https://github.com/herrjemand/awesome-webauthn#server-libs](https://github.com/herrjemand/awesome-webauthn?#server-libraries) -{% endhint %} +!!! info + + See [https://github.com/herrjemand/awesome-webauthn/pull/61](https://github.com/herrjemand/awesome-webauthn/pull/61) and [https://github.com/herrjemand/awesome-webauthn#server-libs](https://github.com/herrjemand/awesome-webauthn?#server-libraries) ## Support @@ -70,6 +70,6 @@ If you feel comfortable writing code, you could try to fix [opened issues where Do not forget to follow [these best practices](contributing.md). -{% hint style="danger" %} -If you think you have found a security issue, **DO NOT open an issue**. [You MUST submit your issue here](https://github.com/web-auth/webauthn-framework/security). -{% endhint %} +!!! danger + + If you think you have found a security issue, **DO NOT open an issue**. [You MUST submit your issue here](https://github.com/web-auth/webauthn-framework/security). diff --git a/the-project/web-browser-support.md b/docs/the-project/web-browser-support.md similarity index 95% rename from the-project/web-browser-support.md rename to docs/the-project/web-browser-support.md index cb573e6..a0bb641 100644 --- a/the-project/web-browser-support.md +++ b/docs/the-project/web-browser-support.md @@ -2,7 +2,7 @@ description: Adoption by web browsers --- -# Web Browser Support +# Web browser support Webauthn is now supported by all main web browsers: diff --git a/webauthn-in-a-nutshell/attestation-and-metadata-statement.md b/docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md similarity index 68% rename from webauthn-in-a-nutshell/attestation-and-metadata-statement.md rename to docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md index 265a90c..e0f1675 100644 --- a/webauthn-in-a-nutshell/attestation-and-metadata-statement.md +++ b/docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md @@ -1,14 +1,14 @@ -# Metadata Statement +# Metadata statement -{% hint style="info" %} -This page is about an advanced feature. You can skip it if you are new with Webauthn -{% endhint %} +!!! info -{% hint style="danger" %} -Disclaimer: you **should not** ask for the Attestation Statement unless you want to play with it or you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...). -{% endhint %} + This page is about an advanced feature. You can skip it if you are new with Webauthn -## Attestation Statement +!!! danger + + Disclaimer: you **should not** ask for the Attestation Statement unless you want to play with it or you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...). + +## Attestation statement During the Attestation Ceremony (i.e. the registration of the authenticator), you can ask for the Attestation Statement of the authenticator. The Attestation Statements have one of the following types: @@ -18,12 +18,12 @@ During the Attestation Ceremony (i.e. the registration of the authenticator), yo * **Attestation CA** (`AttCA`): Authenticators are based on a Trusted Platform Module (TPM). They can generate multiple attestation identity key pairs (AIK) and requests an Attestation CA to issue an AIK certificate for each. * **Anonymization CA** (`AnonCA`): Authenticators use an Anonymization CA, which dynamically generates per-credential attestation certificates such that the attestation statements presented to Relying Parties do not provide uniquely identifiable information. -## Metadata Statement +## Metadata statement The Metadata Statements are issued by the manufacturers of the authenticators. These statements contain details about the authenticators (supported algorithms, biometric capabilities...) and all the necessary information to verify the Attestation Statements generated during the attestation ceremony. There are several possible sources to get these Metadata Statements. The main source is the [FIDO Alliance Metadata Service](https://fidoalliance.org/metadata) that allows fetching statements on-demand, but some of them may be provided by other means. -{% hint style="danger" %} -The FIDO Alliance Metadata Service provides a limited number of Metadata Statements. It is mandatory to get the statement from the manufacturer of your authenticators otherwise the Attestation Statement won't be verified and the Attestation Ceremony will fail. -{% endhint %} +!!! danger + + The FIDO Alliance Metadata Service provides a limited number of Metadata Statements. It is mandatory to get the statement from the manufacturer of your authenticators otherwise the Attestation Statement won't be verified and the Attestation Ceremony will fail. diff --git a/webauthn-in-a-nutshell/authenticators.md b/docs/webauthn-in-a-nutshell/authenticators.md similarity index 74% rename from webauthn-in-a-nutshell/authenticators.md rename to docs/webauthn-in-a-nutshell/authenticators.md index 5213a4c..57fa60f 100644 --- a/webauthn-in-a-nutshell/authenticators.md +++ b/docs/webauthn-in-a-nutshell/authenticators.md @@ -8,27 +8,30 @@ An Authenticator is a cryptographic entity used to generate a public key credent Authenticators may have additional features such as PIN code or biometric sensors (fingerprint, facial recognition…) that offer user verification. -![USB device with fingerprint reader](<../.gitbook/assets/images (1).jpeg>) +![USB device with fingerprint reader](../assets/images/images-1.jpeg) -## Roaming Authenticators +## Roaming authenticators The roaming authenticator may have different forms. The most common form is a USB device the user plugs into its computer. It can be a paired Bluetooth device or a card with NFC capabilities. Authenticators of this class are removable from, and can "roam" among, client devices. -![Webauthn compatible devices](../.gitbook/assets/fido2.jpeg) +![Webauthn compatible devices](../assets/images/fido2.jpeg) -## Platform Authenticators +## Platform authenticators A platform authenticator is usually not removable from the client device. For example an Android smartphone or a Windows 10 computer with the associated security chips can act as an authenticator. -![Android screenshot](<../.gitbook/assets/webauthn-android-fennec (1).png>) +![Android screenshot](../assets/images/webauthn-android-fennec-1.png) -![Fingerprint reader on a laptop](../.gitbook/assets/master.jpg) +![Fingerprint reader on a laptop](../assets/images/master.jpg) ### Passkey -

Passkey Logo

+
+ ![Passkey Logo](../assets/images/fido-passkey-mark-a-black-e1702581853266.png){ width="188" } +
Passkey Logo
+
Passkey are virtual authenticators that have extended capabilities and in particular: diff --git a/webauthn-in-a-nutshell/ceremonies.md b/docs/webauthn-in-a-nutshell/ceremonies.md similarity index 84% rename from webauthn-in-a-nutshell/ceremonies.md rename to docs/webauthn-in-a-nutshell/ceremonies.md index ec989d9..d7f1543 100644 --- a/webauthn-in-a-nutshell/ceremonies.md +++ b/docs/webauthn-in-a-nutshell/ceremonies.md @@ -6,11 +6,11 @@ description: Registration and Authentication process overview In WebAuthn, authentication workflows are called "ceremonies" - a term that emphasizes the formal, cryptographic nature of these operations. There are two main ceremonies that form the foundation of WebAuthn authentication. -## The Two Ceremonies +## The two ceremonies WebAuthn defines two distinct ceremonies: -### 1. Attestation Ceremony (Registration) +### 1. Attestation ceremony (registration) **Also called:** Creation Ceremony, Registration Ceremony @@ -21,7 +21,7 @@ WebAuthn defines two distinct ceremonies: * Adding an additional authenticator to an existing account * Registering a backup security key -### 2. Assertion Ceremony (Authentication) +### 2. Assertion ceremony (authentication) **Also called:** Request Ceremony, Authentication Ceremony, Login Ceremony @@ -32,11 +32,11 @@ WebAuthn defines two distinct ceremonies: * Step-up authentication for sensitive operations * Re-authentication after session timeout -## Common Workflow Pattern +## Common workflow pattern Both ceremonies follow a similar two-step pattern: -### Step 1: Options Creation +### Step 1: Options creation The Relying Party (your server) creates options that: * Define parameters for the operation @@ -44,7 +44,7 @@ The Relying Party (your server) creates options that: * Specify requirements and preferences * Are sent to the browser/authenticator -### Step 2: Response Verification +### Step 2: Response verification The authenticator: * Prompts the user for interaction @@ -52,22 +52,21 @@ The authenticator: * Returns a signed response * The server validates the response -{% hint style="info" %} -User interaction varies based on authenticator capabilities and your configuration. It can range from a simple button touch to biometric authentication (fingerprint, facial recognition, PIN code). -{% endhint %} +!!! info -## Attestation Ceremony (Registration) + User interaction varies based on authenticator capabilities and your configuration. It can range from a simple button touch to biometric authentication (fingerprint, facial recognition, PIN code). + +## Attestation ceremony (registration) The attestation ceremony registers a new authenticator credential for a user. -![The attestation ceremony](<../.gitbook/assets/registration (2).png>) +![The attestation ceremony](../assets/images/registration-2.png) -### Attestation Flow Breakdown +### Attestation flow breakdown -#### Server Side (Step 1) +#### Server side (step 1) -{% code lineNumbers="true" %} -```php +```php linenums="1" // 1. Create PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create( rp: $rpEntity, // Your application identity @@ -83,9 +82,8 @@ $session->set('webauthn_creation_challenge', $publicKeyCredentialCreationOptions // 3. Send options to browser as JSON return new JsonResponse($publicKeyCredentialCreationOptions); ``` -{% endcode %} -#### Client Side (Browser) +#### Client side (browser) ```javascript // 1. Receive options from server @@ -103,10 +101,9 @@ await fetch('/webauthn/register', { }); ``` -#### Server Side (Step 2) +#### Server side (step 2) -{% code lineNumbers="true" %} -```php +```php linenums="1" // 1. Load the attestation response $publicKeyCredential = $serializer->deserialize( $request->getContent(), @@ -124,9 +121,8 @@ $credentialSource = $attestationValidator->check( // 3. Save the new credential $credentialRepository->saveCredentialSource($credentialSource); ``` -{% endcode %} -### What Happens During Attestation? +### What happens during attestation? 1. **Challenge Generation**: Server creates a random challenge to prevent replay attacks 2. **User Prompt**: Browser asks user to interact with authenticator @@ -135,7 +131,7 @@ $credentialRepository->saveCredentialSource($credentialSource); 5. **Verification**: Server verifies signatures and stores the public key 6. **Storage**: Credential is saved and associated with the user account -### Attestation Data Contains +### Attestation data contains * **Public Key**: Used to verify future signatures * **Credential ID**: Unique identifier for this credential @@ -143,18 +139,17 @@ $credentialRepository->saveCredentialSource($credentialSource); * **Signature Counter**: Used to detect cloned authenticators * **Attestation Statement**: Optional proof of authenticator authenticity -## Assertion Ceremony (Authentication) +## Assertion ceremony (authentication) The assertion ceremony authenticates a user with a previously registered credential. -![The assertion ceremony](../.gitbook/assets/login.png) +![The assertion ceremony](../assets/images/login.png) -### Assertion Flow Breakdown +### Assertion flow breakdown -#### Server Side (Step 1) +#### Server side (step 1) -{% code lineNumbers="true" %} -```php +```php linenums="1" // 1. Get user's registered credentials $credentials = $credentialRepository->findAllForUserEntity($userEntity); $allowedCredentials = array_map( @@ -175,9 +170,8 @@ $session->set('webauthn_request_challenge', $publicKeyCredentialRequestOptions-> // 4. Send options to browser return new JsonResponse($publicKeyCredentialRequestOptions); ``` -{% endcode %} -#### Client Side (Browser) +#### Client side (browser) ```javascript // 1. Receive options from server @@ -195,10 +189,9 @@ await fetch('/webauthn/login', { }); ``` -#### Server Side (Step 2) +#### Server side (step 2) -{% code lineNumbers="true" %} -```php +```php linenums="1" // 1. Load the assertion response $publicKeyCredential = $serializer->deserialize( $request->getContent(), @@ -226,9 +219,8 @@ $credentialRepository->saveCredentialSource($updatedCredential); // 5. Log the user in $session->set('user_id', $userEntity->id); ``` -{% endcode %} -### What Happens During Assertion? +### What happens during assertion? 1. **Challenge Generation**: Server creates a new random challenge 2. **Credential Selection**: Browser identifies available credentials @@ -238,14 +230,14 @@ $session->set('user_id', $userEntity->id); 6. **Counter Check**: Server verifies counter to detect cloning 7. **Authentication**: If valid, user is logged in -### Assertion Data Contains +### Assertion data contains * **Authenticator Data**: Flags, counter, extensions * **Client Data**: Origin, challenge, type * **Signature**: Cryptographic proof using the private key * **User Handle**: Optional user identifier (for usernameless authentication) -## Key Differences Between Ceremonies +## Key differences between ceremonies | Aspect | Attestation (Registration) | Assertion (Authentication) | |--------|---------------------------|---------------------------| @@ -257,14 +249,13 @@ $session->set('user_id', $userEntity->id); | **Validator Class** | `AuthenticatorAttestationResponseValidator` | `AuthenticatorAssertionResponseValidator` | | **User Experience** | "Register security key" | "Use security key to sign in" | -## Security Considerations +## Security considerations -### Challenge Uniqueness +### Challenge uniqueness Both ceremonies require a unique, random challenge: -{% code lineNumbers="true" %} -```php +```php linenums="1" // Always generate a fresh challenge $challenge = random_bytes(32); // 32 bytes = 256 bits of entropy @@ -272,14 +263,12 @@ $challenge = random_bytes(32); // 32 bytes = 256 bits of entropy // NEVER use predictable challenges (like timestamps) // NEVER skip challenge verification ``` -{% endcode %} -### Challenge Storage +### Challenge storage Store challenges temporarily in server-side sessions: -{% code lineNumbers="true" %} -```php +```php linenums="1" // Store during Step 1 (options creation) $session->set('webauthn_challenge', base64_encode($challenge)); $session->set('webauthn_challenge_expires', time() + 60); // 60 second timeout @@ -292,14 +281,12 @@ if (time() > $session->get('webauthn_challenge_expires')) { throw new \Exception('Challenge expired'); } ``` -{% endcode %} -### Origin Validation +### Origin validation Always verify the origin matches your domain: -{% code lineNumbers="true" %} -```php +```php linenums="1" // Server must verify origin during validation $credentialSource = $attestationValidator->check( $publicKeyCredential->response, @@ -307,18 +294,17 @@ $credentialSource = $attestationValidator->check( 'https://example.com' // Must match your actual domain ); ``` -{% endcode %} -## Common Pitfalls +## Common pitfalls -### ❌ Challenge Reuse +### ❌ Challenge reuse ```php // WRONG: Reusing the same challenge $challenge = 'my-static-challenge'; // Never do this! ``` -### ❌ Skipping Verification +### ❌ Skipping verification ```php // WRONG: Trusting client data without validation @@ -326,7 +312,7 @@ $credentialId = $_POST['credentialId']; $user = $userRepository->find($credentialId); // DON'T! ``` -### ❌ Incorrect Origin +### ❌ Incorrect origin ```php // WRONG: Verifying with wrong domain @@ -334,14 +320,13 @@ $attestationValidator->check($response, $options, 'http://localhost'); // But the actual domain is https://example.com ``` -## Best Practices +## Best practices -### ✅ Use Timeouts +### ✅ Use timeouts Set reasonable timeouts for user interaction: -{% code lineNumbers="true" %} -```php +```php linenums="1" $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create( rp: $rpEntity, user: $userEntity, @@ -349,14 +334,12 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create timeout: 60000 // 60 seconds in milliseconds ); ``` -{% endcode %} -### ✅ Handle Errors Gracefully +### ✅ Handle errors gracefully Provide helpful error messages: -{% code lineNumbers="true" %} -```php +```php linenums="1" try { $credentialSource = $attestationValidator->check(/* ... */); } catch (\Webauthn\Exception\InvalidDataException $e) { @@ -366,22 +349,19 @@ try { ], 400); } ``` -{% endcode %} -### ✅ Store Credential Metadata +### ✅ Store credential metadata Keep track of when and how credentials were registered: -{% code lineNumbers="true" %} -```php +```php linenums="1" $credential->createdAt = new \DateTimeImmutable(); $credential->lastUsedAt = null; $credential->name = 'My YubiKey'; // Allow users to name their keys $credential->transports = ['usb', 'nfc']; // Store transport hints ``` -{% endcode %} -## See Also +## See also * [Authenticators](authenticators.md) - Learn about different authenticator types * [User Verification](user-verification.md) - Understand UP vs UV flags diff --git a/webauthn-in-a-nutshell/extensions.md b/docs/webauthn-in-a-nutshell/extensions.md similarity index 81% rename from webauthn-in-a-nutshell/extensions.md rename to docs/webauthn-in-a-nutshell/extensions.md index 69b84f2..f8c1ee3 100644 --- a/webauthn-in-a-nutshell/extensions.md +++ b/docs/webauthn-in-a-nutshell/extensions.md @@ -1,12 +1,12 @@ # Extensions -{% hint style="info" %} -This page is about an advanced feature. You can skip it if you are new with WebAuthn. -{% endhint %} +!!! info + + This page is about an advanced feature. You can skip it if you are new with WebAuthn. WebAuthn extensions are optional features that extend the core WebAuthn specification to support additional use cases and capabilities. They allow authenticators and browsers to provide extra functionality beyond basic authentication. -## What Are Extensions? +## What are extensions? Extensions provide a standardized way to: @@ -15,20 +15,20 @@ Extensions provide a standardized way to: * Enable specific features like credential protection or large blob storage * Maintain backward compatibility with legacy systems (like U2F) -## How Extensions Work +## How extensions work Extensions work in both directions: 1. **Extension Inputs** - Sent from the Relying Party to the authenticator via the browser 2. **Extension Outputs** - Returned from the authenticator to the Relying Party after processing -{% hint style="warning" %} -**Important**: Authenticators may ignore extension inputs if they don't support them. Extension outputs are **not guaranteed** and your application should handle their absence gracefully. -{% endhint %} +!!! warning + + **Important**: Authenticators may ignore extension inputs if they don't support them. Extension outputs are **not guaranteed** and your application should handle their absence gracefully. -## Common WebAuthn Extensions +## Common WebAuthn extensions -### Standard Extensions +### Standard extensions The most commonly used WebAuthn extensions include: @@ -49,7 +49,7 @@ All standard extensions are listed in the **IANA WebAuthn Extensions Registry**: [https://www.iana.org/assignments/webauthn/webauthn.xhtml](https://www.iana.org/assignments/webauthn/webauthn.xhtml) -## Library Support +## Library support This library provides: @@ -57,16 +57,15 @@ This library provides: * ✅ **AppID extension** - Built-in support for U2F backward compatibility * ⚠️ **Custom extensions** - You need to implement extension handlers for other extensions -{% hint style="warning" %} -Most extensions require custom implementation. It is up to you to create the extension handlers depending on which extensions you want to support. -{% endhint %} +!!! warning -## Basic Usage Example + Most extensions require custom implementation. It is up to you to create the extension handlers depending on which extensions you want to support. + +## Basic usage example Here's how to add an extension input when creating credential options: -{% code lineNumbers="true" %} -```php +```php linenums="1" response->getAuthenticatorData()->extensions; @@ -103,16 +100,14 @@ if (isset($extensionOutputs['credProps']['rk'])) { echo $isDiscoverable ? 'Resident key created' : 'Non-resident key'; } ``` -{% endcode %} -## Practical Use Cases +## Practical use cases -### 1. U2F Migration (appid Extension) +### 1. U2F migration (appid extension) Allow existing U2F credentials to work with your WebAuthn implementation: -{% code lineNumbers="true" %} -```php +```php linenums="1" $extensions = AuthenticationExtensions::create([ AuthenticationExtension::create('appid', 'https://example.com') ]); @@ -122,14 +117,12 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( extensions: $extensions ); ``` -{% endcode %} -### 2. Credential Properties (credProps Extension) +### 2. Credential properties (credProps extension) Determine if a credential supports resident keys (usernameless authentication): -{% code lineNumbers="true" %} -```php +```php linenums="1" $extensions = AuthenticationExtensions::create([ AuthenticationExtension::create('credProps', true) ]); @@ -137,14 +130,12 @@ $extensions = AuthenticationExtensions::create([ // After registration, check: $rk = $extensionOutputs['credProps']['rk'] ?? false; ``` -{% endcode %} -### 3. Large Blob Storage (largeBlob Extension) +### 3. Large blob storage (largeBlob extension) Store additional encrypted data with the credential: -{% code lineNumbers="true" %} -```php +```php linenums="1" // Write blob during registration $extensions = AuthenticationExtensions::create([ AuthenticationExtension::create('largeBlob', [ @@ -153,18 +144,16 @@ $extensions = AuthenticationExtensions::create([ ]) ]); ``` -{% endcode %} -### 4. Pseudo-Random Function (prf Extension) +### 4. Pseudo-random function (prf extension) + +!!! info -{% hint style="info" %} -PRF extension support was added in version 5.2.0 -{% endhint %} + PRF extension support was added in version 5.2.0 The PRF (Pseudo-Random Function) extension allows you to generate cryptographic keys from authenticator secrets. This is particularly useful for encryption scenarios: -{% code lineNumbers="true" %} -```php +```php linenums="1" // Request PRF during authentication $extensions = AuthenticationExtensions::create([ AuthenticationExtension::create('prf', [ @@ -179,11 +168,10 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( extensions: $extensions ); ``` -{% endcode %} -## Implementation Considerations +## Implementation considerations -### Check Extension Support +### Check extension support Not all authenticators support all extensions. Always: @@ -191,7 +179,7 @@ Not all authenticators support all extensions. Always: 2. Have fallback behavior when extensions aren't supported 3. Don't rely on extensions for critical security features -### Custom Extension Handlers +### Custom extension handlers For extensions beyond `appid`, you'll need to implement: @@ -200,7 +188,7 @@ For extensions beyond `appid`, you'll need to implement: See the [Pure PHP Extensions Guide](../pure-php/advanced-behaviours/extensions.md) or [Symfony Bundle Extensions Guide](../symfony-bundle/advanced-behaviors/extensions.md) for detailed implementation instructions. -## When to Use Extensions +## When to use extensions Use extensions when you need: @@ -215,7 +203,7 @@ Avoid extensions if: * ❌ You need universal compatibility (not all devices support extensions) * ❌ You're just getting started with WebAuthn -## See Also +## See also * [Pure PHP Extensions Implementation](../pure-php/advanced-behaviours/extensions.md) - Detailed extension implementation * [Symfony Extensions Configuration](../symfony-bundle/advanced-behaviors/extensions.md) - Framework-specific setup diff --git a/webauthn-in-a-nutshell/user-verification.md b/docs/webauthn-in-a-nutshell/user-verification.md similarity index 88% rename from webauthn-in-a-nutshell/user-verification.md rename to docs/webauthn-in-a-nutshell/user-verification.md index 2c17c37..64fe9a0 100644 --- a/webauthn-in-a-nutshell/user-verification.md +++ b/docs/webauthn-in-a-nutshell/user-verification.md @@ -2,11 +2,11 @@ description: Prove to me who you claim to be! --- -# User Verification +# User verification User verification is a WebAuthn feature that confirms the identity of the actual user, not just possession of the authenticator. It answers the question: "Is the person using this authenticator really the authorized user?" -## What is User Verification? +## What is user verification? User verification requires the user to prove their identity through: @@ -17,7 +17,7 @@ User verification requires the user to prove their identity through: The goal is to **distinguish individual users** and ensure that the person authenticating is the legitimate account owner, not just someone who stole or found the authenticator. -## User Presence vs User Verification +## User presence vs user verification It's important to understand the difference: @@ -33,7 +33,7 @@ It's important to understand the difference: **User Verification** goes further by confirming the specific identity of that person. -## User Verification Requirements +## User verification requirements You can configure the user verification requirement when creating credential options. There are three possible values: @@ -41,8 +41,7 @@ You can configure the user verification requirement when creating credential opt The operation **requires** user verification and will **fail** if the authenticator does not set the `UV` flag. -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\PublicKeyCredentialCreationOptions; @@ -57,7 +56,6 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create authenticatorSelection: $authenticatorSelection ); ``` -{% endcode %} **Use when:** * High-security applications (banking, healthcare) @@ -68,13 +66,11 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create The operation **prefers** user verification but will **not fail** if the authenticator doesn't provide it. -{% code lineNumbers="true" %} -```php +```php linenums="1" $authenticatorSelection = AuthenticatorSelectionCriteria::create( userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED ); ``` -{% endcode %} This is the **recommended default** for most applications. It uses user verification when available but doesn't exclude authenticators that don't support it. @@ -87,13 +83,11 @@ This is the **recommended default** for most applications. It uses user verifica The operation **discourages** user verification to minimize disruption to the user flow. -{% code lineNumbers="true" %} -```php +```php linenums="1" $authenticatorSelection = AuthenticatorSelectionCriteria::create( userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED ); ``` -{% endcode %} **Use when:** * Low-risk operations @@ -101,12 +95,11 @@ $authenticatorSelection = AuthenticatorSelectionCriteria::create( * Simple presence verification is sufficient * Testing or demonstration purposes -## Checking the UV Flag +## Checking the UV flag After authentication or registration, verify whether user verification actually occurred: -{% code lineNumbers="true" %} -```php +```php linenums="1" // During assertion validation $authenticatorData = $publicKeyCredential->response->getAuthenticatorData(); @@ -123,14 +116,12 @@ if ($userVerified) { $flags = $authenticatorData->getFlags(); $uvFlag = ($flags & 0x04) !== 0; // UV flag is bit 2 ``` -{% endcode %} -## Practical Examples +## Practical examples -### Example 1: High-Security Banking App +### Example 1: High-security banking app -{% code lineNumbers="true" %} -```php +```php linenums="1" // Require user verification for all operations $authenticatorSelection = AuthenticatorSelectionCriteria::create( userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED @@ -144,12 +135,10 @@ $options = PublicKeyCredentialCreationOptions::create( authenticatorSelection: $authenticatorSelection ); ``` -{% endcode %} -### Example 2: General Web Application +### Example 2: General web application -{% code lineNumbers="true" %} -```php +```php linenums="1" // Prefer user verification but don't require it $authenticatorSelection = AuthenticatorSelectionCriteria::create( userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED @@ -163,21 +152,18 @@ $options = PublicKeyCredentialCreationOptions::create( authenticatorSelection: $authenticatorSelection ); ``` -{% endcode %} -### Example 3: Fast Re-authentication +### Example 3: Fast re-authentication -{% code lineNumbers="true" %} -```php +```php linenums="1" // For quick re-auth on already logged-in user, discourage UV $options = PublicKeyCredentialRequestOptions::create( challenge: random_bytes(32), userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED ); ``` -{% endcode %} -## Device Capabilities +## Device capabilities Different authenticators have different user verification capabilities: @@ -190,28 +176,27 @@ Different authenticators have different user verification capabilities: | **YubiKey Bio** | Fingerprint | | **Basic security keys** | None (only user presence) | -{% hint style="info" %} -When you set `userVerification: required`, only authenticators capable of user verification will be available to the user during registration or authentication. -{% endhint %} +!!! info -## Best Practices + When you set `userVerification: required`, only authenticators capable of user verification will be available to the user during registration or authentication. -### For Registration +## Best practices + +### For registration 1. **Use `preferred` as default** - Balances security and compatibility 2. **Use `required` for sensitive accounts** - Banking, healthcare, admin accounts 3. **Inform users** - Explain that biometric/PIN improves security -### For Authentication +### For authentication 1. **Match registration settings** - If registered with `required`, authenticate with `required` 2. **Allow `preferred` for convenience** - Users can choose faster auth if device supports it 3. **Document your policy** - Make clear what level of verification is expected -### Conditional Logic +### Conditional logic -{% code lineNumbers="true" %} -```php +```php linenums="1" // Require UV only for privileged operations $isAdminAction = ($user->getRole() === 'ROLE_ADMIN'); @@ -224,9 +209,8 @@ $options = PublicKeyCredentialRequestOptions::create( userVerification: $userVerification ); ``` -{% endcode %} -## Common Issues +## Common issues ### Issue: "User verification not supported" @@ -240,19 +224,18 @@ $options = PublicKeyCredentialRequestOptions::create( **Solution**: Use `discouraged` or `preferred` for routine operations, save `required` for sensitive actions. -## Security Considerations +## Security considerations * ✅ **Higher assurance** - UV provides stronger identity verification than UP alone * ✅ **Phishing resistant** - Even if an attacker steals the authenticator, they need the PIN/biometric * ⚠️ **Not foolproof** - Biometric systems can have false positives; PINs can be observed * ⚠️ **Device dependent** - Relying solely on UV limits compatible devices -## Constants Reference +## Constants reference The library provides convenient constants in the `AuthenticatorSelectionCriteria` class: -{% code lineNumbers="true" %} -```php +```php linenums="1" use Webauthn\AuthenticatorSelectionCriteria; // For registration (attestation) @@ -263,9 +246,8 @@ AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED // These same constants can be used for authentication (assertion) // in PublicKeyCredentialRequestOptions ``` -{% endcode %} -## See Also +## See also * [Authenticators](authenticators.md) - Learn about different authenticator types * [Authenticator Selection Criteria](../pure-php/advanced-behaviours/authenticator-selection-criteria.md) - Detailed configuration diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a118efe --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,234 @@ +site_name: WebAuthn Framework +site_description: WebAuthn and passkeys for PHP and Symfony. Registration and authentication ceremonies, the Symfony bundle and the Symfony UX components, after WebAuthn Level 3. +site_url: https://webauthn-doc.spomky-labs.com/ +site_author: Spomky-Labs +repo_url: https://github.com/web-auth/webauthn-framework +repo_name: web-auth/webauthn-framework +edit_uri: https://github.com/web-auth/doc/edit/5.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 + - redirects: + redirect_maps: {} + +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 · WebAuthn 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..63282d1 --- /dev/null +++ b/site-root/CNAME @@ -0,0 +1 @@ +webauthn-doc.spomky-labs.com