0
votes

I want to have 2 login providers for my app. Customers would connect with B2C and employees woudl connect with our AAD by SSO. Currently the B2C login for customers works with a SignIn V2 user flow, and our SSO works just fine for any other applications.

I followed these 2 pages to get started, using the exact same names:

They could be clearer but I think I got everything right as far as the XML goes. When I run my custom policy, I get a page with a login form and a button to connect with the AD. If I click the button, I'm redirected to the SSO page and I log in with my user. The first time I'm asked to accept the permissions. So far so good, but after that I get redirected to https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/oauth2/authresp, which gives a generic error 500 page. In the B2C Audit log, I see an event "Federate with an identity provider" with "Status: success" for the same datetime as my login so I believe the login works. Similarly, I can see a successful sign-in in the user's page in the AAD.

Is there something more I need to do that the MSDN pages missed? I should be getting redirected to jwt.ms with a token.

Relevant xml files (redacted): TrustFrameworkBase.xml (only the parts I've modified):

<?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_TrustFrameworkBase"
  PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkBase">

    <!-- snip default building blocks -->

    <ClaimsProviders>
        <ClaimsProvider>
            <DisplayName>Local Account SignIn</DisplayName>
            <TechnicalProfiles>
                <TechnicalProfile Id="login-NonInteractive">
                    <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="ProviderName">https://sts.windows.net/mytenantguid/</Item>
                        <Item Key="METADATA">https://login.microsoftonline.com/mytenant.onmicrosoft.com/.well-known/openid-configuration</Item>
                        <Item Key="authorization_endpoint">https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/token</Item>
                        <Item Key="response_types">id_token</Item>
                        <Item Key="response_mode">query</Item>
                        <Item Key="scope">email openid</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" />
                        <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
                        <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>
            </TechnicalProfiles>
        </ClaimsProvider>

       <!-- snip other default claim providers-->
       <!-- snip default user journeys-->      
</TrustFrameworkPolicy>

TrustFrameworkExtension.xml

<?xml version="1.0" encoding="utf-8" ?>
<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_TrustFrameworkExtensions"
  PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">

    <BasePolicy>
        <TenantId>mytenant.onmicrosoft.com</TenantId>
        <PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
    </BasePolicy>
    <BuildingBlocks>
    </BuildingBlocks>

    <ClaimsProviders>

        <ClaimsProvider>
            <DisplayName>Local Account SignIn</DisplayName>
            <TechnicalProfiles>
                <TechnicalProfile Id="login-NonInteractive">
                    <Metadata>
                        <Item Key="client_id">ProxyIdentityExperienceFramework_AppId</Item>
                        <Item Key="IdTokenAudience">IdentityExperienceFramework_AppId</Item>
                    </Metadata>
                    <InputClaims>
                        <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFramework_AppId" />
                        <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFramework_AppId" />
                    </InputClaims>
                </TechnicalProfile>
            </TechnicalProfiles>
        </ClaimsProvider>

        <ClaimsProvider>
            <Domain>Mycompany</Domain>
            <DisplayName>Login using Mycompany</DisplayName>
            <TechnicalProfiles>
                <TechnicalProfile Id="MycompanyProfile">
                    <DisplayName>Mycompany Employee</DisplayName>
                    <Description>Login with your Mycompany account</Description>
                    <Protocol Name="OpenIdConnect"/>
                    <OutputTokenFormat>JWT</OutputTokenFormat>
                    <Metadata>
                        <Item Key="METADATA">https://login.windows.net/mytenant.onmicrosoft.com/.well-known/openid-configuration</Item>
                        <Item Key="ProviderName">https://sts.windows.net/mytenantguid/</Item>
                        <Item Key="client_id">AzureADB2CApp_AppdId</Item>
                        <Item Key="IdTokenAudience">AzureADB2CApp_AppdId</Item>
                        <Item Key="UsePolicyInRedirectUri">false</Item>
                        <Item Key="response_types">code</Item>
                        <Item Key="scope">openid</Item>
                        <Item Key="response_mode">form_post</Item>
                        <Item Key="HttpBinding">POST</Item>
                    </Metadata>
                    <CryptographicKeys>
                        <Key Id="client_secret" StorageReferenceId="B2C_1A_MycompanySecret"/>
                    </CryptographicKeys>
                    <OutputClaims>
                        <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid"/>
                        <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
                        <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
                        <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
                        <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
                    </OutputClaims>
                    <OutputClaimsTransformations>
                        <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
                        <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
                    </OutputClaimsTransformations>
                </TechnicalProfile>
            </TechnicalProfiles>
        </ClaimsProvider>
    </ClaimsProviders>

    <UserJourneys>
        <UserJourney Id="SignUpOrSignInMycompany">
            <OrchestrationSteps>

                <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signinwithpassword">
                    <ClaimsProviderSelections>
                        <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
                        <ClaimsProviderSelection TargetClaimsExchangeId="MycompanyExchange" />
                    </ClaimsProviderSelections>
                    <ClaimsExchanges>
                        <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
                    </ClaimsExchanges>
                </OrchestrationStep>

                <!-- Check if the user has selected to sign in using one of the social providers -->
                <OrchestrationStep Order="2" Type="ClaimsExchange">
                    <Preconditions>
                        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                            <Value>objectId</Value>
                            <Action>SkipThisOrchestrationStep</Action>
                        </Precondition>
                    </Preconditions>
                    <ClaimsExchanges>
                        <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
                        <ClaimsExchange Id="MycompanyExchange" TechnicalProfileReferenceId="MycompanyProfile" />
                    </ClaimsExchanges>
                </OrchestrationStep>

                <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId). 
          This can only happen when authentication happened using a social IDP. If local account was created or authentication done
          using ESTS in step 2, then an user account must exist in the directory by this time. -->
                <OrchestrationStep Order="3" Type="ClaimsExchange">
                    <Preconditions>
                        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                            <Value>objectId</Value>
                            <Action>SkipThisOrchestrationStep</Action>
                        </Precondition>
                    </Preconditions>
                    <ClaimsExchanges>
                        <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
                    </ClaimsExchanges>
                </OrchestrationStep>

                <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent 
          in the token. -->
                <OrchestrationStep Order="4" Type="ClaimsExchange">
                    <Preconditions>
                        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                            <Value>authenticationSource</Value>
                            <Value>socialIdpAuthentication</Value>
                            <Action>SkipThisOrchestrationStep</Action>
                        </Precondition>
                    </Preconditions>
                    <ClaimsExchanges>
                        <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
                    </ClaimsExchanges>
                </OrchestrationStep>

                <OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

            </OrchestrationSteps>
            <ClientDefinition ReferenceId="DefaultWeb" />
        </UserJourney>
    </UserJourneys>
