Liferay–Keycloak SSO Integration via OpenID Connect
Define the architecture,
configuration, and rollout plan for delegating authentication in a Liferay
portal to Keycloak using OpenID Connect (OIDC), with Liferay acting as the
OpenID Relying Party (RP) and Keycloak as the central Identity Provider (IdP).
●
Covers Keycloak realm/client setup, Liferay OIDC
configuration, user provisioning, and testing.
●
Does not cover Keycloak clustering/HA, LDAP/AD
federation into Keycloak, or Liferay theme/UI customization of the login screen
(call out separately if needed).
Open Questions (to confirm before implementation)
|
# |
Question |
Why it
matters |
|
1 |
Liferay
edition/version — CE 7.4, DXP 7.3, or DXP 7.4? |
DXP has
additional admin UI options and support SLAs; CE 7.4 config is assumed below
unless told otherwise |
|
2 |
Single realm
shared across environments, or per-environment (dev/stage/prod) Keycloak
realms? |
Affects client
registration count and secret rotation process |
|
3 |
Is role/group
mapping between Keycloak and Liferay required (e.g. Keycloak realm roles →
Liferay Site Roles)? |
Needs custom
claim mapping in Keycloak + Liferay OIDC role mapper |
|
4 |
Will Liferay's
local login form remain available alongside OIDC, or is OIDC the only sign-in
path? |
Determines
whether “Enable OpenID Connect” fully replaces the default form or just adds
a button |
|
5 |
New users only,
or does this need to migrate/merge existing Liferay accounts by email match? |
Liferay
auto-provisions new accounts on first OIDC login; existing accounts need a
matching-strategy decision |
Architecture Overview
Key design point: Liferay is
registered as a confidential client, meaning the authorization-code → token
exchange happens server-to-server (Liferay backend to Keycloak token endpoint),
not in the browser. The client secret never reaches the browser.
Component Design
Keycloak — Client Registration
|
Setting |
Value |
|
Realm |
liferay-kc-demo
(dedicated realm recommended over master) |
|
Client ID |
liferay-portal-client |
|
Client type |
OpenID Connect |
|
Access Type |
ON
(confidential) |
|
Standard
Flow Enabled (Authorization
Code) |
Enabled |
|
Direct access
grants |
Enabled (not
needed for browser SSO) |
|
Root URL |
https://<liferay-domain>
in my case ->(http://localhost:9070) |
|
Valid redirect
URIs |
https://<liferay-domain>/*
(narrow to the actual callback path in production, e.g. /c/portal/login) |
|
Web origins |
https://<liferay-domain> |
|
|
|
User attribute prerequisite:
every Keycloak user intended to log into Liferay must have first name, last
name, and email populated — Liferay's OIDC auto-provisioning fails without
these.
Keycloak — Discovery Metadata
Discovery document location:
https://<keycloak-domain>/auth/realms/<realm>/.well-known/openid-configuration
This exposes
authorization_endpoint, token_endpoint, jwks_uri, userinfo_endpoint, and
issuer. Preferred integration path is to feed this single URL to Liferay
(Option A below) rather than hand-copying each endpoint.
Liferay — OIDC Provider Configuration
Location: Control Panel →
Instance Settings → Security → SSO → OpenID Connect
|
Field |
Value |
|
Enabled |
Yes |
|
Provider Name |
Keycloak (label
shown on the sign-in screen) |
|
Client ID |
liferay-portal-client |
|
Client Secret |
(from Keycloak
Credentials tab) |
|
Scopes |
openid profile
email — profile is mandatory for Liferay to pull name/user details |
Endpoint configuration — two options
●
Option A (preferred): Discovery Endpoint. Paste the
.well-known/openid-configuration URL(localhost:9093/auth/realms/liferay-kc-demo/.well-known/openid-configuration);
Liferay auto-fetches authorization/token/jwks/userinfo endpoints. Lower
maintenance — survives Keycloak endpoint changes automatically.
●
Option B (manual): explicit endpoints, only if the
Liferay server cannot reach the discovery URL outbound (e.g. network/firewall
restriction):
○
Authorization:
https://<keycloak-domain>/realms/<realm>/protocol/openid-connect/auth
○
Token:
https://<keycloak-domain>/realms/<realm>/protocol/openid-connect/token
○
Issuer:
https://<keycloak-domain>/realms/<realm>
○
JWKS:
https://<keycloak-domain>/realms/<realm>/protocol/openid-connect/certs
○
Userinfo:
https://<keycloak-domain>/realms/<realm>/protocol/openid-connect/userinfo
User Provisioning Behavior
●
First successful OIDC login creates a local Liferay
User record, populated from the profile/email claims.
●
Subsequent logins reuse the same record, matched by the
Keycloak subject/email depending on Liferay's configured matching strategy.
●
If open question #5 (existing account migration)
applies, this needs explicit handling — Liferay does not merge by email
automatically in all versions; confirm behavior on the target version before
go-live.
Role/Claim Mapping (if in scope — see open question #3)
If Keycloak realm roles need to
drive Liferay Site Roles/Organizations:
●
Add a Keycloak Client Scope with a role-list mapper,
add it to the client's assigned scopes.
●
On the Liferay side, this typically requires either a
custom OpenIdConnectUserInfoProcessor / portal property extension (DXP) or a
Liferay-side event listener that reads the roles claim from the ID token on
login and syncs Site Role membership. This is the piece most likely to need
custom code — flag for a follow-up design note once question #3 is answered.
Security Considerations
●
Client is confidential — secret must be stored in
Liferay's config (not source control) and rotated on the same cadence as other
service credentials.
●
Restrict Valid Redirect URIs to the exact callback path
in production rather than a wildcard, to reduce open-redirect risk.
●
Enforce HTTPS on both Liferay and Keycloak endpoints —
no plaintext token exchange.
●
Consider token lifetime / session timeout alignment
between Keycloak (SSO session max) and Liferay (portal session timeout) so
users aren't logged out of one but not the other.
●
If DPP2's existing Keycloak instance is reused for this
realm, keep the Liferay client isolated in its own realm or client to avoid
scope creep with payment-platform credentials.
Testing / Validation Plan
●
Unit config check — verify discovery URL resolves and
returns expected endpoints from the Liferay server's network (not just a
browser).
●
Happy path — new Keycloak user, first login, incognito
browser: confirm redirect to Keycloak, successful auth, redirect back, and
correct local user auto-provisioned in Liferay with name/email populated.
●
Repeat login — same user logs in a second time: confirm
no duplicate account is created.
●
Negative case — Keycloak user missing first/last name
or email: confirm the expected failure behavior and error messaging, so it can
be documented for support.
●
Role mapping (if in scope) — confirm a Keycloak role
change is reflected in Liferay Site Role membership on next login.
●
Fallback — if local Liferay login must remain available
(open question #4), confirm it still works alongside the OIDC button.
Rollout Plan
|
Phase |
Action |
|
1 |
Stand
up/confirm Keycloak realm and register the Liferay client in a non-prod
environment |
|
2 |
Configure
Liferay OIDC in staging, run the test plan (Section 7) |
|
3 |
Confirm
role-mapping approach (if applicable) with a small pilot group |
|
4 |
Document
rollback plan (disable OIDC toggle reverts to local login without data loss) |
|
5 |
Promote
configuration to production with narrowed redirect URIs and rotated secret |
Assumptions
●
Target is Liferay 7.4 (CE or DXP — pending
confirmation, see open question #1); steps above are version-agnostic for OIDC
but admin UI paths may shift slightly between CE and DXP.
●
A dedicated Keycloak realm (not master) will be used
for this integration.
● Outbound HTTPS from the Liferay server to the Keycloak discovery/token/JWKS endpoints is permitted by network policy.