Multi-provider external authentication for Plone, built on authlib and joserfc.
One canonical Plone user id maps to many external identities β GitHub, Google, another Plone site, any OpenID Connect provider, an emailed magic link β for the same human, without running a separate identity broker.
- Identity linking. One account, many providers. The identity key is
(provider, subject), and an identity already linked to somebody is never silently re-attached: a collision is a hard error, not a merge. - Providers configured through the web. A control panel with a form generated from each driver's published schema. Client secrets are write-only through every API surface, including GenericSetup export.
- Magic-link sign-in. Single-use signed tokens, at most fifteen minutes, rate limited per address and per IP, answering identically for known and unknown addresses.
- An audit log. Successes and refusals, per user or site-wide, bounded and purged on write. IP and user agent are off by default.
- A documented event contract, which is what the audit log, the profile machinery and your own integrations all consume. Nothing reaches into anything else.
- Content-backed profiles and groups, with user properties, enumeration and group membership served entirely from a dedicated catalog β no content object is woken to answer them, and the test suite asserts that rather than claiming it.
- An authorization server, optionally. The
[server]extra makes the site an OAuth 2.1 and OpenID Connect provider in its own right, so one Plone site can be where the others sign in. - Core installs alone.
uv add pas.plugins.identitywith no extras is a tested configuration, enforced in CI by an import-linter contract.
pas.plugins.authomatic is the long-standing multi-provider option, built on the authomatic library, which is no longer maintained upstream. This package is a candidate successor for those sites.
pas.plugins.oidc does one OIDC provider, and does it well. If that is what you need, it is the smaller and more mature dependency; there is no reason to move.
The difference is linking. Neither of the above maps several external identities onto one canonical Plone user id, and that mapping is what this package is arranged around. Migrations from both have shipped: they are dry-run by default, idempotent, and report what they would do before you let them do it. See Migrating from pas.plugins.authomatic and Migrating from pas.plugins.oidc for what each can and cannot recover.
For how this compares with Products.membrane β a similar problem solved a different way β see About users as content.
Full documentation lives in docs/ and is published at collective.github.io/pas-plugins-identity.
- Start here: Install, then Configure a provider.
- Tutorial: Two Plone sites, one login β build a federation end to end.
- How-to guides: write a driver, register an OAuth client, read the audit log, export and import principals.
- Concepts: identities, the two layers, users as content, secrets, federation.
- Reference: shipped drivers, events, claims, security guarantees.
Both packages are installed separately. The backend one is enough on its own; the frontend one requires it.
Requires Plone 6.2 and Python 3.12 or later.
uv add pas.plugins.identityFor the authorization server as well:
uv add "pas.plugins.identity[server]"Then install pas.plugins.identity from the add-ons control panel, and configure a provider in Site Setup > Identity providers.
Requires Volto 19. The add-on is developed against 19.3.0.
Add @plone-collective/volto-identity to your package.json.
"addons": [
"@plone-collective/volto-identity"
],
"dependencies": {
"@plone-collective/volto-identity": "*"
}Important
Adding the package to dependencies without listing it under addons installs the code but never registers it, so nothing is rendered.
- An operating system that runs all the requirements mentioned.
- uv
- nvm
- Node.js and pnpm 24
- Make
- Git
- Docker (optional)
-
Clone this repository, then change your working directory.
git clone [email protected]:collective/pas-plugins-identity.git cd pas-plugins-identity
-
Install this code base.
make install
-
Create a new Plone site on your first run.
make backend-create-site
-
Start the backend at http://localhost:8080/.
make backend-start
-
In a new shell session, start the frontend at http://localhost:3000/.
make frontend-start
Voila! Your Plone site should be live and kicking! π
Deploy a local Docker Compose environment that includes the following.
- Docker images for Backend and Frontend πΌοΈ
- A stack with a Traefik router and a ZEO server ποΈ
- Accessible at http://pas-plugins-identity.localhost π
Run the following commands in a shell session.
make stack-create-site
make stack-startAnd... you're all set! Your Plone site is up and running locally! π
A second stack runs two Plone sites and signs one into the other, which is the fastest way to see what this package is for.
It runs on a single PostgreSQL rather than a ZEO server, with a database each for the two sites' ZODBs and a third for authentication records. Only the identity provider writes its records there; the relying party keeps the bounded log inside its own plugin, so the two arrangements are visible side by side.
make demo-stack-startRead the tutorial for what to click once it is up.
This monorepo consists of the following distinct sections:
- backend: The Plone add-on
pas.plugins.identity, installed with uv, plus its test suite and the demo stack's own package. - frontend: The Volto add-on
@plone-collective/volto-identity, plus its Storybook stories. - docs: The Sphinx and MyST documentation published at collective.github.io/pas-plugins-identity.
- Both halves of the add-on live together, so a change to a REST API payload and the change to the component that reads it are one commit.
- GitHub Workflows are triggered per section, so a documentation change does not rebuild the frontend (refer to .github/workflows).
- The documentation is built from the same checkout as the code it describes, so a reference page and the source it documents cannot drift between repositories.
To check your code against quality standards, run the following shell command.
make checkTo format and rewrite the code base, ensuring it adheres to quality standards, run the following shell command.
make format| Section | Tool | Description | Configuration |
|---|---|---|---|
| backend | Ruff | Python code formatting, imports sorting | backend/pyproject.toml |
| backend | zpretty |
XML and ZCML formatting | -- |
| frontend | ESLint | Fixes most common frontend issues | frontend/.eslintrc.js |
| frontend | prettier | Format JS and Typescript code | frontend/.prettierrc |
| frontend | Stylelint | Format Styles (css, less, sass) | frontend/.stylelintrc |
Formatters can also be run within the backend or frontend folders.
To check the code base without rewriting it, run the following shell command.
make lint| Section | Tool | Description | Configuration |
|---|---|---|---|
| backend | Ruff | Checks code formatting, imports sorting | backend/pyproject.toml |
| backend | Pyroma | Checks Python package metadata | -- |
| backend | check-python-versions | Checks Python version information | -- |
| backend | zpretty |
Checks XML and ZCML formatting | -- |
| frontend | ESLint | Checks JS / Typescript lint | frontend/.eslintrc.js |
| frontend | prettier | Check JS / Typescript formatting | frontend/.prettierrc |
| frontend | Stylelint | Check Styles (css, less, sass) formatting | frontend/.stylelintrc |
Linters can be run individually within the backend or frontend folders.
The backend also enforces its own layering. make check-imports, from backend/, runs import-linter contracts asserting that core never imports the optional server layer, so the no-extras install stays a tested configuration rather than a claim.
Generate translation files for Plone and Volto with ease:
make i18nThis repository holds two packages, released separately and each under its own license.
| Package | Location | Registry | License |
|---|---|---|---|
pas.plugins.identity |
backend/ | PyPI | GPL-2.0-only |
@plone-collective/volto-identity |
frontend/ | npm | MIT |
The backend package is usable on its own: it needs no frontend to authenticate a user against an external provider, link an identity, or serve any of it over the REST API. The frontend package requires the backend, because everything it renders comes from what the backend serves.
Generated using Cookieplone (2.0.0b3) and cookieplone-templates (91c8455) on 2026-08-20 18:32:15.036687. A special thanks to all contributors and supporters!