From bddbd47f1070306fb9ac7405049b3bcc3b42dd83 Mon Sep 17 00:00:00 2001 From: Seung Hyun Myung <87285309+seung-myung@users.noreply.github.com> Date: Tue, 17 Feb 2026 08:30:14 +1300 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=AA=20fix:=20Pass=20Scope=20in=20OpenI?= =?UTF-8?q?D=20Refresh=20Token=20Grant=20for=20Azure=20Custom=20API=20(#11?= =?UTF-8?q?770)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(auth): pass scope parameter in OpenID refresh token grant When using Azure Entra ID with a custom API scope (e.g., api://app-id/access_user) and OPENID_REUSE_TOKENS=true, the refresh token exchange fails with AADSTS90009 because the scope parameter is not included in the refresh request. Azure AD v2.0 requires the scope parameter when refreshing tokens issued for custom API audiences. Without it, Azure interprets the request as the app requesting a token for itself and rejects it. This fix passes OPENID_SCOPE as the scope parameter to refreshTokenGrant(), maintaining backward compatibility (no scope sent if OPENID_SCOPE is not set). Fixes: refresh token 400 error with Azure custom API scopes Tested: Azure Entra ID + Token Reuse + SharePoint integration * style(auth): fix ESLint multiline arguments formatting Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- api/server/controllers/AuthController.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/server/controllers/AuthController.js b/api/server/controllers/AuthController.js index 22e53dcfc9..70491dcc61 100644 --- a/api/server/controllers/AuthController.js +++ b/api/server/controllers/AuthController.js @@ -79,7 +79,12 @@ const refreshController = async (req, res) => { try { const openIdConfig = getOpenIdConfig(); - const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken); + const refreshParams = process.env.OPENID_SCOPE ? { scope: process.env.OPENID_SCOPE } : {}; + const tokenset = await openIdClient.refreshTokenGrant( + openIdConfig, + refreshToken, + refreshParams, + ); const claims = tokenset.claims(); const { user, error, migration } = await findOpenIDUser({ findUser,