You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure AD API权限与Expose an API区别及管理员同意问题排查

问题背景

我目前正在开发一款React Web应用,将使用微软的MSAL包完成用户身份认证,确保仅所属租户内的用户可以访问对应API。

  • 已构建名为TARGET_APP的HTTP函数应用,内置用于访问业务数据并返回结果的Python函数,且已将该应用注册至Azure AD企业应用列表。
  • 根据官方文档对「On Behalf Of(代理)」调用流程的要求,额外注册了代表React客户端应用的应用,命名为CALLER_APP,已完成该应用注册,并配置了所需的权限范围,包括email、user.read以及TARGET_APP公开的API权限。

CALLER_APP的权限配置示例如下:
CALLER_APP Permissions

客户端通过MSAL携带图中所示的权限范围发起CALLER_APP授权请求时,收到了「需要管理员同意」的提示。
以下是认证流程代码片段(handleLogin为触发认证的入口函数):

const msalConfig = {
  auth: {
      authority: "https://login.microsoftonline.com/MY_TENANT/",
      clientId: "CALLER_APP_CLIENT_ID",
      redirectUri,
      postLogoutRedirectUri: redirectUri
  },
  cache: {
      cacheLocation: "localStorage"
  }
}

// 实际调用的caller范围已用"CALLER_APP_SCOPE"占位替换
const loginRequest = {
  scopes: ["CALLER_APP_SCOPE", "user.read", "email"]
};

async function handleLogin(instance) {
  const loginUrl = await getLoginUrl(instance, loginRequest);
  const loginResult = await launchWebAuthFlow(instance, loginUrl);

  // 获取令牌
  const { accessToken } = await acquireToken(instance, loginRequest);

  console.log(accessToken)
}

/**
 * 生成登录url
 */
 async function getLoginUrl(instance, request) {
  return new Promise((resolve, reject) => {
    instance.loginRedirect({
          ...request,
          onRedirectNavigate: (url) => {
              resolve(url);
              return false;
          }
      }).catch(reject);
  });
}

/**
* 唤起Web授权流程
*/
async function launchWebAuthFlow(instance, url) {
  return new Promise((resolve, reject) => {
      chrome.identity.launchWebAuthFlow({
          interactive: true,
          url
      }, (responseUrl) => {
          // 响应url包含hash(登录、获取令牌类请求)
          if (responseUrl.includes("#")) {
            instance.handleRedirectPromise(`#${responseUrl.split("#")[1]}`)
                  .then(resolve)
                  .catch(reject)
          } else {
              // 登出类请求
              resolve();
          }
      })
  })
}

/**
  * 先尝试静默获取访问令牌,失败则回退到交互模式
  */
 async function acquireToken(instance, request) {
  return instance.acquireTokenSilent(request).then((response) => {
    console.log(response.accessToken);
  }).catch(async (error) => {
    console.error(error);
    storage.set({'loggedState': false});
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

上述大部分代码直接参考官方文档编写。调用handleLogin函数可正常发起认证流程,但使用微软账号登录时,会弹出「应用需要访问你组织中仅管理员可授予的资源权限」的提示。
已反复核对所有配置的权限范围,确认没有设置需要管理员同意的权限;同时进入企业应用的用户同意与权限设置页,开启了低影响权限范围的用户同意功能,配置如下图所示:
企业应用配置:
企业应用权限配置
被归类为低影响的3项权限就是前文提到的三个范围:email、user.read、allow-caller。
测试发现,如果改为进入CALLER_APP的「Expose an API」边栏选项卡,在其中创建权限范围,并在MSAL调用时使用该自建范围,认证流程可以完整走完,能成功获取bearer token,也可以正常调用API满足业务需求,但该实现方式并未在官方文档及查阅的参考资料中提及。
现有两个疑问:

  • 为什么当前业务场景下不适合使用「Expose an API」的配置方式?
  • 为什么配置API权限时会触发管理员同意的要求?
问题解答

触发管理员同意要求的核心原因

按排查优先级从高到低,常见原因如下:

  • TARGET_APP侧暴露API范围时未开放用户同意权限。进入TARGET_APP的「Expose an API」配置页,找到创建的allow-caller范围,检查「谁可以同意?」配置项,如果选中的是「仅管理员」,哪怕租户全局开启了低影响权限用户同意,普通用户也无法自行授权,必须管理员同意才能继续,改成「管理员和用户」即可解决。
  • 请求权限时使用的范围格式错误。Azure AD自定义API的委托权限范围必须严格遵循api://<TARGET_APP的客户端ID>/<范围名称>格式,如果漏写了APP ID URI前缀、错将CALLER_APP的客户端ID作为前缀,AAD会将请求识别为无效/未授权权限,直接触发管理员同意提示。
  • 添加API权限时误选了权限类型。在CALLER_APP中添加TARGET_APP的权限时,如果选的是「应用权限」而非「委托权限」,所有应用权限默认都要求管理员同意,不存在用户自主同意的选项,只有委托权限支持普通用户授权。

不适合直接在CALLER_APP配置「Expose an API」的原因

直接在CALLER_APP上自建范围拿到的令牌,受众(aud声明)是CALLER_APP本身,而非后端服务TARGET_APP。TARGET_APP做令牌校验时,第一步就会校验aud字段是否匹配自身的应用ID,受众不匹配的令牌会直接被判定为无效。
更关键的是这种方式完全不符合OBO流程的设计要求:OBO流程需要客户端先获取针对TARGET_APP的委托权限令牌,将该令牌传给TARGET_APP后,TARGET_APP才能用它兑换访问下游资源(比如Graph API、其他Azure服务)的令牌。如果传入的是受众为CALLER_APP的令牌,TARGET_APP根本无法完成OBO令牌兑换,后续所有涉及用户身份访问下游服务的逻辑都会报错。


内容的提问来源于stack exchange,提问作者John Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 02:27:30