Azure AD B2C:login-NonInteractive直接调用POST失效及替代方案咨询
Azure AD B2C 内部M2M场景JWT签发问题解决
需求
我需要创建一个支持通过查询参数传入密码的端点,用于微服务间内部M2M场景签发JWT。该密码仅用于限制授权服务访问,无需极高安全性。
遇到的问题
在Azure AD B2C中,login-NonInteractive技术配置文件仅能通过Self-Asserted技术配置文件的ValidationTechnicalProfile调用才能正常工作(此方式会带UI界面)。但直接从编排步骤调用时,即使配置了<Item Key="HttpBinding">POST</Item>,请求仍以GET方法发送,而目标端点仅接受OPTIONS和POST请求,导致报错。
带Self-Asserted UI的有效代码
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Username"> <DisplayName>Local Account Signin</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item> <Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">The last names you provided are not the same</Item> <Item Key="AllowGenerationOfClaimsWithNullValues">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <!-- <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" /> </InputClaims> --> <OutputClaims> <OutputClaim ClaimTypeReferenceId="signInName" Required="true" /> <OutputClaim ClaimTypeReferenceId="password" Required="true" /> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="login-NonInteractive" /> </ValidationTechnicalProfiles> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile>
login-NonInteractive配置代码
<TechnicalProfile Id="login-NonInteractive"> <DisplayName>Local Account SignIn</DisplayName> <Protocol Name="OpenIdConnect" /> <Metadata> <Item Key="ProviderName">https://sts.windows.net/</Item> <Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item> <Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item> <Item Key="response_types">id_token</Item> <Item Key="response_mode">query</Item> <Item Key="scope">email openid</Item> <!-- <Item Key="grant_type">password</Item> --> <!-- Policy Engine Clients --> <Item Key="UsePolicyInRedirectUri">false</Item> <Item Key="HttpBinding">POST</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" /> <InputClaim ClaimTypeReferenceId="password" Required="true" /> <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" AlwaysUseDefaultValue="true" /> <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" AlwaysUseDefaultValue="true" /> <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="oid" /> <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" /> <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" /> <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" /> <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" /> </OutputClaims> </TechnicalProfile>
问题
- 是否有无需依赖Self-Asserted技术配置文件即可让
login-NonInteractive正常发送POST请求的解决方法? - 若没有,如何通过REST技术配置文件或OAuth2配置文件实现相同的用户密码验证及JWT签发功能(优先避免已弃用的ROPC)?
解决方案
问题1:无需Self-Asserted调用login-NonInteractive发送POST请求的方法
直接从编排步骤调用login-NonInteractive时,即便配置了HttpBinding=POST仍用GET请求,是因为OpenID Connect技术配置文件在非验证场景下的默认行为限制,目前没有直接绕过的方式——login-NonInteractive设计上就是作为验证步骤(通过Self-Asserted的ValidationTechnicalProfile)来触发,此时才会遵循POST绑定配置。
如果必须避免UI,可尝试以下两种思路:
- 隐藏Self-Asserted UI:修改Self-Asserted技术配置文件的元数据,将
ContentDefinitionReferenceId指向一个空白/隐藏的内容定义,同时通过Claims Transformation预先填充signInName和password(从查询参数提取),这样用户看不到UI,但仍能触发验证步骤发送POST请求。 - 使用Custom Policy的API Connector:通过REST技术配置文件先处理查询参数提取,再调用内部验证逻辑,但此方式需要额外的后端服务支持。
问题2:用REST/OAuth2实现用户验证与JWT签发(避免ROPC)
方案一:REST技术配置文件+自定义验证服务
- 创建一个REST API服务,负责接收查询参数中的
signInName和password,调用MS Graph API验证用户凭据(使用应用权限调用users/getByUserPrincipalName并校验密码,需启用目录读写权限)。 - 在Azure AD B2C自定义策略中添加REST技术配置文件,直接从编排步骤调用该API:
<TechnicalProfile Id="REST-ValidateUserCredentials"> <DisplayName>Validate User Credentials via REST</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://your-api-domain.com/validate-credentials</Item> <Item Key="SendClaimsIn">QueryString</Item> <Item Key="AuthenticationType">None</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" /> <InputClaim ClaimTypeReferenceId="password" PartnerClaimType="password" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="oid" /> <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" /> <!-- 其他需要的用户信息声明 --> </OutputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile>
- 在编排步骤中调用该REST技术配置文件,验证通过后继续执行JWT签发流程(使用
JwtIssuer技术配置文件)。
方案二:使用Client Credentials Flow(纯M2M场景推荐)
如果你的场景是严格的微服务间M2M,无需模拟用户身份,更合适的方式是使用Azure AD的Client Credentials Flow:
- 为每个微服务注册Azure AD应用,配置应用权限(而非委派权限)。
- 微服务直接调用Azure AD令牌端点,使用客户端ID和客户端密钥(或证书)获取JWT,无需用户密码。
- 此方式完全符合M2M场景最佳实践,避免了密码传递的风险,也不需要自定义策略。
内容的提问来源于stack exchange,提问作者Dessus
相关产品推荐
相关产品推荐

