Setting up Solr to use Azure Active Directory Open ID Connect.

For the past week I have been trying to get Solr to authenticate using the JWTAuthPlugin with Azure AD. What should have been simple was made difficult by Microsoft and the way they handle scopes. They default all scopes that aren't prefixed by anything to be a graph scope. On top of that, it messes with the signature and the token would become unusable altogether. This post is all about how to get Solr to use Azure AD. And it does work, it just took digging through source, getting help with a bug in Solr and a lot of trial and error.

So, without further ado, here we go.

Create the Azure App Registration

The first step is to create an Azure AD App Registration. Navigate through the Azure Portal to the Azure Active Directory, then App Registrations. Once in the the App Registration pane click New Registration.

Give it a name, for example solr.

Only allow accounts in your organization.

Set the Redirect URI to be Web and set it to your Solr admin portal page. Example: https://solr.example.com/solr/

Click Register. You will then be taken to your application. Take note of the Application (client) ID and the Directory (tenant) ID at the top. You will need them later when we configure Solr.

Go to the Authentication tab and check Access Tokens and ID Tokens. And click Save.

Now, we need to create a scope for the admin portal so we can log in to it.

Go to the Expose an API tab.

Click Add a scope

It will then pop up a window on the right asking you to set the Application ID URI. You can leave this as is or set it to something more friendly. It doesn't matter. But you will need this value when configuring Solr, so you may want to copy it. Click Save and continue.

In the next screen we will give this new scope a name, a display name and a description. Make note of the full scope name, in this screenshot it is right below the top name, api://621684a5-65c9-479a-8ec4-1026195f2fd2/admin. Once done with that, click Add scope.

We are now done with Azure.

Configure Solr

Now that Azure is configured, we need to configure Solr. You will be modifying the security.json file. In a SolrCloud installation this file is stored in Zoo Keeper. I will show how to update it in there. It is easy.

We will start with building your security.json file.

The base template is this:

{
    "authentication": {
        "class": "solr.JWTAuthPlugin",
        "blockUnknown": true,
        "redirectUris": "###SOLRADMINURL###",
        "principalClaim": "name",
        "rolesClaim": "roles",
        "adminUiScope": "###SCOPENAME###",
        "issuers": [
            {
               "name": "AzureAD",
                "wellKnownUrl": "https://login.microsoftonline.com/###TENANTID###/v2.0/.well-known/openid-configuration",
                "clientId": "###CLIENTID###",
                "aud": "###APPLICATIONURI###",
                "iss": "https://sts.windows.net/###TENANTID###/"
            }
        ]
    }
}

In that template, you'll replace ###SOLARADMINURL### with the same URL you put in for the redirect when you created the app registration.

The ###SCOPENAME### is the full name of the scope that you just created.

###TENANTID### is in the template twice, it is the tenant id that you noted down right after you first created the scope.

###CLIENTID### is the client id of the application you just created.

###APPLICATIONURI### is the application id URI that was set when creating the scope.

The resulting security.json is this

{
    "authentication": {
        "class": "solr.JWTAuthPlugin",
        "blockUnknown": true,
        "redirectUris": "https://solr.example.com/solr/",
        "principalClaim": "name",
        "rolesClaim": "roles",
        "adminUiScope": "api://621684a5-65c9-479a-8ec4-1026195f2fd2/admin",
        "issuers": [
            {
               "name": "AzureAD",
                "wellKnownUrl": "https://login.microsoftonline.com/8928c58d-5212-47b0-a549-8e92e51244bf/v2.0/.well-known/openid-configuration",
                "clientId": "621684a5-65c9-479a-8ec4-1026195f2fd2",
                "aud": "api://621684a5-65c9-479a-8ec4-1026195f2fd2",
                "iss": "https://sts.windows.net/8928c58d-5212-47b0-a549-8e92e51244bf/"
            }
        ]
    }
}

Save that file and apply it to your Solr instance.

In SolrCloud, if you saved your security.json to /tmp you use a command like this solr zk cp file:/tmp/security.json zk:/security.json

Conclusion

This has been a hair pulling experience with one thing after another. The biggest kicker is Microsoft's implementation of OpenID Connect is not quite standard. They do some weirdness with the scopes.

A couple of the errors we were getting were the following:

o.a.s.s.SolrDispatchFilter Error authenticating => org.jose4j.jwt.consumer.InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: org.jose4j.lang.JoseException: Parsing error: org.jose4j.json.internal.json_simple.parser.ParseException: Unexpected character (?) at position 0.)

o.a.s.s.JWTAuthPlugin JWT Authentication attempt failed: Required JWT claim missing

The first one was because of a bug in Solr. When a token was not validated it attempted to base64 decode the entire header, Bearer and all. You can see that bug at the link below

[SOLR-16090] JWTAuthPlugin logs in to admin portal but can’t make requests - ASF JIRA

The second issue was that we were setting the scope, and the way Microsoft kindly returns the scopes without the application URI part on the front, Solr spits out that error message. I figured out how to get around that by digging through source code. I like open source :)

I hope this helps save someone a few hairs.

JWT Authentication Plugin | Apache Solr Reference Guide 8.11
JWT.IO
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
[SOLR-16090] JWTAuthPlugin logs in to admin portal but can’t make requests - ASF JIRA
lucene-solr/JWTAuthPlugin.java at branch_8_11 · apache/lucene-solr
Apache Lucene and Solr open-source search software - lucene-solr/JWTAuthPlugin.java at branch_8_11 · apache/lucene-solr