Azure AD B2C自定义策略调用注册应用端点遇权限问题求助
我需要在Azure AD B2C自定义策略中调用Web API,需先通过client_credentials授权类型获取令牌。使用Postman调用以下请求可成功获取令牌:
https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/B2C_1_SignInPolicy/oauth2/v2.0/token?grant_type=client_credentials&client_id=---client_id------&client_secret=-----client_secret----&scope=https://mytenant.onmicrosoft.com/myRegisteredApp/.default
我已通过编辑应用Manifest文件并添加App Role配置范围,但在自定义策略中使用如下TechnicalProfile调用该端点时,始终返回BadRequest:
<TechnicalProfile Id="REST-AcquireAccessToken"> <DisplayName></DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ServiceUrl">https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/B2C_1_SignInPolicy/oauth2/v2.0/token </Item> <Item Key="AuthenticationType">Basic</Item> <Item Key="SendClaimsIn">Form</Item> <Item Key="DebugMode">true</Item> </Metadata> <CryptographicKeys> <Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_SecureRESTClientId" /> <Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_SecureRESTClientSecret" /> </CryptographicKeys> <InputClaims> <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" AlwaysUseDefaultValue="true" /> <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://mytenant.onmicrosoft.com/myRegisteredApp/.default" AlwaysUseDefaultValue="true" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" /> </OutputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile>
若将ServiceUrl改为普通Entra ID端点:
https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/v2.0/token
或:
https://login.microsoftonline.com/common/oauth2/v2.0/token
TechnicalProfile可正常运行,但获取的令牌中权限为Roles字段而非scp,导致Web API不接受该令牌。
核心问题:
- 能否在自定义策略中调用B2C策略端点获取
client_credentials令牌? - 如何让Web API忽略
scp属性缺失的问题,接受带有Roles的令牌?
一、调用B2C策略端点获取client_credentials令牌的可行性
Azure AD B2C的策略专属端点(b2clogin.com/.../B2C_XXX_Policy/...)不支持client_credentials授权类型。这个端点的设计目标是处理用户交互类的授权流(如authorization_code、implicit),仅针对用户参与的身份验证流程,官方并未开放给应用级的client_credentials流使用。你在Postman中能成功调用大概率是特殊场景下的临时兼容,但自定义策略中调用该端点必然返回BadRequest,这是产品设计限制,无法绕过。
二、让Web API接受带有Roles字段的令牌
client_credentials流返回的令牌中,应用级权限默认放在roles字段,而scp字段仅用于用户委托权限(需用户参与的授权流)。要让API接受roles字段的权限,可从以下两方面调整:
1. 修改API的令牌验证逻辑
在API的权限校验代码中,同时兼容scp和roles字段。以.NET为例,使用Microsoft.IdentityWeb时可通过自定义事件合并两类权限:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(options => { // 指定roles为角色声明类型 options.TokenValidationParameters.RoleClaimType = "roles"; options.TokenValidationParameters.NameClaimType = "name"; options.Events = new JwtBearerEvents { OnTokenValidated = context => { var permissions = new List<string>(); // 读取scp字段中的委托权限 if (context.Principal.Claims.Any(c => c.Type == "scp")) { permissions.AddRange(context.Principal.Claims.First(c => c.Type == "scp").Value.Split(' ')); } // 读取roles字段中的应用级权限 permissions.AddRange(context.Principal.Claims .Where(c => c.Type == "roles") .Select(c => c.Value)); // 将合并后的权限添加到身份声明中 var identity = context.Principal.Identity as ClaimsIdentity; foreach (var perm in permissions.Distinct()) { identity.AddClaim(new Claim(ClaimTypes.Role, perm)); } return Task.CompletedTask; } }; }, options => { Configuration.Bind("AzureAd", options); });
2. 确认API与客户端的应用注册配置
- 确保API的应用Manifest中已正确定义
appRoles(应用级权限),且调用API的客户端应用已被分配了对应的App Roles。 - 若API原本仅支持委托权限(scp),需在API的应用注册中添加应用级角色,再给客户端分配这些角色,确保
client_credentials流返回的令牌包含正确的roles值。
内容的提问来源于stack exchange,提问作者dluniewska

