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

如何以编程方式永久删除Azure App Registration的API权限

问题原因

你当前的代码仅修改了应用注册(Application)对象上的requiredResourceAccess属性,这个属性只用于定义「应用声明需要配置哪些权限」,不会删除租户内已经实际授予给该应用的权限授权记录。你看到权限移动到「为[TENANT]授予的其他权限」分类,本质是权限配置条目被删除了,但历史授权记录仍然绑定在应用对应的服务主体(Service Principal)上没有被清理。
要永久删除API权限,必须同时完成配置移除和授权记录删除两个操作。

操作步骤
  • 第一步:移除应用注册上的权限配置,即你现有代码已经实现的逻辑,更新requiredResourceAccess属性,移除不需要的权限条目
  • 第二步:删除对应服务主体上的已生效授权记录,这是你当前缺失的核心步骤:
    • 如果要删除的是委托权限(Delegated Permission),需要删除对应的oAuth2PermissionGrant对象
    • 如果要删除的是应用程序权限(Application Permission),需要删除对应的appRoleAssignment对象
完整实现代码(C# Microsoft Graph SDK)
// 配置参数
var appObjectId = "{你的应用注册对象ID}";
var appClientId = "{你的应用注册Application (client) ID}";
var targetResourceAppId = "{要删除权限所属API的客户端ID,即你代码中的proxyClientId}";
var targetPermissionId = "{要删除的权限ID}";
var isDelegatedPermission = true; // 删除应用权限时将该值设为false

// 1. 移除应用注册上的权限配置
var app = await _graphClient.Applications[appObjectId].Request().GetAsync();
var updatedRequiredAccess = new List<RequiredResourceAccess>();
foreach (var resource in app.RequiredResourceAccess)
{
    if (resource.ResourceAppId != targetResourceAppId)
    {
        updatedRequiredAccess.Add(resource);
        continue;
    }
    // 同资源下仅删除单个权限时,过滤掉目标权限即可,无需移除整个资源条目
    var remainingPermissions = resource.ResourceAccess.Where(p => p.Id != targetPermissionId).ToList();
    if (remainingPermissions.Any())
    {
        updatedRequiredAccess.Add(new RequiredResourceAccess
        {
            ResourceAppId = resource.ResourceAppId,
            ResourceAccess = remainingPermissions
        });
    }
}
await _graphClient.Applications[appObjectId].Request().UpdateAsync(new Application
{
    RequiredResourceAccess = updatedRequiredAccess
});

// 2. 删除服务主体上的实际授权记录
// 先查询应用对应的服务主体
var spResponse = await _graphClient.ServicePrincipals.Request()
    .Filter($"appId eq '{appClientId}'")
    .GetAsync();
var servicePrincipal = spResponse.FirstOrDefault();
if (servicePrincipal == null) throw new Exception("未找到应用对应的服务主体");

if (isDelegatedPermission)
{
    // 处理委托权限删除
    var grants = await _graphClient.Oauth2PermissionGrants.Request()
        .Filter($"clientId eq '{servicePrincipal.Id}' and resourceId eq (select id from servicePrincipals where appId eq '{targetResourceAppId}')")
        .GetAsync();
    foreach (var grant in grants)
    {
        var scopes = grant.Scope.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
        // 注意:oAuth2PermissionGrant中存储的是权限scope值,若按ID匹配请先查询资源服务主体的oauth2PermissionScope列表获取对应值
        if (scopes.Contains("{替换为要删除的委托权限scope值}"))
        {
            scopes.Remove("{替换为要删除的委托权限scope值}");
            if (!scopes.Any())
            {
                // 授权条目下无剩余权限时直接删除整个条目
                await _graphClient.Oauth2PermissionGrants[grant.Id].Request().DeleteAsync();
            }
            else
            {
                // 有剩余权限时更新scope列表
                grant.Scope = string.Join(' ', scopes);
                await _graphClient.Oauth2PermissionGrants[grant.Id].Request().UpdateAsync(grant);
            }
        }
    }
}
else
{
    // 处理应用程序权限删除
    var assignments = await _graphClient.ServicePrincipals[servicePrincipal.Id].AppRoleAssignments.Request()
        .Filter($"resourceId eq (select id from servicePrincipals where appId eq '{targetResourceAppId}') and appRoleId eq {targetPermissionId}")
        .GetAsync();
    foreach (var assignment in assignments)
    {
        await _graphClient.ServicePrincipals[servicePrincipal.Id].AppRoleAssignments[assignment.Id].Request().DeleteAsync();
    }
}

注意:执行以上操作需要给Graph客户端授予Application.ReadWrite.All等具备应用、服务主体读写能力的目录权限,操作完成后权限会被彻底移除,不会再出现在其他已授予权限列表中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:36:21