From 6d36c942ddd3001f78d61b01e2835fad5e600535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 6 May 2026 14:19:50 +0200 Subject: [PATCH 1/2] SOLR-18215 JWT auth module now defaults to blockUnknown=true Backport of #4373 to branch_9x --- .../SOLR-18215-jwt-auth-blockUnknown-default-true.yml | 8 ++++++++ .../org/apache/solr/security/jwt/JWTAuthPlugin.java | 2 +- .../apache/solr/security/jwt/JWTAuthPluginTest.java | 10 ++++++++++ .../pages/jwt-authentication-plugin.adoc | 11 +++++------ .../upgrade-notes/pages/major-changes-in-solr-9.adoc | 6 ++++++ solr/webapp/web/js/angular/controllers/security.js | 5 +++-- 6 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 changelog/unreleased/SOLR-18215-jwt-auth-blockUnknown-default-true.yml diff --git a/changelog/unreleased/SOLR-18215-jwt-auth-blockUnknown-default-true.yml b/changelog/unreleased/SOLR-18215-jwt-auth-blockUnknown-default-true.yml new file mode 100644 index 000000000000..08e26cf170ec --- /dev/null +++ b/changelog/unreleased/SOLR-18215-jwt-auth-blockUnknown-default-true.yml @@ -0,0 +1,8 @@ +title: JWT Authentication `blockUnknown` now defaults to `true`, blocking unauthenticated requests by default. Previously the code defaulted to `false` despite the reference guide documenting `true`. Users relying on pass-through must explicitly set `blockUnknown` to `false` in their security.json. +type: changed +authors: + - name: Jan Høydahl + url: https://home.apache.org/phonebook.html?uid=janhoy +links: + - name: SOLR-18215 + url: https://issues.apache.org/jira/browse/SOLR-18215 diff --git a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java index 819f2293f9be..bdb0ac874c30 100644 --- a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java +++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java @@ -179,7 +179,7 @@ public void init(Map pluginConfig) { } blockUnknown = - Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_BLOCK_UNKNOWN, false))); + Boolean.parseBoolean(String.valueOf(pluginConfig.getOrDefault(PARAM_BLOCK_UNKNOWN, true))); requireIssuer = Boolean.parseBoolean( String.valueOf(pluginConfig.getOrDefault(PARAM_REQUIRE_ISSUER, "true"))); diff --git a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java index f871c779ed44..55c51c754201 100644 --- a/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java +++ b/solr/modules/jwt-auth/src/test/org/apache/solr/security/jwt/JWTAuthPluginTest.java @@ -484,6 +484,15 @@ public void noHeaderBlockUnknown() { assertEquals(NO_AUTZ_HEADER, resp.getAuthCode()); } + @Test + public void noHeaderDefaultBlocksUnknown() { + // blockUnknown defaults to true — omitting it must block requests without a JWT + testConfig.remove("blockUnknown"); + plugin.init(testConfig); + JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(null); + assertEquals(NO_AUTZ_HEADER, resp.getAuthCode()); + } + @Test public void noHeaderNotBlockUnknown() { testConfig.put("blockUnknown", false); @@ -511,6 +520,7 @@ public void wellKnownConfigNoHeaderPassThrough() { .toString(); testConfig.put("wellKnownUrl", wellKnownUrl); testConfig.remove("jwk"); + testConfig.put("blockUnknown", false); plugin.init(testConfig); JWTAuthPlugin.JWTAuthenticationResponse resp = plugin.authenticate(null); assertEquals(PASS_THROUGH, resp.getAuthCode()); diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc index 4a5094f62252..63fcdbb6a870 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc @@ -36,15 +36,14 @@ The simplest possible `security.json` for registering the plugin without configu ---- { "authentication": { - "class":"solr.JWTAuthPlugin", - "blockUnknown":"false" + "class":"solr.JWTAuthPlugin" } } ---- -The plugin will by default require a valid JWT token for all traffic. +By default, `blockUnknown` is `true`, so all requests without a valid JWT token will be blocked. -If the `blockUnknown` property is set to `false` as in the above example, it is possible to start configuring the plugin using unauthenticated REST API calls, which is further described in section <>. +If you need to configure the plugin using unauthenticated REST API calls, set `blockUnknown` to `false` as further described in section <>. == Configuration Parameters @@ -52,7 +51,7 @@ If the `blockUnknown` property is set to `false` as in the above example, it is [%header,format=csv,separator=;,cols="25%,50%,25%"] |=== Key ; Description ; Default -blockUnknown ; Set to `false` to if you need to perform configuration through REST API or if you use an Authorization Plugin and only want certain paths protected. By default all requests will require a token ; `true` +blockUnknown ; Set to `false` if you need to perform configuration through REST API or if you use an Authorization Plugin and only want certain paths protected. By default all requests will require a token ; `true` realm ; Name of the authentication realm to echo back in HTTP 401 responses. Will also be displayed in Admin UI login page ; 'solr-jwt' scope ; Whitespace separated list of valid scopes. If configured, the JWT access token MUST contain a `scope` claim with at least one of the listed scopes. Example: `solr:read solr:admin` ; requireIss ; Fails requests that lacks an `iss` (issuer) claim ; `true` @@ -167,7 +166,7 @@ Let's look at a more complex configuration, this time with two issuers configure Let's comment on this config: <1> Plugin class -<2> Make sure to block anyone without a valid token (this is also the default) +<2> Block anyone without a valid token (this is also the default) <3> Fetch the user id from another claim than the default `sub` <4> Require that the `foo` claim is one of "A" or "B" and that the `dept` claim is "IT" <5> Require one of the scopes `solr:read`, `solr:write` or `solr:admin` diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc index 37180f2d30f3..a9fde5d46967 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc @@ -97,6 +97,12 @@ The project normally doesn't remove functionality in a minor release, but we mad +NOTE: The previous parse-context-based configuration (`parseContext.config`) is no longer supported. Tika parser-specific properties must now be configured directly on the Tika Server itself, rather than through Solr configuration. Please refer to the Tika Server documentation for details on how to set these properties. +=== JWT Authentication + +The `blockUnknown` setting in the JWT Authentication plugin now defaults to `true`, meaning requests without a valid JWT token are blocked by default. +In Solr 9.10 and earlier, the code default was `false` (pass-through), which contradicted the reference guide documentation that described `true` as the default. +Users upgrading from 9.10 who relied on the pass-through behavior must explicitly set `"blockUnknown": false` in their `security.json`. + == Solr 9.10 === SolrJ diff --git a/solr/webapp/web/js/angular/controllers/security.js b/solr/webapp/web/js/angular/controllers/security.js index 52b5c2ac46a2..fd65a289988f 100644 --- a/solr/webapp/web/js/angular/controllers/security.js +++ b/solr/webapp/web/js/angular/controllers/security.js @@ -242,7 +242,7 @@ solrAdminApp.controller('SecurityController', function ($scope, $timeout, $cooki $scope.hideAll(); $scope.tls = false; - $scope.blockUnknown = "false"; // default setting + $scope.blockUnknown = "true"; // default setting $scope.realmName = "solr"; $scope.forwardCredentials = "false"; $scope.multiAuthWithBasic = false; @@ -371,7 +371,8 @@ solrAdminApp.controller('SecurityController', function ($scope, $timeout, $cooki //console.log(">> authn: "+JSON.stringify(authn)); - $scope.blockUnknown = authn["blockUnknown"] === true ? "true" : "false"; + var blockUnknown = authn["blockUnknown"]; + $scope.blockUnknown = (blockUnknown === false || blockUnknown === "false") ? "false" : "true"; $scope.forwardCredentials = authn["forwardCredentials"] === true ? "true" : "false"; if ("realm" in authn) { From 86411be9b3a26959be67ac00fadb68f9c790e520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B8ydahl?= Date: Wed, 6 May 2026 14:45:10 +0200 Subject: [PATCH 2/2] Revert wrong edits to jwt-authentication-plugin.adoc --- .../pages/jwt-authentication-plugin.adoc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc index 63fcdbb6a870..316b9ec465c1 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/jwt-authentication-plugin.adoc @@ -36,14 +36,15 @@ The simplest possible `security.json` for registering the plugin without configu ---- { "authentication": { - "class":"solr.JWTAuthPlugin" + "class":"solr.JWTAuthPlugin", + "blockUnknown":"false" } } ---- -By default, `blockUnknown` is `true`, so all requests without a valid JWT token will be blocked. +The plugin will by default require a valid JWT token for all traffic. -If you need to configure the plugin using unauthenticated REST API calls, set `blockUnknown` to `false` as further described in section <>. +If the `blockUnknown` property is set to `false` as in the above example, it is possible to start configuring the plugin using unauthenticated REST API calls, which is further described in section <>. == Configuration Parameters @@ -166,7 +167,7 @@ Let's look at a more complex configuration, this time with two issuers configure Let's comment on this config: <1> Plugin class -<2> Block anyone without a valid token (this is also the default) +<2> Make sure to block anyone without a valid token (this is also the default) <3> Fetch the user id from another claim than the default `sub` <4> Require that the `foo` claim is one of "A" or "B" and that the `dept` claim is "IT" <5> Require one of the scopes `solr:read`, `solr:write` or `solr:admin`