Skip to content

fix(sso): join the Entra ID authority and tenant with a single separator - #3259

Open
marevol wants to merge 1 commit into
masterfrom
fix/entraid-authority-separator
Open

fix(sso): join the Entra ID authority and tenant with a single separator#3259
marevol wants to merge 1 commit into
masterfrom
fix/entraid-authority-separator

Conversation

@marevol

@marevol marevol commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Problem

getAuthority() returns the configured entraid.authority / aad.authority verbatim. Its only guard maps a blank value onto DEFAULT_AUTHORITY, which already carries a trailing slash — a non-blank value without one was passed through untouched and concatenated straight onto the tenant:

  • getAuthUrlgetAuthority() + getTenant() + "/oauth2/v2.0/authorize?..."
  • getClientApplication / buildClientApplicationKeygetAuthority() + getTenant() + "/"

So with

entraid.authority=https://login.microsoftonline.com
entraid.tenant=contoso.onmicrosoft.com

the authorization URL became

https://login.microsoftonline.comcontoso.onmicrosoft.com/oauth2/v2.0/authorize?...

The host and the tenant fuse into a single bogus hostname. The browser gets NXDOMAIN, and because the URL is only logged at debug level nothing points back at the setting. https://login.microsoftonline.com is how the endpoint is written wherever it is documented, so omitting the trailing slash is the expected mistake rather than an exotic one.

This is pre-existing and reproduces on 15.7.x as well; it is not a 15.8 regression.

Fix

Add getTenantAuthority(), which joins the authority and the tenant with exactly one /, and route getAuthUrl, getClientApplication, buildClientApplicationKey() and the two debug-log authorities in getAccessToken through it.

getAuthority() itself is deliberately unchanged: it is a documented getter whose value also feeds buildClientApplicationKey.

Normalisation rules:

authority ends / tenant starts / result
yes no unchanged (the normal case, byte-identical)
no no one / inserted — the bug
no yes unchanged — already correct today
yes yes collapsed to one /

A tenant that already starts with a slash joins correctly against a slashless authority today, so a naive "always append a slash to the authority" fix would break a working configuration. A blank tenant gets no separator at all, because every caller appends its own / afterwards and inserting one here would turn today's output into a doubled slash.

Nothing is lowercased or trimmed, and the shape of the authority handed to msal4j is unchanged — it still receives at least one path segment, which Authority.detectAuthorityType requires.

The msal4j side was already loud about this (it throws IllegalArgumentException, which becomes an SsoLoginException); it is the browser redirect that failed silently. That path is untouched.

Tests

The authenticator test class gains seven cases: all four authority/tenant slash combinations, a blank tenant against both authority shapes, a casing guard, and one that pins the full authorization URL for the fixed case so the regression is caught end to end.

Every new test was mutation-checked against two reverts:

  • pre-fix behaviour (getAuthority() + getTenant()) — 4 fail, including the full-URL test, which reports the fused login.microsoftonline.comcontoso.onmicrosoft.com.
  • the naive fix (always append / to the authority) — the other 3 fail, confirming the "already correct today" cases are live guards and not tautologies.

mvn clean test -Dtest='org.codelibs.fess.sso.**' → Tests run: 250, Failures: 0, Errors: 0, Skipped: 0.
mvn clean javadoc:jar → BUILD SUCCESS.

getAuthority() returns the configured entraid.authority / aad.authority
verbatim, and its only guard maps a blank value onto DEFAULT_AUTHORITY,
which already carries a trailing slash. A non-blank value without one was
concatenated straight onto the tenant, so

  entraid.authority=https://login.microsoftonline.com
  entraid.tenant=contoso.onmicrosoft.com

produced

  https://login.microsoftonline.comcontoso.onmicrosoft.com/oauth2/v2.0/authorize

The host and the tenant fuse into one hostname, the browser gets NXDOMAIN,
and the authorization URL is only logged at debug level, so nothing points
at the setting. https://login.microsoftonline.com is how the endpoint is
written wherever it is documented, so omitting the trailing slash is the
expected mistake rather than an exotic one.

Add getTenantAuthority(), which joins the two with exactly one slash, and
route getAuthUrl, getClientApplication, buildClientApplicationKey and the
two debug-log authorities through it. A tenant that already starts with a
slash joins correctly against a slashless authority today, so a separator
is inserted only when neither side supplies one and a doubled separator is
collapsed; a blank tenant gets no separator, because every caller appends
its own slash afterwards. getAuthority() itself is unchanged, and so is
the shape of the authority handed to msal4j.

Tests cover all four authority/tenant slash combinations, a blank tenant
and the casing, and pin the full authorization URL for the fixed case.
@marevol marevol added this to the 15.9.0 milestone Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant