Skip to content

Replace OpenID 2.0 with OAuth 2.0/OIDC login - #155

Open
mraible wants to merge 7 commits into
feature/jakarta-ee-10-migrationfrom
feature/oidc-login
Open

Replace OpenID 2.0 with OAuth 2.0/OIDC login#155
mraible wants to merge 7 commits into
feature/jakarta-ee-10-migrationfrom
feature/oidc-login

Conversation

@mraible

@mraible mraible commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Stacked on #154 (1/3 in the stack); after it merges, this retargets to master. Replaces the obsolete OpenID 2.0 authentication (removed in Spring Security 6) with OAuth 2.0/OIDC login using spring-security-oauth2-client. Phase 3 of the Jakarta EE 10 migration (ROL-2183).

Removed / changed behavior — read before merging:

  • The openid and db-openid values of authentication.method no longer exist; configs using them must move to oidc or db-oidc. OpenID 2.0 relay support does not survive in any form (the protocol is dead and Spring Security dropped it upstream).
  • users.oidc.autoProvision.enabled defaults to false, matching users.ldap.autoProvision.enabled: only pre-created accounts, or accounts the provider links via a verified matching email, can sign in until it is enabled. The docker demo turns it on (override with OIDC_AUTO_PROVISION=false). Linking to an existing account always requires the provider to assert a verified email matching that account.
  • Security note: users.firstUserAdmin would have handed the admin role to whichever provider user reached a fresh install first. Auto-provisioned accounts no longer keep that bootstrap grant unless the provider asserts an admin role claim or the new users.oidc.firstUserAdmin property (default false) is enabled; the revocation is logged with the alternatives.
  • authentication.method is now enforced server side, not just in the UI: under db, ldap, and cma no provider registrations are served and the OIDC flow is rejected outright, and under pure oidc the form login refuses password lookups. db-oidc allows both.
  • A confidential client without a client-secret is rejected at configuration time with a clear log message; oidc.{id}.client-authentication-method=none supports public clients using PKCE.
  • Provider discovery is cached per provider with a bounded retry backoff, so one unreachable identity provider does not block the others or turn login-page rendering into repeated discovery attempts.
  • OAuth2/OIDC failures redirect with their own error=oidc marker, so the login page reports them as provider failures instead of a wrong password.
  • docker-compose now defaults AUTHENTICATION_METHOD to oidc, so the demo signs in through the bundled Keycloak by default; set db for the old form-login demo or db-oidc for both side by side.
  • This adds OAuth 2.0 for browser login only. It does not restore the AtomPub API's removed OAuth 1.0a option (see ROL-2183: Migrate from javax to Jakarta EE 11 #154); AtomPub remains basic/wsse.