</TrustFrameworkPolicy>

SignUpOrSigninMycompany.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_signin_mycompany"
  PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_signup_signin_mycompany">

    <BasePolicy>
        <TenantId>mytenant.onmicrosoft.com</TenantId>
        <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
    </BasePolicy>

    <RelyingParty>
        <DefaultUserJourney ReferenceId="SignUpOrSignInMycompany" />
        <TechnicalProfile Id="PolicyProfile">
            <DisplayName>PolicyProfile</DisplayName>
            <Protocol Name="OpenIdConnect" />
            <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>

In the Identity Experience Framework panel, I run B2C_1A_signup_signin_mycompany and choose testapp1 with https://jwt.ms as the repyl url.

1
In your Technical profile (MycompanyProfile), your targeted clientid and secrets are from B2C apps but those should be from your AD Tenant App Registrations not form Azure B2C Application. - Ramakrishna
@Ramakrishna The name is a bit confusing but it's an AD tenant app. It's named just like the MSDN one. I'm not sure what you mean for the secret, it's a policy key created from the Identity Experience Framework panel. - 0xFF
In TechnicalProfile (MycompanyProfile) CryptographicKeys section you added one Key with name B2C_1A_MycompanySecret. This key should be from AD Tenant not from Azure B2C IEF. - Ramakrishna
@Ramakrishna Yeah it's a policy key created in the B2C IEF from the AAD application key as per this step in MSDN - 0xFF
Nope, it must be from docs.microsoft.com/en-us/azure/active-directory-b2c/… 10th step. Create Key in your AD Tenant registered app and add that key in IEF and refer that Key Name (B2C_1A_YourADTenanatKey) - Ramakrishna

1 Answers

0
votes

After reaching out for support on MSDN, which didn't help, I've redone the whole process from scratch and I get past this error though I still end up with an error :

AADB2C90037: An error occurred while processing the request. Please contact administrator of the site you are trying to access.

Which was caused by a missing outputclaim for the objectid.


What solved the problem of this question was 2 misunderstandings on my part from unclear instructions (imo).

  • The starter pack policies have placeholders you need to replace. Some fields look like placeholders but aren't and I had replaced them.
  • The claim provider given at step 3 contains transformations and OutputClaim that aren't defined in TrustFrameworkBase.xml. I had simply removed them at first but this time I found their definition on MSDN and added them.