Multi-provider external authentication for Plone.
The frontend package for multi-provider external authentication in Plone: signing in through an external provider, managing the identities linked to your account, and the pages a user and a group are. See also the backend package pas.plugins.identity, which this package requires.
Everything here renders from what the backend serves. The add-on describes no schema of its own: a provider form is generated from the driver's published schema, and this package supplies components where Volto has none.
- Sign-in screen — the login page offers every provider the backend says to show, in the order it gives, with the provider's own icon and colours. Plone's username and password form is offered alongside, or not, depending on
RAZZLE_IDENTITY_SHOW_PLONE_LOGIN. - Callback route —
/login-identitycompletes the flow the provider redirects back into, and routes onward by what the backend answered rather than by guessing. - Sign-in methods page —
/identitieslists the providers linked to your account, links another, and unlinks one. The addresses on your Profile are managed here too, and verifying one is what enables a magic link to it. - Authorized applications —
/applicationsis the other direction: the applications you signed in to, rather than the providers you signed in with. Only a site running the backend's[server]layer has any. - Consent screen —
/oauth-consent, for a site whose authorization server asks the frontend to render the question rather than its own standalone page. - First-login gate — a just-signed-in user whose Profile is incomplete is routed to
/first-loginbefore anywhere else, so a site can require what the provider did not supply. - Profile and group views —
UserProfileandUserGroupare content, and without this they render through Volto's default view: a title over an empty body, because neither type has rich text. Registered by portal type, so a site filing users under its own type keeps its own view. - Two control panels — identity providers and OAuth clients, each with the configlet icon and label the backend registers, so they sit in the control-panel listing looking like the panels beside them rather than like two things that failed to install.
- User account review — an administrator's view of one account: its linked identities, its addresses, and its audit trail in one place.
- User menu and toolbar — personal information, preferences, sign-in methods and applications reachable from where a user already looks for them.
- A
provider_iconwidget — the one widget this add-on registers, because it is the one Volto does not have. Which widget a field uses is the backend's decision, declared on the field itself; Volto looks the name up and finds this one.
| Environment variable | Default | Effect |
|---|---|---|
RAZZLE_IDENTITY_SHOW_PLONE_LOGIN |
off | Whether Plone's own username and password form appears alongside the providers. |
1, true, yes and on switch it on; anything else switches it off, and an unset variable means the default.
The check is deliberately not Boolean(value), which reads the string "false" as true and would leave the password form up for an operator who had just switched it off.
The form is off by default: a site installing this add-on has external providers, and offering a password form beside them invites people to make a second way into the same account.
A site that is itself an identity provider is the case that wants it on, and sets config.settings.identity.showPloneLogin in its own configuration. The environment variable still wins over that.
Every setting the add-on reads is under config.settings.identity: showPloneLogin, and avatarColors, the palette a user's initials are drawn on when they have no portrait. The shipped palette was chosen for contrast against the white initials; a palette you configure is not checked.
This variable is read at run time, not baked into the bundle. That is why the Login component reads it through runtimeConfig rather than writing process.env.RAZZLE_IDENTITY_SHOW_PLONE_LOGIN literally: webpack substitutes the literal form while pnpm build runs, which would make the answer a property of the image and force two images on two sites wanting two answers.
Three of Volto's own components are shadowed, in each case because Volto offers nothing to extend at that point.
| Shadowed | Why Volto could not be extended instead |
|---|---|
Toolbar |
toolbar-personal is a DOM id on a button, not a pluggable, so the icon it draws cannot be replaced from outside. That icon is the generic user.svg, which says somebody is signed in rather than who. |
PersonalTools |
The only pluggable is toolbar-user-menu, at the end of the menu list. The avatar block above it cannot be touched, and the entries themselves cannot be reordered or removed at all. |
Controlpanels/Users/RenderUsers |
The row's Edit action is built inline with no extension point, and led to the wrong form on the wrong store for every user whose fields live in a Profile, which is most of them. |
None of those files holds an implementation.
Each is a docstring saying why the path is shadowed, plus a one-line re-export of a component under components/, which is where the code, its tests and its stories live.
A shadowed path is a wiring decision, not somewhere to keep code nothing can import by name.
Full documentation is published at collective.github.io/pas-plugins-identity, and its source lives in docs/ at the repository root.
The pages closest to this package are Configure a provider, Review a user account, and the federation tutorial, which walks two Plone sites into trusting each other.
Every component this add-on ships is in Storybook, which is the fastest way to see one without a running Plone site.
This add-on requires Volto 19 and pas.plugins.identity installed on the Plone site.
It is developed against Volto 19.3.0; see the frontend install guide for the full requirement table.
Add @plone-collective/volto-identity to your package.json.
"addons": [
"@plone-collective/volto-identity"
],
"dependencies": {
"@plone-collective/volto-identity": "*"
}Name it in both keys. Adding the package to dependencies without listing it under addons installs the code but never registers it, so nothing is rendered.
Visit http://localhost:3000/ in a browser, login, and check the awesome new features.
The development of this add-on is done in isolation using pnpm workspaces, the latest mrs-developer, and other Volto core improvements.
For these reasons, it only works with pnpm and Volto 19.
- An operating system that runs all the requirements mentioned.
- nvm
- Node.js and pnpm 24
- Make
- Git
- Docker (optional)
-
Clone this repository, then change your working directory.
git clone git@github.com:collective/pas-plugins-identity.git cd pas-plugins-identity/frontend -
Install this code base.
make install
Run make help to list the available Make commands.
Install package requirements.
make installStart the backend.
make backend-docker-startIn a separate terminal session, start the frontend.
make startRun ESlint, Prettier, and Stylelint in analyze mode.
make lintRun ESlint, Prettier, and Stylelint in fix mode.
make formatExtract the i18n messages to locales.
make i18nRun unit tests.
make testStart Storybook on port 6006.
make storybook-startBuild the static site, as CI does before publishing it:
make storybook-buildEvery component this add-on ships has stories, and a .stories.tsx beside a component is as expected here as a .test.tsx.
Stories use Component Story Format 3, and payloads come from src/stories/fixtures.tsx, which holds provider lists, identities and audit entries shaped exactly like the backend serves them.
Reuse those rather than inventing a payload, so a story that renders is evidence the component handles the real contract.
Two decorators in src/stories/, beside the fixtures, cover what these components need from their surroundings.
withPagesupplies the router and store a page-level component reads.withUserMenuputs a component in the chrome it actually appears in, which is the only way to see a menu entry rendered where a user meets it.
Run each of these steps in separate terminal sessions.
In the first session, start the frontend in development mode.
make acceptance-frontend-dev-startIn the second session, start the backend acceptance server.
make acceptance-backend-startIn the third session, start the Cypress interactive test runner.
make acceptance-testThe project is licensed under the MIT license.