你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
重要
自 2025 年 5 月 1 日起,Azure AD B2C 将不再可供新客户购买。 在我们的常见问题解答中了解详细信息。
在开始之前,请使用此页面顶部的 “选择策略类型 选择器”来选择要设置的策略类型。 Azure Active Directory B2C 提供了两种定义用户如何与应用程序交互的方法:通过预定义的用户流,或者通过可完全配置的自定义策略。 对于每种方法,本文中所需的步骤都不同。
在 Azure Active Directory B2C(Azure AD B2C)中,资源所有者密码凭据(ROPC)流是 OAuth 标准身份验证流。 在此流中,应用程序(也称为信赖方)交换令牌的有效凭据。 凭据包括用户 ID 和密码。 返回的令牌是 ID 令牌、访问令牌和刷新令牌。
警告
建议 不要 使用 ROPC 流。 在大多数情况下,提供了更安全的替代方法,建议使用。 此流要求在应用程序中高度信任,并具有在其他流中不存在的风险。 仅当不能使用其他更安全的流时,才应使用此流。
ROPC 流备注
在 Azure Active Directory B2C(Azure AD B2C)中,支持以下选项:
- 本机客户端:在用户端设备上运行代码时,身份验证期间会发生用户交互。 设备可以是在原生操作系统(如 Android 和 iOS)中运行的移动应用程序。
- 公共客户端流:仅在 API 调用中发送由应用程序收集的用户凭据。 应用程序的凭据未被发送。
- 添加新声明:可以更改 ID 令牌内容以添加新声明。
不支持以下流:
- 服务器到服务器:标识保护系统需要从调用方(本机客户端)收集的可靠 IP 地址作为交互的一部分。 在服务器端 API 调用中,仅使用服务器的 IP 地址。 如果超出身份验证失败的动态阈值,标识保护系统可能会将重复的 IP 地址标识为攻击者。
- 机密客户端流:验证应用程序客户端 ID,但未验证应用程序机密。
使用 ROPC 流时,请考虑以下限制:
- 当需要用户交互的身份验证流发生任何中断时,ROPC 不起作用。 例如,当密码过期或需要更改时,需要 多重身份验证 ,或者登录期间需要收集更多信息(例如用户同意)。
- ROPC 仅支持本地帐户。 用户无法使用 联合标识提供者 (如 Microsoft、Google+、X、AD-FS 或 Facebook)登录。
- 会话管理,包括 使我保持登录状态(KMSI),不适用。
注册应用程序
要在 Azure AD B2C 租户中注册应用程序,可以使用新的统一“应用注册”体验或旧版“应用程序(旧版)”体验。 详细了解新体验。
- 登录到 Azure 门户。
- 确保正在使用的目录包含 Azure AD B2C 租户:
- 在门户工具栏中选择“目录 + 订阅”图标。
- 在“门户设置 | 目录+订阅”页上的“目录名称”列表中找到你的 Azure AD B2C 目录,然后选择“切换”。
- 在 Azure 门户中,搜索并选择“Azure AD B2C”
- 选择“应用注册”,然后选择“新建注册”。
- 输入应用程序的“名称”。 例如, ROPC_Auth_app。
- 保留其他值不变,然后选择“注册”。
- 记录“应用程序(客户端) ID”,以便在后续步骤中使用。
- 在“管理”下,选择“身份验证”。
- 选择“ 试用新体验 ”(如果已显示)。
- 在 “高级设置”和“ 启用以下移动和桌面流”部分下,选择“ 是 ”以将应用程序视为公共客户端。 ROPC 流需要此设置。
- 选择“保存”。
- 在左侧菜单中,选择“ 清单 ”以打开清单编辑器。
- 将 oauth2AllowImplicitFlow 属性设置为 true。 如果该属性不存在,请添加它:
"oauth2AllowImplicitFlow": true,
- 选择“保存”。
创建资源所有者用户流
- 以 Azure AD B2C 租户的外部 ID 用户流管理员身份登录到 Azure 门户。
- 如果有权访问多个租户,请选择顶部菜单中的“设置”图标,从“目录 + 订阅”菜单切换到你的 Azure AD B2C 租户。
- 在 Azure 门户中,搜索并选择 Azure AD B2C。
- 选择 “用户流”,然后选择“ 新建用户流”。
- 选择“使用资源所有者密码凭据登录”(ROPC)。
- 在 “版本”下,确保已选择 “预览 ”,然后选择“ 创建”。
- 为用户流提供名称,例如 ROPC_Auth。
- 在 “应用程序声明”下,选择“ 显示更多”。
- 选择应用程序所需的应用程序声明,例如“显示名称”、“电子邮件”和“标识提供者”。
- 选择确定,然后选择创建。
先决条件
如果您未这样做,请了解如何在 Active Directory B2C 中的自定义策略入门指南中使用自定义策略入门套件。
创建资源所有者策略
打开 TrustFrameworkExtensions.xml 文件。
在 BuildingBlocks 元素下,找到 ClaimsSchema 元素,然后添加以下声明类型:
<ClaimsSchema> <ClaimType Id="logonIdentifier"> <DisplayName>User name or email address that the user can use to sign in</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="resource"> <DisplayName>The resource parameter passes to the ROPC endpoint</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="refreshTokenIssuedOnDateTime"> <DisplayName>An internal parameter used to determine whether the user should be permitted to authenticate again using their existing refresh token.</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="refreshTokensValidFromDateTime"> <DisplayName>An internal parameter used to determine whether the user should be permitted to authenticate again using their existing refresh token.</DisplayName> <DataType>string</DataType> </ClaimType> </ClaimsSchema>
ClaimsSchema 之后,将 ClaimsTransformations 元素及其子元素添加到 BuildingBlocks 元素:
<ClaimsTransformations> <ClaimsTransformation Id="CreateSubjectClaimFromObjectID" TransformationMethod="CreateStringClaim"> <InputParameters> <InputParameter Id="value" DataType="string" Value="Not supported currently. Use oid claim." /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="sub" TransformationClaimType="createdClaim" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="AssertRefreshTokenIssuedLaterThanValidFromDate" TransformationMethod="AssertDateTimeIsGreaterThan"> <InputClaims> <InputClaim ClaimTypeReferenceId="refreshTokenIssuedOnDateTime" TransformationClaimType="leftOperand" /> <InputClaim ClaimTypeReferenceId="refreshTokensValidFromDateTime" TransformationClaimType="rightOperand" /> </InputClaims> <InputParameters> <InputParameter Id="AssertIfEqualTo" DataType="boolean" Value="false" /> <InputParameter Id="AssertIfRightOperandIsNotPresent" DataType="boolean" Value="true" /> </InputParameters> </ClaimsTransformation> </ClaimsTransformations>
找到 ClaimsProvider 元素,其中 DisplayName 为
Local Account SignIn
,并添加以下技术配置文件:<TechnicalProfile Id="ResourceOwnerPasswordCredentials-OAUTH2"> <DisplayName>Local Account SignIn</DisplayName> <Protocol Name="OpenIdConnect" /> <Metadata> <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item> <Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item> <Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item> <Item Key="DiscoverMetadataByTokenIssuer">true</Item> <Item Key="ValidTokenIssuerPrefixes">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> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="logonIdentifier" PartnerClaimType="username" Required="true" DefaultValue="{OIDC:Username}"/> <InputClaim ClaimTypeReferenceId="password" Required="true" DefaultValue="{OIDC:Password}" /> <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" /> <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" /> <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" /> <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFrameworkAppId" /> <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFrameworkAppId" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="oid" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn" /> </OutputClaims> <OutputClaimsTransformations> <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromObjectID" /> </OutputClaimsTransformations> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile>
将 client_id 的 DefaultValue 替换为在先决条件教程中创建的 ProxyIdentityExperienceFramework 应用程序的应用程序 ID。 然后将 resource_id 的 DefaultValue 替换为在先决条件教程中创建的 IdentityExperienceFramework 应用程序的应用程序 ID。
将以下 ClaimsProvider 元素及其技术配置文件添加到 ClaimsProviders 元素:
<ClaimsProvider> <DisplayName>Azure Active Directory</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="AAD-UserReadUsingObjectId-CheckRefreshTokenDate"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" Required="true" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="refreshTokensValidFromDateTime" /> </OutputClaims> <OutputClaimsTransformations> <OutputClaimsTransformation ReferenceId="AssertRefreshTokenIssuedLaterThanValidFromDate" /> <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromObjectID" /> </OutputClaimsTransformations> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Session Management</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="SM-RefreshTokenReadAndSetup"> <DisplayName>Trustframework Policy Engine Refresh Token Setup Technical Profile</DisplayName> <Protocol Name="None" /> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="refreshTokenIssuedOnDateTime" /> </OutputClaims> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Token Issuer</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="JwtIssuer"> <Metadata> <!-- Point to the redeem refresh token user journey--> <Item Key="RefreshTokenUserJourneyId">ResourceOwnerPasswordCredentials-RedeemRefreshToken</Item> </Metadata> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider>
将 UserJourneys 元素及其子元素添加到 TrustFrameworkPolicy 元素:
<UserJourney Id="ResourceOwnerPasswordCredentials"> <PreserveOriginalAssertion>false</PreserveOriginalAssertion> <OrchestrationSteps> <OrchestrationStep Order="1" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="ResourceOwnerFlow" TechnicalProfileReferenceId="ResourceOwnerPasswordCredentials-OAUTH2" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="2" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> </UserJourney> <UserJourney Id="ResourceOwnerPasswordCredentials-RedeemRefreshToken"> <PreserveOriginalAssertion>false</PreserveOriginalAssertion> <OrchestrationSteps> <OrchestrationStep Order="1" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="RefreshTokenSetupExchange" TechnicalProfileReferenceId="SM-RefreshTokenReadAndSetup" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="2" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="CheckRefreshTokenDateFromAadExchange" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId-CheckRefreshTokenDate" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> </UserJourney>
在 Azure AD B2C 租户的 “自定义策略 ”页上,选择“ 上传策略”。
启用“覆盖策略(若存在)”,然后浏览到 TrustFrameworkExtensions.xml 文件并选中该文件。
选择“上传”。
创建信赖方文件
接下来,更新启动你创建的用户旅程的信赖方文件:
在工作目录中创建 SignUpOrSignin.xml 文件的副本,并将其重命名为 ROPC_Auth.xml。
打开新文件并将 TrustFrameworkPolicy 的 PolicyId 属性的值更改为唯一值。 策略标识符是策略的名称。 例如, B2C_1A_ROPC_Auth。
将 DefaultUserJourney 中 ReferenceId 特性的值更改为
ResourceOwnerPasswordCredentials
.将 OutputClaims 元素更改为仅包含以下声明:
<OutputClaim ClaimTypeReferenceId="sub" /> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="displayName" DefaultValue="" /> <OutputClaim ClaimTypeReferenceId="givenName" DefaultValue="" /> <OutputClaim ClaimTypeReferenceId="surname" DefaultValue="" />
在 Azure AD B2C 租户的 “自定义策略 ”页上,选择“ 上传策略”。
启用“覆盖策略(若存在)”,然后浏览到 ROPC_Auth.xml 文件并选中该文件。
选择“上传”。
测试 ROPC 流
使用偏好的 API 开发应用程序生成 API 调用,并查看用于调试策略的响应。 使用以下信息作为 POST 请求的正文,构造一个与此示例类似的调用:
https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/B2C_1A_ROPC_Auth/oauth2/v2.0/token
- 将
<tenant-name>
替换为 Azure AD B2C 租户的名称。 - 将
B2C_1A_ROPC_Auth
替换为资源所有者密码凭据策略的全名。
密钥 | 价值 |
---|---|
用户名 | user-account |
密码 | password1 |
授权类型 (grant_type) | 密码 |
范围 | openid application-id offline_access |
客户编号 | application-id |
response_type(响应类型) | token id_token |
- 将
user-account
替换为租户中某个用户帐户的名称。 - 将
password1
替换为用户帐户的密码。 - 将
application-id
替换为ROPC_Auth_app注册中的应用程序 ID。 - 如果要接收刷新令牌,Offline_access是可选的。
实际的 POST 请求如以下示例所示:
POST /<tenant-name>.onmicrosoft.com/B2C_1A_ROPC_Auth/oauth2/v2.0/token HTTP/1.1
Host: <tenant-name>.b2clogin.com
Content-Type: application/x-www-form-urlencoded
username=contosouser.outlook.com.ws&password=Passxword1&grant_type=password&scope=openid+00001111-aaaa-2222-bbbb-3333cccc4444+offline_access&client_id=00001111-aaaa-2222-bbbb-3333cccc4444&response_type=token+id_token
使用脱机访问的成功响应如以下示例所示:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik9YQjNhdTNScWhUQWN6R0RWZDM5djNpTmlyTWhqN2wxMjIySnh6TmgwRlki...",
"token_type": "Bearer",
"expires_in": "3600",
"refresh_token": "eyJraWQiOiJacW9pQlp2TW5pYVc2MUY0TnlfR3REVk1EVFBLbUJLb0FUcWQ1ZWFja1hBIiwidmVyIjoiMS4wIiwiemlwIjoiRGVmbGF0ZSIsInNlciI6Ij...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik9YQjNhdTNScWhUQWN6R0RWZDM5djNpTmlyTWhqN2wxMjIySnh6TmgwRlki..."
}
兑换刷新令牌
构造一个与此处所示示例类似的 POST 调用。 将下表中的信息用作请求正文:
https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/B2C_1A_ROPC_Auth/oauth2/v2.0/token
- 将
<tenant-name>
替换为 Azure AD B2C 租户的名称。 - 将
B2C_1A_ROPC_Auth
替换为资源所有者密码凭据策略的全名。
密钥 | 价值 |
---|---|
授权类型 (grant_type) | 刷新令牌 |
response_type(响应类型) | id_token |
客户编号 | application-id |
资源 | application-id |
刷新令牌 | refresh-token |
- 请将
application-id
替换为ROPC_Auth_app注册中的应用程序 ID。 - 将
refresh-token
替换为上一个响应中发回的 refresh_token。
成功的响应如以下示例所示:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhT...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQn...",
"token_type": "Bearer",
"not_before": 1533672990,
"expires_in": 3600,
"expires_on": 1533676590,
"resource": "bef2222d56-552f-4a5b-b90a-1988a7d634c3",
"id_token_expires_in": 3600,
"profile_info": "eyJ2ZXIiOiIxLjAiLCJ0aWQiOiI1MTZmYzA2NS1mZjM2LTRiOTMtYWE1YS1kNmVlZGE3Y2JhYzgiLCJzdWIiOm51bGwsIm5hbWUiOiJEYXZpZE11IiwicHJlZmVycmVkX3VzZXJuYW1lIjpudWxsLCJpZHAiOiJMb2NhbEFjY291bnQifQ",
"refresh_token": "eyJraWQiOiJjcGltY29yZV8wOTI1MjAxNSIsInZlciI6IjEuMCIsInppcCI6IkRlZmxhdGUiLCJzZXIiOiIxLjAi...",
"refresh_token_expires_in": 1209600
}
故障排除
提供的应用程序未配置为允许“OAuth”隐式流
- 症状 - 运行 ROPC 流并获取以下消息: AADB2C90057:提供的应用程序未配置为允许“OAuth”隐式流。
- 可能的原因 - 应用程序不允许隐式流。
- 解决方法:在 Azure AD B2C 中创建 应用注册 时,需要手动编辑应用程序清单并将属性的值
oauth2AllowImplicitFlow
设置为true
。 配置oauth2AllowImplicitFlow
属性后,更改可能需要几分钟(通常不超过 5 分钟)才能生效。
使用本机 SDK 或 App-Auth
Azure AD B2C 符合公共客户端资源所有者密码凭据的 OAuth 2.0 标准,并且应与大多数客户端 SDK 兼容。 有关最新信息,请参阅 适用于 OAuth 2.0 的本机应用 SDK 和实现新式最佳做法的 OpenID Connect。