B2C 自定义策略登录 - "The username or password provided in the request are invalid"

B2C custom policy login - "The username or password provided in the request are invalid"

我们有一个遗留系统,我们正在升级到 Azure B2C。用户需要能够使用他们的用户名或电子邮件登录。在遗留系统中,多个用户可以拥有相同的电子邮件地址。 我们创建了自定义策略(基于 https://github.com/azure-ad-b2c/samples/tree/master/policies/username-signup-or-signin ),以便用户可以使用他们的用户名登录。为了能够支持用户使用电子邮件登录,我们更改了政策。

用户在登录时输入的电子邮件被传递给我们的 api 如果电子邮件仅链接到一个用户,则 returns 用户名或如果电子邮件链接到多个用户,则为 409 错误用户(returns 用户需要使用他的用户名登录的错误消息,因为我们不知道他是谁)。

如果 api returns 用户名,我们将用户名传递给 login-NonInteractive 配置文件,但配置文件 returns “请求中提供的用户名或密码无效”。

所以基本上,我们尝试传递从 api 获得的用户名来登录,而不是电子邮件用户输入。但这是行不通的。如果我们使用从 api 获得的相同用户名登录,它就可以正常工作。

即使我们为了测试目的调整了策略并对用户名进行了硬编码(而不是调用 api)并尝试使用电子邮件登录,但还是失败了。如果我们使用在 b2c 中作为用户不存在的虚拟用户名登录,它可以正常工作,因为它使用硬编码的用户名。但是,一旦我们尝试使用电子邮件登录,即使我们在策略中使用相同的硬编码用户名,它也不起作用。

我们正在使用声明转换(通过调用 singInNameCopyText 技术配置文件)将硬编码值设置为 login-NonInteractive 配置文件中使用的 signInNamePlainText 声明。

我们使用硬编码用户名值的登录政策示例 – 登录与注册分开:

    <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>
            <Item Key="setting.operatingMode">Username</Item>
        </Metadata>

        <InputClaims>
            <InputClaim ClaimTypeReferenceId="signInName" />

        </InputClaims>
        <DisplayClaims>
            <DisplayClaim ClaimTypeReferenceId="signInName" Required="true" />
            <DisplayClaim ClaimTypeReferenceId="password" Required="true" />
        </DisplayClaims>
        <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
            <OutputClaim ClaimTypeReferenceId="password" Required="true" />
            <OutputClaim ClaimTypeReferenceId="objectId" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" />
            <OutputClaim ClaimTypeReferenceId="isEmailBoolean" />
            <OutputClaim ClaimTypeReferenceId="signInNamePlainText" Required="true"/>
        </OutputClaims>
        <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="regexAnalysisUsername"/>

            <ValidationTechnicalProfile ReferenceId="SecureREST-AccessToken">
                <Preconditions>
                    <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                        <Value>isEmailBoolean</Value>
                        <Value>True</Value>
                        <Action>SkipThisValidationTechnicalProfile</Action>
                    </Precondition>
                </Preconditions>
            </ValidationTechnicalProfile>
                <ValidationTechnicalProfile ReferenceId="REST-CheckEmail" >
                <Preconditions>
                    <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                        <Value>isEmailBoolean</Value>
                        <Value>True</Value>
                        <Action>SkipThisValidationTechnicalProfile</Action>
                    </Precondition>
                </Preconditions>
            </ValidationTechnicalProfile>
            <ValidationTechnicalProfile ReferenceId="singInNameCopyText" >

            </ValidationTechnicalProfile>

            <ValidationTechnicalProfile ReferenceId="log-in-NonInteractive2" />
        </ValidationTechnicalProfiles>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
    </TechnicalProfile>

    <TechnicalProfile Id="log-in-NonInteractive2">
        <DisplayName>Local Account SignIn</DisplayName>
        <Protocol Name="OpenIdConnect" />
        <Metadata>
            <Item Key="client_id">{{ProxyIdentityExperienceFramework}}</Item>
            <Item Key="IdTokenAudience">{{IdentityExperienceFramework}}</Item>
            <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="ProviderName">https://sts.windows.net/</Item>
            <Item Key="METADATA">https://log-in.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item>
            <Item Key="authorization_endpoint">https://log-in.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="LocalAccountProfile">true</Item>
            <Item Key="grant_type">password</Item>

            <!-- Policy Engine Clients -->
            <Item Key="UsePolicyInRedirectUri">false</Item>
            <Item Key="HttpBinding">POST</Item>

        </Metadata>
        <InputClaims>
            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{{ProxyIdentityExperienceFramework}}" />
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{{IdentityExperienceFramework}}" />
            <InputClaim ClaimTypeReferenceId="signInNamePlainText" PartnerClaimType="username" Required="true"/>
            <InputClaim ClaimTypeReferenceId="password" PartnerClaimType="password" Required="true" />
            <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" />
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
            <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />

        </InputClaims>
        <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" />
            <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>

用户旅程:

<UserJourney Id="SignInUsernameOrEmail">
            <OrchestrationSteps>
                <OrchestrationStep Order="1" Type="ClaimsExchange">
                    <ClaimsProviderSelections>
                        <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninLog-inNoLog-in" />
                    </ClaimsProviderSelections>
                    <ClaimsExchanges>
                        <ClaimsExchange Id="LocalAccountSigninLog-inNoLog-in" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Username" />
                    </ClaimsExchanges>
                </OrchestrationStep>

                <OrchestrationStep Order="2" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
            </OrchestrationSteps>
            <ClientDefinition ReferenceId="DefaultWeb" />
        </UserJourney>

登录政策依赖方:

<RelyingParty>
        <DefaultUserJourney ReferenceId="SignInUsernameOrEmail" />
        <UserJourneyBehaviors>
            <JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="{{appInsightsInstrumentationKey}}"
             DeveloperMode="true" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
        </UserJourneyBehaviors>
        <TechnicalProfile Id="PolicyProfile">
            <DisplayName>PolicyProfile</DisplayName>
            <Protocol Name="OpenIdConnect" />
            <OutputClaims>
                
                <OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" />
                <OutputClaim ClaimTypeReferenceId="signInNamePlainText" />
                
                <OutputClaim ClaimTypeReferenceId="isEmailBoolean" />
                
                <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" DefaultValue="{Policy:TenantObjectId}"/>
    
                <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
            </OutputClaims>
            <SubjectNamingInfo ClaimType="sub" />
        </TechnicalProfile>
    </RelyingParty>

更新 1: 刚刚发现我们在使用电子邮件登录时遇到问题的原因是我们为 api.

请求的令牌

我们的 api 受到不记名令牌的保护。

我们使用以下技术配置文件从 b2c 获取令牌

 <TechnicalProfile Id="SecureREST-AccessToken">
      <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://login.microsoftonline.com/{{tenantID}}.onmicrosoft.com/oauth2/v2.0/token</Item>
             <Item Key="AuthenticationType">Basic</Item>
             <Item Key="SendClaimsIn">Form</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" />
             <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://{{tenantID}}.onmicrosoft.com/{{registeredApiAppName}}/.default" />
      </InputClaims>
      <OutputClaims>
             <OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
      </OutputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
 </TechnicalProfile>

 

当我删除对 SecureREST-AccessToken 的调用并调用 login-NonInteractive 时,我不再收到“请求中提供的用户名或密码无效”。

但不确定为什么会导致此问题。

更新 2:更新了 SelfAsserted-LocalAccountSignin-Username 以反映 api 调用。

这是因为这些输入声明正在覆盖登录非交互的输入声明名称。

<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" />
         <InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://{{tenantID}}.onmicrosoft.com/{{registeredApiAppName}}/.default" />
  </InputClaims>

使用不同的声明名称,并使用 partnerclaimtype 发送具有原始声明名称的值。