Repository navigation
[#1215] Serve /.well-known (WebFinger) through CHF and delete its Restlet code - #1219
Merged
maximthomas merged 9 commits intoNov 6, 2026
Conversation
…with the Restlet realm semantics
…vlet and delete the Restlet WebFinger code
…nd repeated parameters in e2e
…x the WebFinger upgrade note
…rejects and pin realm resolution
CHF decodes the {subrealm} variable, so a %2C reached the realm lookup as a
DN special character and its IllegalArgumentException escaped as a 500;
master looked the raw segment up and answered 404. The handler now catches
it around the lookup only and names the full realm path, also for %2F.
New WebFingerHttpRouteProviderTest cases pin the host-name step, the
realms/<sub> spelling, the first of two realm parameters, the realms
keyword over a top-level realm named "realms", and the %2C/%2F answers.
RealmTestHelper gains mockMalformedRealm for a lookup that throws
IllegalArgumentException.
… named realms, realm request attributes The note now says how to reach a top-level realm named "realms", which the realms/ spellings shadow as under /json, and that WebFinger requests no longer set the realm and realmObject servlet request attributes. The e2e spec header lists the realms/<sub> spelling it pins.
maximthomas
marked this pull request as ready for review
November 5, 2026 12:37
vharseko
approved these changes
Nov 5, 2026
vharseko
left a comment
Member
There was a problem hiding this comment.
Reviewed head 5ddcad9 and the descriptions and discussion history of this PR, #1215 and #1075. No new actionable findings. The documented compatibility changes and the acknowledged RealmRoutingFactory limitation are consistent with the agreed scope.
Validation: all 32 new unit-test cases passed with openam-http built from the same head and RealmTestHelper taken from this PR. The updated Playwright spec passes node --check; git diff --check is clean. I did not rerun the container-based e2e tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is slice S5 of the sequential Restlet to CHF migration tracked in #1075. Closes #1215.
OpenID Connect issuer discovery (
/.well-known/webfinger, RFC 7033) moves from a servlet of its own, which ran the RestletWebFingerapplication, to theOpenAMservlet (CHF), next to/jsonand/xacml. Its Restlet code is deleted. The wire contract pinned bye2e/oauth2/webfinger-test.spec.mjsis kept, except for the divergences listed below.Changes
OAuth2HttpAccessAuditFilter, a CHF access audit filter with componentOAuththat takes the realm from theRealmContext.WebFingerService, a CHF@Getendpoint. It calls the unchangedOpenIDConnectProviderDiscoverywith the deployment URL of the request's realm.{"error":"bad_request","error_description":...}withContent-Type: application/jsonand no charset.resourceorrelparameter counts with its first value, as on master.WebFingerHttpRouteProviderandWebFingerRealms, registered throughMETA-INF/services/org.forgerock.openam.http.HttpRouteProvider.RestletRealmRouterresolved them: the realm of the host name, then one sub-realm per path segment beforewebfinger, then the firstrealmquery value, which wins.{"error":"Not Found","error_description":"Realm \"<realm>\" not found"}.%2F, or a DN special character such as%2C) answers the same 404, naming the full realm path. Master looked the raw segment up, found no realm and answered 404./jsonspellingrealms/root[/realms/<sub>]is added throughRealmRoutingFactory.RealmContextFilteris not used, because it answers 400 to an unknownrealmparameter.WebFingerservlet and its mapping are deleted, and/.well-known/*is mapped to theOpenAMservlet.org.forgerock.openidconnect.restlet.WebFinger,org.forgerock.openidconnect.restlet.OpenIDConnectDiscovery, andorg.forgerock.oauth2.restlet.OAuth2StatusService(onlyWebFingerused it).e2e/oauth2/webfinger-test.spec.mjsgrows from 11 to 19 tests. The upgrade guide gets an[IMPORTANT]note, and the WebFinger sentence of the Use the upstream org.restlet.jee 2.4.4 artifacts instead of the Jakarta-transformed copies, and fix WebFinger answering 500 #1201 note now points to it.Divergences produced by the CHF code
GET /.well-known/realms/root/webfinger?...Realm "/realms" not found)GET /.well-known/realms/root/realms/<sub>/webfinger?...GET /.well-known/realms/<sub>/webfinger?...RealmRoutingFactorylooks<sub>up by name or alias, as under/json)On a deployment with a top-level realm named
realms, master answered/.well-known/realms/webfingerfrom that realm and/.well-known/realms/x/webfingerfrom/realms/x. The branch takes these paths as therealms/spellings, as/jsondoes: 404 for the first unless a realm or alias namedwebfingerexists, and the realm or aliasxfor the second. This is intended.WebFingerHttpRouteProviderTest#theRealmsKeywordWinsOverATopLevelRealmNamedRealmspins it, and the upgrade note names the two ways to reach such a realm:/.well-known/realms/root/realms/realms/webfingerand?realm=/realms.Unpinned changes
These were measured on a branch container, unless marked otherwise.
Allow: GET. The body changes from{"error":"Method Not Allowed",...}to CREST{"code":405,"reason":"Method Not Allowed","message":"Method Not Allowed"}./.well-known: still 404. The Restlet framework body is gone.realms/spelling: 404 with the CREST body that/jsongives,{"code":404,"reason":"Not Found","message":"Realm "/bogus" not found"}. The quotes are HTML-escaped byRealmRoutingFactory, as under/json. An unknown realm in therealmparameter or in a/<realm>/webfingerpath keeps master's OAuth2-shaped body.Server: Restlet-Framework/2.4.4header.RestletRealmRouter, not measured./<realm>/spellings.realms/spellings it answers 400 CREST fromRealmRoutingFactory's hostname filter, as/jsondoes.?_apiis answered by CHF's globalApiDescriptorFilter.%escape in the query answers 400 before routing.realmandrealmObject, which master'sRestletRealmRouterset before the base URL was computed. Nothing in the repository reads them, and the CHF router of/jsonnever set them. A custom Base URL Source extension or servlet filter that reads them would get null. The upgrade note says so. This was read from the code, not measured.OAuth2AccessAuditFilterread from a session cookie the request sent. They record componentOAuth, the realm and the status.Known edge, not fixed here
An encoded slash or an encoded DN special character in a realm segment of a
realms/spelling answers 500, for example/.well-known/realms/root/realms/a%2Fb/webfingeror/.well-known/realms/root/realms/a%2Cb/webfinger.RealmRoutingFactory's CHF realm router lets theIllegalArgumentExceptionescape:Realm.of(Realm, "a/b")rejects the slash, and the DN parser of the realm lookup rejects the comma. The%2Ccase was traced through the code, not measured./jsonand/xacmlshare that router and answer the same. Tomcat rejects%2FunlessALLOW_ENCODED_SLASHis set, but lets%2Cthrough. The bare and/<realm>/spellings (/.well-known/a%2Fb/webfinger,/.well-known/sub/a%2Cb/webfinger) answer 404, which unit tests pin. The fix belongs inRealmRoutingFactory, for all three endpoints.Tests
Unit tests in openam-oauth2, all with 0 failures:
OAuth2HttpAccessAuditFilterTest2,WebFingerServiceTest8,WebFingerHttpRouteProviderTest22.**/oauth2/**and**/openidconnect/**: 951 tests, 0 failures, 0 errors. This includesOpenIDConnectProviderDiscoveryTest(10).WebFingerHttpRouteProviderTestrun: 8 of 22 tests failed, exactly the expected ones./instead of the host name:resolvesTheRealmOfTheHostNamefails.realmquery value wins: the&realm=/sub&realm=/row fails.RealmRoutingFactoryanswersrealms/<name>with the host realm: the/realms/sub/webfingerrow andtheRealmsKeywordWinsOverATopLevelRealmNamedRealmsfail.subRealmHandlerwithout itsIllegalArgumentExceptioncatch: the four%2Cand%2Frows fail.New or changed e2e tests (19 in the spec):
answers the realms/root path spelling like the bare pathreplacesanswers 404 to the realms/root path spelling(divergence).answers 404 for a path segment that names no realm,answers an empty realm parameter like the root realm,answers HEAD like GET without a bodyanduses the first value of a repeated parameter(parity: all pass on master)./.well-known/webfinger in a sub-realm: a throwaway realm is created and deleted per run. It has four tests:?realm=and the path segment (parity),realms/root/realms/<sub>andrealms/<sub>(divergences).Runtime evidence
Docker stand: the
openidentityplatform/openamimage with the WAR replaced and the embedded config store. Each spec set ran twice on one container.The stand ran the WAR of 4c11483. The round-1 review fixes were not run on a stand. They change only the answer to a decoded
%2For%2Cin a realm path segment, which no e2e test sends, plus tests and documentation.build-dockerruns the e2e specs on the new head.oauth2/webfinger-testrealms/spellings, each 404 instead of 200oauth2/webfinger-testoauth2/webfinger-test,oauth2/oauth2-test,uma,xacmlWebFingerServicesets the errorContent-TypebeforesetEntityoauth2/webfinger-testrealmParameterFilterignores therealmparameteroauth2/webfinger-testanswers 404 for a realm that does not existanswers a JRD for the sub-realm named by ?realm=stays green under M2, because the JRD is the same in every realm. The unit testWebFingerHttpRouteProviderTest#resolvesTheRealmOfEverySpellingpins the realm itself.Startup on the branch container:
catalina.outand the debug logs hold no GuiceProvisionException,ConfigurationExceptionorCreationException.RuntimeException caughtfromHttpFrameworkServlet./json/authenticateissued the admin token, and/.well-known/webfingeranswered from CHF, soHttpRouterProviderbuilt the new route provider and its injected types.Upgrade note
New
[IMPORTANT]block inopenam-documentation/openam-doc-source/src/main/asciidoc/upgrade-guide/chap-upgrade-servers.adoc:web.xmlmust delete theWebFingerservlet and map/.well-known/*to theOpenAMservlet.realms/spellings (404 before) and the CREST 404 body for an unknown realm in them.The first paragraph of the #1201 note now refers to this block instead of telling operators to swap the servlet class.