How it works:

  • RollerClientRegistrationRepository reads provider config (client-id, client-secret, issuer-uri) from Roller properties, with OIDC discovery at startup
  • RollerOidcUserService resolves the Roller account behind an authenticated OIDC user via the existing openIdUrl column (stores issuer#sub) and provisions accounts just in time, so the principal carries Roller roles from the first request
  • The returned principal is named after the resolved Roller account. Roller looks users up by principal name throughout (rendering models, filters, Struts actions), and the default OidcUser name is the opaque sub claim, which made every rendered weblog page fail with a Velocity error for signed-in OIDC users
  • AuthMethod gains OIDC/DB_OIDC, and Login.jsp shows provider buttons built from the resolved registrations, so it never advertises a provider whose discovery failed
  • docker-compose gains Keycloak seeded with admin/admin and user/user, and the image builds from the working tree instead of a cloned release tag
  • 19 unit tests cover the new classes, including regressions for the principal name and the provisioning policy

To try it: add 127.0.0.1 keycloak to /etc/hosts, then docker compose up -d and log in at http://localhost:8080/ as admin/admin or user/user.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces obsolete OpenID 2.0 browser authentication with OAuth 2.0/OIDC, including account linking, provisioning, provider discovery, and local Keycloak support.

Changes:

  • Adds OIDC login, Roller principal/role integration, and tests.
  • Updates authentication configuration and login UI.
  • Adds a Keycloak-backed Docker Compose demo.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
Dockerfile Builds Roller from the working tree.
.dockerignore Excludes local build data.
docker-compose.yml Adds Keycloak and OIDC defaults.
docker/entry-point.sh Writes OIDC runtime properties.
docker/realm-config/roller-realm.json Seeds the demo realm and users.
docker/realm-config/keycloak-health-check.sh Checks Keycloak readiness.
app/pom.xml Adds OAuth2/OIDC dependencies.
AuthMethod.java Replaces OpenID authentication modes.
RollerContext.java Exposes registrations and adjusts PBKDF2 compatibility.
RollerSession.java Resolves sessions by OIDC subject.
RollerClientRegistrationRepository.java Discovers configured OIDC providers.
RollerOidcUserService.java Links and provisions Roller users.
RollerOAuth2SuccessHandler.java Redirects successful OIDC logins.
Login.java Supplies provider data to the login page.
Register.java Handles OIDC external-auth registration.
Profile.java Updates OIDC profile behavior.
ProfileBean.java Exposes issuer and subject fields.
UserEdit.java Updates OIDC administration behavior.
security.xml Configures Spring Security OIDC login.
Login.jsp Renders provider sign-in buttons.
roller.properties Documents OIDC configuration and provisioning.
ApplicationResources.properties Adds OIDC UI messages.
RollerOidcUserServiceTest.java Tests linking, provisioning, and roles.
RollerOAuth2SuccessHandlerTest.java Tests post-login redirects.
RollerClientRegistrationRepositoryTest.java Tests registration configuration handling.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread app/src/main/webapp/WEB-INF/security.xml
Comment thread app/src/main/java/org/apache/roller/weblogger/ui/struts2/core/Login.java Outdated
Comment thread .dockerignore
Comment thread app/src/main/resources/ApplicationResources.properties
Replace the obsolete OpenID 2.0 authentication (removed in Spring Security 6) with modern OAuth 2.0/OIDC login using spring-security-oauth2-client. OIDC providers are configured via roller-custom.properties with issuer discovery. The existing openIdUrl column is reused to store OIDC subjects (formatted as issuer#sub), avoiding schema changes. New OIDC users are redirected to registration with claims pre-populated; returning users go straight to the menu. AuthMethod enum values updated from OPENID/DB_OPENID to OIDC/DB_OIDC.
…est users

The Dockerfile previously cloned the roller-6.1.0 tag from GitHub, so local changes never made it into the image; it now builds from the checkout. The Keycloak realm import gains an administrator (admin/admin) and a regular user (user/user) so both roles can be tried right after docker compose up, and the compose file accepts AUTHENTICATION_METHOD=db-oidc to offer form login next to the provider buttons.
…form registration

Roller looks users up by principal name throughout the rendering layer and servlet filters, but an OidcUser's default name is the sub claim, an opaque provider ID that matches no Roller account; every rendered weblog page then failed with a Velocity NullPointerException for a signed-in OIDC user. The principal returned by RollerOidcUserService is now named after the resolved Roller account. Provisioning moves into the user service so the principal carries Roller roles from the first request, gets its own users.oidc.autoProvision.enabled toggle (default true, mirroring users.ldap.autoProvision.enabled) instead of piggybacking on the runtime form-registration setting that is off by default and blocked every provider user after the first, and requires a verified matching email before linking an existing account. Discovery needs jackson-databind on the classpath, and the login page lists providers from the resolved client registrations so it never advertises one whose discovery failed.
…IDC provisioning

Hiding login forms and provider buttons is not an authentication control, so both sides of the filter chain now check authentication.method: the registration repository serves no providers unless the method is oidc or db-oidc (closing /oauth2/authorization/* under db, ldap, and cma), the OIDC user service rejects the flow outright, and the user details service refuses password lookups in pure oidc mode.

users.firstUserAdmin would hand the admin role to whichever provider user reached a fresh install first, so auto-provisioned accounts no longer keep the bootstrap grant unless an admin role claim is asserted or the new users.oidc.firstUserAdmin property is enabled; the revocation is logged with the bootstrap alternatives.

Provider discovery is now cached per provider with a bounded retry backoff, so one unreachable identity provider no longer blocks the others or turns every login page render into a discovery attempt. A confidential client without a client-secret is rejected at configuration time with a pointer to client-authentication-method=none for PKCE public clients. OAuth2 failures redirect with error=oidc so the login page stops diagnosing every provider failure as a wrong password. All raised by Copilot review.
…d auth methods

These views still compared against the removed OPENID and DB_OPENID values and requested message keys deleted with them, so under the OIDC methods their password controls never rendered and the registration script never enabled submission. They now branch on OIDC and DB_OIDC, drop the OpenID URL entry fields, and show the provider-assigned identity read-only. The equivalent logic already existed one level up in the Bootstrap 5 UI conversion, which is why the stacked browser tests passed; it belongs at this level. Raised by Copilot review.
users.ldap.autoProvision.enabled has always shipped false, and the OIDC equivalent should not be more permissive: with the default on, everyone the identity provider authenticates received a Roller account. The docker entry point turns provisioning on when a provider is configured (override with OIDC_AUTO_PROVISION=false) so the Keycloak demo and CI matrix keep working, and the unit test configuration enables it so the provisioning path stays exercised.
…by code review

The profile and admin user editors rendered the federated identity disabled, and disabled inputs are never submitted while both actions still validate and copy the posted value: under oidc no profile or user could ever be saved, and under db-oidc every save silently wiped the account's provider link. The profile shows the identity read-only (submitted), and the admin editor makes it editable, which is also the remediation the account_link_required error asks for. Profile.jsp also still requested the renamed noPasswordForOidc message key.

Upgrades from the OpenID era now boot: authentication.method values openid and db-openid map to their OIDC replacements with a warning instead of throwing on every request. Linking sees disabled and pending-activation accounts and refuses them cleanly, where before they fell through to provisioning and died on the username unique constraint.

Provider resolution no longer serializes logins on a dead identity provider: findByRegistrationId resolves only the requested provider, discovery runs under a ten second timeout, concurrent requests skip a provider that is already being resolved, and configured scopes are trimmed so "openid, profile, email" works. OIDC login honors Spring Security's saved request, so deep links survive the login roundtrip; the redirect-only success handler class, its test, and two orphaned handler beans are gone.
@mraible
mraible force-pushed the feature/oidc-login branch from d98a001 to 212dad6 Compare August 13, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants