= JWT Authentication Plugin
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

Solr can support https://en.wikipedia.org/wiki/JSON_Web_Token[JSON Web Token] (JWT) based Bearer authentication with the use of the JWTAuthPlugin. This allows Solr to assert that a user is already authenticated with an external https://en.wikipedia.org/wiki/Identity_provider[Identity Provider] by validating that the JWT formatted https://en.wikipedia.org/wiki/Access_token[access token] is digitally signed by the Identity Provider. The typical use case is to integrate Solr with an https://en.wikipedia.org/wiki/OpenID_Connect[OpenID Connect] enabled IdP.

== Enable JWT Authentication

To use JWT Bearer authentication, the `security.json` file must have an `authentication` part which defines the class being used for authentication along with configuration parameters.

The simplest possible `security.json` for registering the plugin without configuration is:

[source,json]
----
{
  "authentication": {
    "class":"solr.JWTAuthPlugin"
  }
}
----

The plugin will NOT block anonymous traffic in this mode, since the default for `blockUnknown` is false. It is then possible to start configuring the plugin using REST API calls, which is described below.

== Configuration parameters

[%header,format=csv,separator=;]
|===
Key                  ; Description                                             ; Default
blockUnknown         ; Set to `true` in order to block requests from users without a token  ; `false`
wellKnownUrl         ; URL to an https://openid.net/specs/openid-connect-discovery-1_0.html[OpenID Connect Discovery] endpoint ; (no default)
clientId             ; Client identifier for use with OpenID Connect           ; (no default value) Required to authenticate with Admin UI
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` ;
jwkUrl               ; An https URL to a https://tools.ietf.org/html/rfc7517[JWK] keys file. ; Auto configured if `wellKnownUrl` is provided
jwk                  ; As an alternative to `jwkUrl` you may provide a JSON object here containing the public key(s) of the issuer.  ;
iss                  ; Validates that the `iss` (issuer) claim equals this string        ; Auto configured if `wellKnownUrl` is provided
aud                  ; Validates that the `aud` (audience) claim equals this string      ; If `clientId` is configured, require `aud` to match it
requireSub           ; Makes `sub` (subject) claim mandatory                         ; `true`
requireExp           ; Makes `exp` (expiry time) claim mandatory                     ; `true`
algWhitelist         ; JSON array with algorithms to accept: `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`, `PS256`, `PS384`, `PS512`, `none  ; Default is to allow all algorithms 
jwkCacheDur          ; Duration of JWK cache in seconds                        ; `3600` (1 hour)
principalClaim       ; What claim id to pull principal from                    ; `sub`
claimsMatch          ; JSON object of claims (key) that must match a regular expression (value). Example: `{ "foo" : "A|B" }` will require the `foo` claim to be either "A" or "B". ; (none)
adminUiScope         ; Define what scope is requested when logging in from Admin UI ; If not defined, the first scope from `scope` parameter is used
authorizationEndpoint; The URL for the Id Provider's authorization endpoint ; Auto configured if `wellKnownUrl` is provided 
redirectUris         ; Valid location(s) for redirect after external authentication. Takes a string or array of strings. Must be the base URL of Solr, e.g. https://solr1.example.com:8983/solr/ and must match the list of redirect URIs registered with the Identity Provider beforehand. ; Defaults to empty list, i.e. any node is assumed to be a valid redirect target.
|===

== More configuration examples
=== With JWK URL
To start enforcing authentication for all users, requiring a valid JWT in the `Authorization` header, you need to configure the plugin with one or more https://tools.ietf.org/html/rfc7517[JSON Web Key]s (JWK). This is a JSON document containing the key used to sign/encrypt the JWT. It could be a symmetric or asymmetric key. The JWK can either be fetched (and cached) from an external HTTPS endpoint or specified directly in `security.json`. Below is an example of the former:

[source,json]
----
{
  "authentication": {
    "class": "solr.JWTAuthPlugin",
    "blockUnknown": true,
    "jwkUrl": "https://my.key.server/jwk.json"
  }
}
----

=== With Admin UI support   
The next example shows configuring using https://openid.net/specs/openid-connect-discovery-1_0.html[OpenID Connect Discovery] with a well-known URI for automatic configuration of many common settings, including ability to use the Admin UI with an OpenID Connect enabled Identity Provider. 

[source,json]
----
{
  "authentication": {
    "class": "solr.JWTAuthPlugin",
    "blockUnknown": true,
    "wellKnownUrl": "https://idp.example.com/.well-known/openid-configuration",
    "clientId": "xyz",
    "redirectUri": "https://my.solr.server:8983/solr/"
  }
}
---- 

In this case, `jwkUrl`, `iss` and `authorizationEndpoint` will be automatically configured from the fetched configuration.

