Keycloak SSO

Adds Keycloak single sign-on to Payload CMS 3 via an OIDC auth strategy with PKCE, silent token refresh, and permission-based access control.

Community 0 16/wk MIT v1.0.1

Installation

pnpm add payload-keycloak

About

This plugin adds Keycloak single sign-on to Payload CMS 3. It registers a `keycloak` auth strategy that verifies Keycloak access tokens offline against JWKS and the issuer, fetches `/userinfo` (cached per token), and attaches a permissions claim to `req.user`. Payload's local auth strategy is disabled, so there are no local users or passwords; users are created on first login by their Keycloak `sub`, and `email`, `name`, and `keycloakSub` fields are added to the auth collection when missing. A first-login create race against the unique `keycloakSub` index is retried as a lookup, so parallel initial requests do not silently fail. Admin login uses the Authorization Code flow with PKCE (S256) against a public Keycloak client. The default `/admin/login` view redirects straight to Keycloak through a `beforeLogin` server component, and the callback at `/api/auth/callback` sets the session cookies and returns to `/admin`. An expired access token in the cookie is silently refreshed using the refresh-token cookie, so Payload's inactivity timers follow the Keycloak SSO session; `/api/users/me` reports the refresh token's `exp`, and `/api/users/refresh-token` and `/api/users/logout` are wired up. Logout clears the cookies and hands off to Keycloak's end-session endpoint with an `id_token_hint`. Permissions are read from a userinfo claim (default `permissions`) as nested booleans, for example `{ posts: { edit: true } }`. The helpers `authenticated`, `can('posts.edit')`, `withKeycloakAccess`, `withKeycloakGlobalAccess`, `withAuth`, and `permissionsOf` compose those rules on top of each collection's existing access rather than replacing it. Configuration requires `url`, `realm`, `clientId`, and `serverURL`; other options include `basePath`, `usersSlug`, `scope`, `allowedOrigins`, `permissionsClaim`, `userInfoTtl`, `internalHosts`, `messages`, and `enabled`. The only runtime dependency is `jose`. Notable limits: `aud` is not validated, so access is gated with permissions instead. The userinfo cache and refresh coalescing live in process memory, which suits a single replica. Keycloak's "Revoke Refresh Token" must be off, because admin pages are server-rendered without `canSetHeaders` and cannot write rotated cookies back. The access token is a Keycloak realm credential, so the plugin forces `auth.removeTokenFromResponses` and keeps it in the httpOnly cookie, never in the `me` or `refresh-token` JSON body.

Package info

Package name
payload-keycloak
Latest version
1.0.1
Unpacked size
118 kB
License
MIT
Weekly downloads
16
Last publish
Sep 16, 2026

Similar plugins

More in Auth