电子邮件邀请注册后 AD B2C 登录失败
AD B2C Sign in fails after email invitation sign up
我遵循了 SignUp with email invitation 教程,除了一个例外,它工作正常。 用户完成注册过程并被重定向后,应用程序无法验证用户并显示错误。
查看来自 OpenIdConnectEvents
的 OnMessageReceived
事件表明 MessageReceivedContext.ProtocolMessage.IdToken
为空。
但是,用户被重定向到 /MicrosoftIdentity/Account/Error,id_token 似乎有效。
在 OnRemoteFailure
事件中,我可以捕捉到以下错误:
{
"ClassName": "System.Exception",
"Message": "OpenIdConnectAuthenticationHandler: message.State is null or empty.",
"Data": null,
"InnerException": null,
"HelpURL": null,
"StackTraceString": null,
"RemoteStackTraceString": null,
"RemoteStackIndex": 0,
"ExceptionMethod": null,
"HResult": -2146233088,
"Source": null,
"WatsonBuckets": null
}
顺便说一句,新用户已正确添加到AD B2C,之后可以登录。只是电子邮件 link 发起的注册过程未能通过身份验证。
这是完整的 SignUpInvitation.xml
自定义策略:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
PolicySchemaVersion="0.3.0.0"
TenantId="mytenant.onmicrosoft.com"
PolicyId="B2C_1A_signup_invitation"
PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_signup_invitation"
DeploymentMode="Development"
UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!-- Sample: Read only email address to present to the user-->
<ClaimType Id="ReadOnlyEmail">
<DisplayName>Verified Email Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!--Sample: Stores the error message for unsolicited request (a request without id_token_hint) and user not found-->
<ClaimType Id="errorMessage">
<DisplayName>Error</DisplayName>
<DataType>string</DataType>
<UserHelpText>Add help text here</UserHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<!--Sample: Initiates the errorMessage claims type with the error message-->
<ClaimsTransformation Id="CreateUnsolicitedErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You cannot sign-up without invitation" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!--Sample: Copy the email to ReadOnlyEmail claim type-->
<ClaimsTransformation Id="CopyEmailAddress" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!--Sample: Sign-up self-asserted technical profile-->
<TechnicalProfile Id="LocalAccountSignUpWithReadOnlyEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
<!-- Sample: Remove sign-up email verification -->
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<InputClaimsTransformations>
<!--Sample: Copy the email to ReadOnlyEamil claim type-->
<InputClaimsTransformation ReferenceId="CopyEmailAddress" />
</InputClaimsTransformations>
<InputClaims>
<!--Sample: Set input the ReadOnlyEmail claim type to prefilled the email address-->
<InputClaim ClaimTypeReferenceId="ReadOnlyEmail" />
<InputClaim ClaimTypeReferenceId="displayName" />
<InputClaim ClaimTypeReferenceId="givenName" />
<InputClaim ClaimTypeReferenceId="surName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<!-- Sample: Display the ReadOnlyEmail claim type (instead of email claim type)-->
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<!-- Sample: Disable session management for sign-up page -->
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<!-- Demo: Show error message-->
<TechnicalProfile Id="SelfAsserted-Unsolicited">
<DisplayName>Unsolicited error message</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>
<!-- Sample: Remove the continue button-->
<Item Key="setting.showContinueButton">false</Item>
</Metadata>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUnsolicitedErrorMessage" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="errorMessage"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage"/>
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!--Sample: This technical profile specifies how B2C should validate your token, and what claims you want B2C to extract from the token.
The METADATA value in the TechnicalProfile meta-data is required.
The “IdTokenAudience” and “issuer” arguments are optional (see later section)-->
<ClaimsProvider>
<DisplayName>My ID Token Hint ClaimsProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName>My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<!--Sample action required: replace with your endpoint location -->
<Item Key="METADATA">https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_SIGNUP_INVITATION</Item>
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<Item Key="issuer">https://localhost:44316/</Item>
</Metadata>
<OutputClaims>
<!--Sample: Read the email claim from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpInvitation">
<OrchestrationSteps>
<!--Sample: Read the input claims from the id_token_hint-->
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<!-- Sample: Check if user tries to run the policy without invitation -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Sample: Self-asserted sign-up page -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSignUpWithReadOnlyEmail" TechnicalProfileReferenceId="LocalAccountSignUpWithReadOnlyEmail"/>
</ClaimsExchanges>
</OrchestrationStep>
<!--Sample: Issue an access token-->
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb"/>
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpInvitation" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="64729310-c74e-45ab-a59e-f35f7ada76ee" DeveloperMode="true" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<!--Sample: Set the input claims to be read from the id_token_hint-->
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<InputClaim ClaimTypeReferenceId="surname" PartnerClaimType="surname" />
<InputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName" />
<InputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
编辑
如前所述,邀请 link 应重定向到我的网络应用程序,因此我添加了以下路由来处理 id_token_hint
.
[HttpGet("/redeem/{scheme?}")]
public IActionResult Redeem([FromRoute] string scheme, [Bind(Prefix = "id_token_hint")] string idTokenHint, string policy)
{
scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
string redirectUrl = Url.Content("~/");
AuthenticationProperties properties = new AuthenticationProperties
{
RedirectUri = redirectUrl,
Items =
{
["id_token_hint"] = idTokenHint,
["policy"] = policy
}
};
return Challenge(properties, scheme);
}
在 StartUp.cs
中,我必须添加以下块才能使其正常工作。
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, Constants.AzureAdB2C);
services.Configure<MicrosoftIdentityOptions>(options =>
{
options.Events ??= new OpenIdConnectEvents();
options.Events.OnRedirectToIdentityProvider += async ctx =>
{
// Append token hint when present (ie. email invitation)
if (ctx.Properties.Items.ContainsKey("id_token_hint"))
ctx.ProtocolMessage.IdTokenHint = ctx.Properties.Items["id_token_hint"];
await Task.CompletedTask.ConfigureAwait(false);
};
});
在您的生产应用程序中使用它
当身份验证流程从您的应用程序开始时,身份验证库会创建一个 state
。此示例为 Azure AD B2C 策略创建原始 link,也称为“运行 Now”link。这种类型的 link 不适合您的生产应用程序实例,只能用于测试示例。
对于生产场景,包含 id_token_hint
的 link 应该指向您的应用程序 https://myapp.com/redeem?hint=<id_token_hint value>
。应用程序应该有一个有效的路由来处理包含 id_token_hint
的查询参数。然后,应用程序应使用身份验证库启动对 AAD B2C 策略 ID 的身份验证,该 id_token_hint
将在该 ID 上使用。该库将包含一种将查询参数添加到身份验证请求的方法。请参阅用于实现此功能的库的文档。
身份验证库随后将构建最终身份验证 link,并将 id_token_hint
作为查询参数的一部分附加。这现在将是一个有效的身份验证请求,您的用户将从您的应用程序重定向到 Azure AD B2C 策略。您的应用程序将能够正确处理来自 Azure AD B2C 的响应。
我遵循了 SignUp with email invitation 教程,除了一个例外,它工作正常。 用户完成注册过程并被重定向后,应用程序无法验证用户并显示错误。
查看来自 OpenIdConnectEvents
的 OnMessageReceived
事件表明 MessageReceivedContext.ProtocolMessage.IdToken
为空。
但是,用户被重定向到 /MicrosoftIdentity/Account/Error,id_token 似乎有效。
在 OnRemoteFailure
事件中,我可以捕捉到以下错误:
{
"ClassName": "System.Exception",
"Message": "OpenIdConnectAuthenticationHandler: message.State is null or empty.",
"Data": null,
"InnerException": null,
"HelpURL": null,
"StackTraceString": null,
"RemoteStackTraceString": null,
"RemoteStackIndex": 0,
"ExceptionMethod": null,
"HResult": -2146233088,
"Source": null,
"WatsonBuckets": null
}
顺便说一句,新用户已正确添加到AD B2C,之后可以登录。只是电子邮件 link 发起的注册过程未能通过身份验证。
这是完整的 SignUpInvitation.xml
自定义策略:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
PolicySchemaVersion="0.3.0.0"
TenantId="mytenant.onmicrosoft.com"
PolicyId="B2C_1A_signup_invitation"
PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_signup_invitation"
DeploymentMode="Development"
UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!-- Sample: Read only email address to present to the user-->
<ClaimType Id="ReadOnlyEmail">
<DisplayName>Verified Email Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<!--Sample: Stores the error message for unsolicited request (a request without id_token_hint) and user not found-->
<ClaimType Id="errorMessage">
<DisplayName>Error</DisplayName>
<DataType>string</DataType>
<UserHelpText>Add help text here</UserHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<!--Sample: Initiates the errorMessage claims type with the error message-->
<ClaimsTransformation Id="CreateUnsolicitedErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You cannot sign-up without invitation" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!--Sample: Copy the email to ReadOnlyEmail claim type-->
<ClaimsTransformation Id="CopyEmailAddress" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!--Sample: Sign-up self-asserted technical profile-->
<TechnicalProfile Id="LocalAccountSignUpWithReadOnlyEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
<!-- Sample: Remove sign-up email verification -->
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<InputClaimsTransformations>
<!--Sample: Copy the email to ReadOnlyEamil claim type-->
<InputClaimsTransformation ReferenceId="CopyEmailAddress" />
</InputClaimsTransformations>
<InputClaims>
<!--Sample: Set input the ReadOnlyEmail claim type to prefilled the email address-->
<InputClaim ClaimTypeReferenceId="ReadOnlyEmail" />
<InputClaim ClaimTypeReferenceId="displayName" />
<InputClaim ClaimTypeReferenceId="givenName" />
<InputClaim ClaimTypeReferenceId="surName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<!-- Sample: Display the ReadOnlyEmail claim type (instead of email claim type)-->
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<!-- Sample: Disable session management for sign-up page -->
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<!-- Demo: Show error message-->
<TechnicalProfile Id="SelfAsserted-Unsolicited">
<DisplayName>Unsolicited error message</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>
<!-- Sample: Remove the continue button-->
<Item Key="setting.showContinueButton">false</Item>
</Metadata>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUnsolicitedErrorMessage" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="errorMessage"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage"/>
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!--Sample: This technical profile specifies how B2C should validate your token, and what claims you want B2C to extract from the token.
The METADATA value in the TechnicalProfile meta-data is required.
The “IdTokenAudience” and “issuer” arguments are optional (see later section)-->
<ClaimsProvider>
<DisplayName>My ID Token Hint ClaimsProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName>My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<!--Sample action required: replace with your endpoint location -->
<Item Key="METADATA">https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_SIGNUP_INVITATION</Item>
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<Item Key="issuer">https://localhost:44316/</Item>
</Metadata>
<OutputClaims>
<!--Sample: Read the email claim from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpInvitation">
<OrchestrationSteps>
<!--Sample: Read the input claims from the id_token_hint-->
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<!-- Sample: Check if user tries to run the policy without invitation -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Sample: Self-asserted sign-up page -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSignUpWithReadOnlyEmail" TechnicalProfileReferenceId="LocalAccountSignUpWithReadOnlyEmail"/>
</ClaimsExchanges>
</OrchestrationStep>
<!--Sample: Issue an access token-->
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb"/>
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpInvitation" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="64729310-c74e-45ab-a59e-f35f7ada76ee" DeveloperMode="true" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<!--Sample: Set the input claims to be read from the id_token_hint-->
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<InputClaim ClaimTypeReferenceId="surname" PartnerClaimType="surname" />
<InputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName" />
<InputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
编辑
如前所述,邀请 link 应重定向到我的网络应用程序,因此我添加了以下路由来处理 id_token_hint
.
[HttpGet("/redeem/{scheme?}")]
public IActionResult Redeem([FromRoute] string scheme, [Bind(Prefix = "id_token_hint")] string idTokenHint, string policy)
{
scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
string redirectUrl = Url.Content("~/");
AuthenticationProperties properties = new AuthenticationProperties
{
RedirectUri = redirectUrl,
Items =
{
["id_token_hint"] = idTokenHint,
["policy"] = policy
}
};
return Challenge(properties, scheme);
}
在 StartUp.cs
中,我必须添加以下块才能使其正常工作。
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, Constants.AzureAdB2C);
services.Configure<MicrosoftIdentityOptions>(options =>
{
options.Events ??= new OpenIdConnectEvents();
options.Events.OnRedirectToIdentityProvider += async ctx =>
{
// Append token hint when present (ie. email invitation)
if (ctx.Properties.Items.ContainsKey("id_token_hint"))
ctx.ProtocolMessage.IdTokenHint = ctx.Properties.Items["id_token_hint"];
await Task.CompletedTask.ConfigureAwait(false);
};
});
在您的生产应用程序中使用它
当身份验证流程从您的应用程序开始时,身份验证库会创建一个 state
。此示例为 Azure AD B2C 策略创建原始 link,也称为“运行 Now”link。这种类型的 link 不适合您的生产应用程序实例,只能用于测试示例。
对于生产场景,包含 id_token_hint
的 link 应该指向您的应用程序 https://myapp.com/redeem?hint=<id_token_hint value>
。应用程序应该有一个有效的路由来处理包含 id_token_hint
的查询参数。然后,应用程序应使用身份验证库启动对 AAD B2C 策略 ID 的身份验证,该 id_token_hint
将在该 ID 上使用。该库将包含一种将查询参数添加到身份验证请求的方法。请参阅用于实现此功能的库的文档。
身份验证库随后将构建最终身份验证 link,并将 id_token_hint
作为查询参数的一部分附加。这现在将是一个有效的身份验证请求,您的用户将从您的应用程序重定向到 Azure AD B2C 策略。您的应用程序将能够正确处理来自 Azure AD B2C 的响应。