Skip to content

fix(sso): request the OIDC scopes at the Entra ID authorization endpoint - #3226

Open
marevol wants to merge 1 commit into
masterfrom
entraid-explicit-oidc-scopes
Open

fix(sso): request the OIDC scopes at the Entra ID authorization endpoint#3226
marevol wants to merge 1 commit into
masterfrom
entraid-explicit-oidc-scopes

Conversation

@marevol

@marevol marevol commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

The v2.0 authorization request asked for one scope:

"/oauth2/v2.0/authorize?response_type=code&scope=https://graph.microsoft.com/.default&..."

.default resolves to whatever delegated permissions the app registration has been granted, so
whether an id_token and a refresh token come back depends on that registration happening to
include openid, profile and offline_access. validateNonce() reads
authData.idToken() unconditionally, so a registration without openid fails the login with a
confusing message.

Meanwhile msal4j already asks for those three at the token endpoint — COMMON_SCOPES in
OAuthAuthorizationGrant:

this.scopes = new HashSet<>(AbstractMsalAuthorizationGrant.COMMON_SCOPES);   // openid profile offline_access

So the two halves of the same flow were asking for different things: consent was collected for one
set, the token exchange requested another.

Fix

Ask for the same set at the authorization endpoint:

scope=openid profile offline_access https://graph.microsoft.com/.default

Microsoft documents .default as combinable with exactly these three. The scope parameter is now
URL encoded, which a space-separated value requires — previously the single scope happened to
contain no characters needing it.

The v1.0 branch is untouched: it identifies the resource with the resource parameter rather than
through scopes.

Scope of the claim

This is a consistency and robustness fix, not a repair of a confirmed breakage. Because msal4j
adds the OIDC scopes at the token step, an existing deployment whose app registration has consent
for them is already getting an id_token and a refresh token today. What changes is that the
authorization request no longer depends on that being true.

Tests

EntraIdAuthenticator's unit test class, 2 new tests, the first failing before the change
(expected: <openid profile offline_access https://graph.microsoft.com/.default> but was: <https://graph.microsoft.com/.default>):

  • test_getAuthUrl_requestsTheOidcScopesUpFront — decodes the scope parameter out of the built
    URL and compares it exactly
  • test_getAuthUrl_encodesTheScopeParameter — a raw space would truncate the query string
Tests run: 133, Failures: 0, Errors: 0, Skipped: 0

(the whole org.codelibs.fess.sso package)

The authorization request asked only for https://graph.microsoft.com/.default,
so whether an id_token and a refresh token come back depended on the app
registration's static permissions happening to include openid, profile and
offline_access.

msal4j already prepends those three to the token request -- see COMMON_SCOPES in
OAuthAuthorizationGrant -- so the two halves of the flow were asking for
different things. Naming them in the authorization request means consent is
asked for the same set the token exchange goes on to request.

The scope parameter is now URL encoded, which a space-separated value requires.
The v1.0 branch is untouched: it uses the resource parameter rather than scopes.
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