=== Complex example
Let's look at a more complex configuration, this time with a static embedded JWK:

[source,json]
----
{
  "authentication": {
    "class": "solr.JWTAuthPlugin", <1>
    "blockUnknown": true, <2>
    "jwk": { <3>
      "e": "AQAB",
      "kid": "k1",
      "kty": "RSA",
      "n": "3ZF6wBGPMsLzsS1KLghxaVpZtXD3nTLzDm0c974i9-KNU_1rhhBeiVfS64VfEQmP8SA470jEy7yWcvnz9GvG-YAlm9iOwVF7jLl2awdws0ocFjdSPT3SjPQKzOeMO7G9XqNTkrvoFCn1YAi26fbhhcqkwZDoeTcHQdRN32frzccuPhZrwImApIedroKLlKWv2IvPDnz2Bpe2WWVc2HdoWYqEVD3p_BEy8f-RTSHK3_8kDDF9yAwI9jx7CK1_C-eYxXltm-6rpS5NGyFm0UNTZMxVU28Tl7LX8Vb6CikyCQ9YRCtk_CvpKWmEuKEp9I28KHQNmGkDYT90nt3vjbCXxw"
    },
    "clientId": "solr-client-12345", <4>
    "iss": "https://example.com/idp", <5>
    "aud": "https://example.com/solr", <6>
    "principalClaim": "solruid", <7>
    "claimsMatch": { "foo" : "A|B", "dept" : "IT" }, <8>
    "scope": "solr:read solr:write solr:admin", <9>
    "algWhitelist" : [ "RS256", "RS384", "RS512" ] <10>
  }
}
----

Let's comment on this config:

<1> Plugin class
<2> Make sure to block anyone without a valid token
<3> Here we pass the JWK inline instead of referring to a URL with `jwkUrl`
<4> Set the client id registered with Identity Provider
<5> The issuer claim must match "https://example.com/idp"
<6> The audience claim must match "https://example.com/solr"
<7> Fetch the user id from another claim than the default `sub`
<8> Require that the `roles` claim is one of "A" or "B" and that the `dept` claim is "IT"
<9> Require one of the scopes `solr:read`, `solr:write` or `solr:admin`
<10> Only accept RSA algorithms for signatures


== Editing JWT Authentication Plugin Configuration

All properties mentioned above can be set or changed using the Config Edit API. You can thus start with a simple configuration with only `class` configured and then configure the rest using the API.

=== Set a config Property

Set properties for the authentication plugin. Each of the configuration keys in the table above can be used as parameter keys for the `set-property` command.

Example:

[.dynamic-tabs]
--
[example.tab-pane#jwt-v1set-property]
====
[.tab-label]*V1 API*

[source,bash]
----
curl http://localhost:8983/solr/admin/authentication -H 'Content-type:application/json' -H 'Authorization: Bearer xxx.yyy.zzz' -d '{"set-property": {"blockUnknown":true, "wellKnownUrl": "https://example.com/.well-knwon/openid-configuration", "scope": "solr:read solr:write"}}'
----
====

[example.tab-pane#jwt-v2set-property]
====
[.tab-label]*V2 API*

[source,bash]
----
curl http://localhost:8983/api/cluster/security/authentication -H 'Content-type:application/json' -H 'Authorization: Bearer xxx.yyy.zzz' -d -d '{"set-property": {"blockUnknown":true, "wellKnownUrl": "https://example.com/.well-knwon/openid-configuration", "scope": "solr:read solr:write"}}'
----
====
--

Insert a valid JWT access token in compact serialization format (`xxx.yyy.zzz` above) to authenticate with Solr once the plugin is active.

== Using clients with JWT Auth 

[#jwt-soljr]
=== SolrJ

SolrJ does not currently support supplying JWT tokens per request.

[#jwt-curl]
=== cURL

To authenticate with Solr when using the cURL utility, supply a valid JWT access token in an `Authorization` header, as follows (replace xxxxxx.xxxxxx.xxxxxx with your JWT compact token):

[source,bash]
----
curl -H "Authorization: Bearer xxxxxx.xxxxxx.xxxxxx" http://localhost:8983/solr/admin/info/system 
----

=== Admin UI 

When this plugin is enabled, users will be redirected to a login page in the Admin UI once they attempt to do a restricted action. The page has a button that users will click and be redirected to the Identity Provider's login page. Once authenticated, the user will be redirected back to Solr Admin UI to the last known location. The session will last as long as the JWT token expiry time and is valid for one Solr server only. That means you have to login again when navigating to another Solr node. There is also a logout menu in the left column where user can explicitly log out.

== Using the Solr Control Script with JWT Auth

The control script (`bin/solr`) does not currently support JWT Auth.