Overview
Fess supports Single Sign-On (SSO) authentication using Microsoft Entra ID (formerly Azure AD). By using Entra ID authentication, you can integrate user information and group information from your Microsoft 365 environment with Fess role-based search.
How Entra ID Authentication Works
In Entra ID authentication, Fess operates as an OAuth 2.0/OpenID Connect client and collaborates with Microsoft Entra ID for authentication.
User accesses the Fess SSO endpoint (
/sso/)Fess redirects to the Entra ID authorization endpoint
User authenticates with Entra ID (Microsoft sign-in)
Entra ID redirects the authorization code to Fess
Fess uses the authorization code to obtain an access token
User is logged in
In the background, Fess uses the Microsoft Graph API to retrieve the user’s group and role information and applies it to role-based search once resolution completes
Note
From Fess 15.8, the authorization response in step 4 is returned as a GET request, because Fess asks the authorization endpoint for response_mode=query. Up to 15.7 it was returned as a cross-site POST, and the shipped default tomcat.sameSiteCookies = lax does not send the session cookie on such a request, so tomcat.sameSiteCookies = none was required as a workaround. If you set none only for that reason, you can restore the default.
For role-based search integration, see Role-Based Search Configuration.
Prerequisites
Before configuring Entra ID authentication, verify the following prerequisites:
Fess 15.8 or later is installed
A Microsoft Entra ID (Azure AD) tenant is available
Fess is accessible via HTTPS (required for production environments)
You have permission to register applications in Entra ID
Basic Configuration
Enabling SSO
To enable Entra ID authentication, add the following setting in app/WEB-INF/conf/system.properties:
Required Settings
Configure the information obtained from Entra ID.
| Property | Description | Default |
|---|---|---|
entraid.tenant | Tenant ID (e.g., xxx.onmicrosoft.com) | (Required) |
entraid.client.id | Application (Client) ID | (Required) |
entraid.client.secret | Client secret value | (Required) |
entraid.reply.url | Redirect URI (Callback URL) | Uses request URL |
Note
Instead of the entraid.* prefix, you can also use the legacy aad.* prefix for backward compatibility.
Optional Settings
The following settings can be added as needed.
| Property | Description | Default |
|---|---|---|
entraid.authority | Authentication server URL | https://login.microsoftonline.com/ |
entraid.state.ttl | State time-to-live (seconds) | 3600 |
entraid.response.mode | How the authorization response is returned. Either query or form_post. | query |
entraid.default.groups | Default groups (comma-separated). Applied to every Entra ID user. | (None) |
entraid.default.roles | Default roles (comma-separated). Applied to every Entra ID user. | (None) |
entraid.permission.fields | Group/role fields (comma-separated) to additionally use as permission values. The group/role ID (GUID) is always used as a permission, and the values of the fields specified here (e.g., mail) are added. Only a field whose value is a string can be used. Microsoft Graph answers a field such as securityEnabled with a boolean and groupTypes with a list, and neither can become a permission value, so such a field is ignored and a warning naming it is written to the log. | mail |
entraid.use.ds | Domain service integration. When true, for permission values in the name@domain format, the local part (name) with the domain part removed is also added as a permission. This applies to the signed-in user as well as to groups and roles: the local part of the user principal name is added as a user-level permission. Setting it to false therefore removes that user-level permission too, not only the group ones. | true |
Note
The group/role ID (GUID) is always used as a permission, but only mail-enabled groups have a mail value. Microsoft 365 groups are mail-enabled, so their name is registered as a permission as well. Security groups are not mail-enabled, so with the default only their GUID becomes a permission. If file system access rights name a security group, the permissions do not match and those documents do not appear in search results.
In that case, add displayName, which every group has:
displayName is not domain-qualified and is not unique, which is why it is not in the default. For example, if Entra ID has a group named Administrators, it also matches documents whose access rights name the built-in Windows Administrators group. Before adding it, check that the names do not collide with the ones already used in your access rights.
Note
With the default query, the authorization code is included in the query string of the callback URL. form_post keeps the code out of the URL, and therefore out of browser history and the access logs of any front-end proxy or WAF, but it makes the callback a cross-site POST and requires tomcat.sameSiteCookies = none. Without that setting the session cookie is not sent back and login fails. Browsers also only accept none on a cookie that carries the Secure attribute, so form_post additionally requires Fess to be served over HTTPS: over plain HTTP the browser does not store the session cookie at all and login still fails. Most deployments should therefore keep the default. Any other value is ignored with a warning and query is used.
Warning
entraid.default.groups and entraid.default.roles are single global values with no per-user scoping. Fess applies them to every Entra ID user at login and applies them again on every later resolution, so Microsoft Graph never takes them away. In particular, never put the Fess administrator role — admin with the shipped authentication.admin.roles — in entraid.default.roles: that grants every user in the tenant permanent access to the administration screens.
Entra ID Side Configuration
App Registration in Azure Portal
Sign in to Azure Portal
Select Microsoft Entra ID
Go to Manage → App registrations → New registration
Register the application:
Setting Value Name Any name (e.g., Fess SSO) Supported account types “Accounts in this organizational directory only” Platform Web Redirect URI https://<Fess host>/sso/Click Register
Creating a Client Secret
On the app details page, click Certificates & secrets
Click New client secret
Set a description and expiration, then click Add
Copy and save the generated Value (this value will not be shown again)
Warning
The client secret value is only displayed immediately after creation. Be sure to record it before navigating away from the page.
Configuring API Permissions
Click API permissions in the left menu
Click Add a permission
Select Microsoft Graph
Select Delegated permissions
Add the following permission:
User.Read- Required to retrieve the signed-in user’s group memberships (/me/memberOf). Granted by default when the app registration is createdGroupMember.Read.All- Required to read group attributes such as the group name, and to resolve nested groups
Click Add permissions
Click Grant admin consent for <tenant name>
Note
Admin consent requires tenant administrator privileges.
Note
Group.Read.All or Directory.Read.All can be granted instead of GroupMember.Read.All, and the group attribute lookup and the nested group resolution still work. However, /me/memberOf is not authorized by Group.Read.All, so User.Read is required in either case.
Note
The permissions above do not cover the displayName of a directory role: Microsoft Graph returns it as null. Naming displayName in entraid.permission.fields therefore adds nothing for a directory role, and only the role’s ID (GUID) becomes a permission. To use role names as permission values, also grant RoleManagement.Read.Directory (or Directory.Read.All).
Note
Fess requests the https://graph.microsoft.com/.default scope when acquiring a token, and from 15.8 it also sends openid profile offline_access https://graph.microsoft.com/.default to the authorization endpoint so that consent is requested for the same set. This means that all access permissions configured and consented to on the app registration are used. Therefore, to retrieve group information, you must add the permissions above to the app registration and grant administrator consent.
Information to Obtain
The following information is used for Fess configuration:
Application (Client) ID: Found on the Overview page as “Application (client) ID”
Tenant ID: Found on the Overview page as “Directory (tenant) ID” or in
xxx.onmicrosoft.comformatClient secret value: The value created in Certificates & secrets
Group and Role Mapping
With Entra ID authentication, Fess automatically retrieves the groups and roles that a user belongs to using the Microsoft Graph API. The retrieved group IDs and group names can be used for Fess role-based search.
Nested Groups
Fess retrieves not only groups that users directly belong to, but also the parent groups those belong to (nested groups). Both the direct membership lookup and the parent group lookup run in the same background task after login, so login itself is never slowed down by Microsoft Graph. The parent group lookup uses the Microsoft Graph getMemberGroups operation, which resolves transitively: one call per directly assigned group returns every group above it, however deep the nesting goes. The retrieved results are cached for a certain period. When that background task completes, the user’s permissions are recalculated.
Default Group Settings
To assign common groups to all Entra ID users:
Configuration Examples
Minimal Configuration (for Testing)
The following is a minimal configuration example for verification in a test environment.
Recommended Configuration (for Production)
The following is a recommended configuration example for production environments.
Legacy Configuration (Backward Compatibility)
For compatibility with previous versions, the aad.* prefix can also be used. When each entraid.* property is not set, the value of the corresponding aad.* property is used. In addition, sso.type=aad is treated the same as sso.type=entraid.
Troubleshooting
Common Issues and Solutions
Cannot Return to Fess After Authentication
Verify that the Redirect URI is correctly configured in the Azure Portal app registration
Ensure the
entraid.reply.urlvalue exactly matches the Azure Portal configurationCheck that the protocol (HTTP/HTTPS) matches
Verify the Redirect URI ends with
/If
entraid.response.modeis set toform_post, check both thattomcat.sameSiteCookies = noneis configured and that Fess is served over HTTPS. With the shipped defaultlaxthe browser does not send the session cookie on the callback’s cross-site POST; withnoneover plain HTTP the browser does not store that cookie at all, becausenonerequires theSecureattribute. Either way the login fails once, on the spot: the browser returns to the login page showing “SSO login process failed.”, and a warning readingFailed to process SSO login: could not validate stateis written to the log
Authentication Errors Occur
Verify that the Tenant ID, Client ID, and Client Secret are correctly configured
Check that the client secret has not expired
Verify that admin consent has been granted for API permissions
Cannot Retrieve Group Information
Verify that the
User.ReadandGroupMember.Read.Allpermissions have been granted (Group.Read.AllorDirectory.Read.Allcan replaceGroupMember.Read.All, but/me/memberOfstill requiresUser.Read)Verify that admin consent has been granted
Check that the user belongs to groups in Entra ID
If nested parent groups cannot be resolved,
Not allowed to read the parent groups of ...is logged as a warning. GrantGroupMember.Read.Allin that caseFess resolves the user’s group and role membership in the background after login completes, so login itself never waits on Microsoft Graph. Until resolution finishes, the user holds only their own user-level permission and whatever
entraid.default.groupsandentraid.default.rolesprovide. With neither of those set, which is the shipped default, a search made in that window returns no documents at all:role.search.default.permissionsis empty out of the box, and a crawling configuration created with the shippedrole.search.default.display.permissionsgrants{role}guest, which a logged-in user does not hold. The window is up to about a second of scheduling delay plus the Microsoft Graph calls themselves — one for the direct memberships, then one more for each of those groups to walk the nested groups, issued one after another on a cold cache — so it grows with the number of groups the user belongs to. While it lasts, the search screen tells the user that their group and role permissions are still loading and asks them to search again in a momentIf resolution does not fully succeed, the search screen tells the user that their group and role permissions could not be fully loaded, asks them to log out and log in again, and to contact an administrator if it keeps happening. “Not fully” is deliberate: the resolution counts as failed unless both the direct membership lookup and the nested group walk succeeded, so a user who holds their direct groups but not their parent groups gets that message too. One case is exempt, and it is the one the previous item describes: when Microsoft Graph refuses the nested group lookup with
Authorization_RequestDeniedbecauseGroupMember.Read.Allwas never granted, Fess takes that as an answer meaning the group has no parents rather than as a failure. Resolution then counts as successful and no message is shown, even though the parent group permissions are missing. The only sign is theNot allowed to read the parent groups of ...warning in the log, so check for it whenever nested groups are in use. Throttling is the usual cause of the partial case — a single HTTP 429 or 503 from Microsoft Graph makes Fess back off for as long as theRetry-Afterheader asks (60 seconds when it says nothing usable, 60 minutes at most), and every nested group lookup in the whole Fess instance is skipped for that time while the direct lookups keep answering. The failure is not necessarily final: resolution is retried whenever the access token is renewed, and a later success clears the message and restores the missing permissions. Logging out and logging in again retries it straight away — opening the SSO login URL while still logged in only redirects back to the search screen
Debug Settings
To investigate issues, you can output detailed Entra ID-related logs by adjusting the Fess log level.
In app/WEB-INF/classes/log4j2.xml, you can add the following logger to change the log level:
Reference
Role-Based Search Configuration - Role-based search configuration
SAML Authentication SSO Setup - SSO configuration with SAML authentication
SSO Configuration with OpenID Connect - SSO configuration with OpenID Connect authentication