0

I'm using Azure B2C to connect to an external OpenID Connect identity provider, i created a basic user flow within B2C which works but only brings back a small number of claims so i need to create a custom policy to pass custom input parameters to my IDP and collect additional claims.

I started with the SocialAndLocalAccount sample and modified with my IDP's details:

TrustFrameworkBase.xml (Cut down to Technical Profile + User Journey)

      ...
      <ClaimType Id="sub">
        <DisplayName>Subject</DisplayName>
        <DataType>string</DataType>
        <DefaultPartnerClaimTypes>
          <Protocol Name="OpenIdConnect" PartnerClaimType="sub" />
        </DefaultPartnerClaimTypes>
        <UserHelpText />
      </ClaimType>
      <ClaimType Id="ui_locales">
        <DisplayName>UI Locales</DisplayName>
        <DataType>string</DataType>
        <UserHelpText>Special parameter passed for account authentication to Tell Us Once.</UserHelpText>
      </ClaimType>
      <ClaimType Id="givenName">
        <DisplayName>Given Name</DisplayName>
        <DataType>string</DataType>
        <DefaultPartnerClaimTypes>
          <Protocol Name="OAuth2" PartnerClaimType="given_name" />
          <Protocol Name="OpenIdConnect" PartnerClaimType="given_name" />
          <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" />
        </DefaultPartnerClaimTypes>
        <UserHelpText>Your given name (also known as first name).</UserHelpText>
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
      <ClaimType Id="surname">
        <DisplayName>Surname</DisplayName>
        <DataType>string</DataType>
        <DefaultPartnerClaimTypes>
          <Protocol Name="OAuth2" PartnerClaimType="family_name" />
          <Protocol Name="OpenIdConnect" PartnerClaimType="family_name" />
          <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
        </DefaultPartnerClaimTypes>
        <UserHelpText>Your surname (also known as family name or last name).</UserHelpText>
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
      <ClaimType Id="DateOfBirth">
        <DisplayName>Date Of Birth</DisplayName>
        <DataType>string</DataType>
        <DefaultPartnerClaimTypes>
          <Protocol Name="OAuth2" PartnerClaimType="birthdate" />
          <Protocol Name="OpenIdConnect" PartnerClaimType="birthdate" />
          <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth" />
        </DefaultPartnerClaimTypes>
        <UserHelpText>Your date of birth.</UserHelpText>
        <UserInputType>TextBox</UserInputType>
      </ClaimType>
      ...
    <ClaimsProvider>
      <Domain>tellusonce</Domain>
      <DisplayName>Tell Us Once</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="TUO-OpenIdConnect">
          <DisplayName>Tell Us Once Login</DisplayName>
          <Description>Login through Tell Us Once</Description>
          <Protocol Name="OpenIdConnect" />
          <Metadata>
            <Item Key="client_id">this is where i put my client id</Item>
            <Item Key="ProviderName">Tell Us Once</Item>
            <Item Key="METADATA">this is where my metadata is</Item>
            <Item Key="response_types">code</Item>
            <Item Key="response_mode">form_post</Item>
            <Item Key="scope">openid</Item>
            <Item Key="HttpBinding">POST</Item>
            <Item Key="UsePolicyInRedirectUri">false</Item>
          </Metadata>
          <CryptographicKeys>
            <Key Id="client_secret" StorageReferenceId="B2C_1A_TellUsOnceSecret" />
          </CryptographicKeys>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid profile" />
            <InputClaim ClaimTypeReferenceId="prompt" DefaultValue="login" />
            <InputClaim ClaimTypeReferenceId="ui_locales" DefaultValue="en-US" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
            <OutputClaim ClaimTypeReferenceId="middleName" DefaultValue="" />
            <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name" />
            <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
            <OutputClaim ClaimTypeReferenceId="DateOfBirth" PartnerClaimType="birthdate" />
            <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="ITP-B2C" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="ITP-Auth-DEV-OIDC" />
          </OutputClaims>
          <OutputClaimsTransformations>
            <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
            <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
            <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
            <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
          </OutputClaimsTransformations>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
    ...
  <UserJourneys>
    <UserJourney Id="SignUpOrSignInOidc">
      <OrchestrationSteps>
        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="TUO-OIDCExchange" />
          </ClaimsProviderSelections>
        </OrchestrationStep>
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="TUO-OIDCExchange" TechnicalProfileReferenceId="TUO-OpenIdConnect" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- For social IDP authentication, attempt to find the user account in the directory. -->
        <OrchestrationStep Order="3" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- Create the user in the directory if one does not already exist. -->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>
  </UserJourneys>

TrustFrameworkExtensions.xml

<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="itpauthdev.onmicrosoft.com" 
  PolicyId="B2C_1A_TrustFrameworkExtensions" 
  PublicPolicyUri="http://itpauthdev.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions" 
  TenantObjectId="ae2201eb-e4e9-44e7-8c73-b52e37ba01f8">
  <BasePolicy>
    <TenantId>itpauthdev.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
  </BasePolicy>
  <BuildingBlocks></BuildingBlocks>
  <ClaimsProviders>
    <ClaimsProvider>
      <Domain>tellusonce</Domain>
      <DisplayName>Tell Us Once</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="TUO-OpenIdConnect">
          <DisplayName>Tell Us Once Login</DisplayName>
          <Description>Login through Tell Us Once</Description>
          <Protocol Name="OpenIdConnect" />
          <Metadata>
            <Item Key="client_id">this is where i put my client id</Item>
            <Item Key="ProviderName">Tell Us Once</Item>
            <Item Key="METADATA">this is where my metadata is</Item>
            <Item Key="response_types">code</Item>
            <Item Key="response_mode">form_post</Item>
            <Item Key="scope">openid</Item>
            <Item Key="HttpBinding">POST</Item>
            <Item Key="UsePolicyInRedirectUri">false</Item>
          </Metadata>
          <CryptographicKeys>
            <Key Id="client_secret" StorageReferenceId="B2C_1A_TellUsOnceSecret" />
          </CryptographicKeys>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="acr_values" DefaultValue="urn:id.gov.au:tdif:acr:ip1:cl1" />
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid profile" />
            <InputClaim ClaimTypeReferenceId="prompt" DefaultValue="login" />
            <InputClaim ClaimTypeReferenceId="ui_locales" DefaultValue="en-US" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
            <OutputClaim ClaimTypeReferenceId="middleName" DefaultValue="" />
            <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name" />
            <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
            <OutputClaim ClaimTypeReferenceId="DateOfBirth" PartnerClaimType="birthdate" />
            <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="ITP-B2C" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="ITP-Auth-DEV-OIDC" />
          </OutputClaims>
          <OutputClaimsTransformations>
            <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
            <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
            <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
            <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
          </OutputClaimsTransformations>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>
  <UserJourneys>
    <UserJourney Id="SignUpOrSignInOidc">
      <OrchestrationSteps>
        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="TUO-OIDCExchange" />
          </ClaimsProviderSelections>
        </OrchestrationStep>
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="TUO-OIDCExchange" TechnicalProfileReferenceId="TUO-OpenIdConnect" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- For social IDP authentication, attempt to find the user account in the directory. -->
        <OrchestrationStep Order="3" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- Create the user in the directory if one does not already exist. -->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>
  </UserJourneys>
</TrustFrameworkPolicy>

signin_signup_oidc.xml

<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="itpauthdev.onmicrosoft.com" 
  PolicyId="B2C_1A_signup_signin_oidc" 
  PublicPolicyUri="http://itpauthdev.onmicrosoft.com/B2C_1A_signup_signin" 
  TenantObjectId="ae2201eb-e4e9-44e7-8c73-b52e37ba01f8">
  <BasePolicy>
    <TenantId>itpauthdev.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>
  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignInOidc" />
    <UserJourneyBehaviors></UserJourneyBehaviors>
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="middleName" DefaultValue="" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="DateOfBirth" DefaultValue="ClaimNotFound" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
        <OutputClaim ClaimTypeReferenceId="identityProvider" />
        <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

When i click "B2C_1A_signin_signup_oidc" and choose Run User Flow i get my IDP's login screen, once i've successfully logged in i get an authorization code response from my IDP, my IDP has been configured with the "/authresp" redirect uri as per Microsoft documentation ("Redirect Uri" section at the bottom) but then it doesn't seem like "/authresp" is exchanging the code for an id_token because when it redirects to jwt.ms i get "token does not contain a valid issuer".

Fiddler Code Response

By using fiddler's inspector i can see that the IDP's endpoint is responding with a code that looks something like this (not actual code):

<HTML>

<HEAD>
    <TITLE>OIDC Form_Post Response</TITLE>
</HEAD>

<BODY Onload="document.forms[0].submit()">
    <FORM METHOD="POST" ACTION="https://itpauthdev.b2clogin.com/itpauthdev.onmicrosoft.com/oauth2/authresp"> <INPUT
            TYPE="HIDDEN" NAME="code"
            VALUE="4cfed1f1-bfcc-42db-b39c-d79480f6d333.056c6e05-eaec-426a-a5e7-bf9f66f962be.15ac212d-38b7-4e9b-80e2-a948be9360e5" />
        <INPUT TYPE="HIDDEN" NAME="state"
            VALUE="StateProperties=eyJTSUQiOiJ4LW1zLWNwaW0tcmM6M2RkYmRhYjktY2VkZS00MDA4LTliNWYtOGRmZjU2ZDZmZDYzIiveWIlEIjoiMmFtkoc5YjUtMzg0Zi00M2JmLThkMDUtMjIzMGYxNzU1M2JjIiwiVE9CRJI6ImFlMjIwMWViLWU0ZTktNDRlNy04YzczLWI1MmUzN2JhMDFmOCJ9" />
        <INPUT TYPE="HIDDEN" NAME="session_state" VALUE="65e01676-90d1-4c5e-a7f4-66dfd7b3211b" /> <NOSCRIPT>
            <P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue .</P>
            <INPUT name="continue" TYPE="SUBMIT" VALUE="CONTINUE" />
        </NOSCRIPT> </FORM>
</BODY>

</HTML>

Fiddler AuthResp Response

Using Fiddler once again, i can see that AuthResp does not exchange the code for an id_token using the IDP's token endpoint (or encounters an error, i don't get told anything helpful). Instead i get back:

<html>

<head>
    <title>Object moved</title>
</head>

<body>
    <h2>Object moved to <a
            href="https://jwt.ms/#error=invalid_request&amp;error_description=AADB2C90238%3a+The+provided+token+does+not+contain+a+valid+issuer.+Please+provide+another+token+and+try+again.%0d%0aCorrelation+ID%3a+2ad979b5-384f-43bf-8d05-2230f17553bc%0d%0aTimestamp%3a+2021-02-26+04%3a29%3a08Z%0d%0a">here</a>.
    </h2>
</body>

</html>

Questions

  1. I read in documentation somewhere that an "OpenIDConnect" technical profile will automatically exchange the code for an id token but i can't seem to find the document, is my configuration missing a step or do i have to do this manually with an extra userjourney?
  2. If there is miscommunication between "/authresp" and the token endpoint, is there somewhere i can look to get a more descriptive error message? Both jwt.ms and the audit logs just say "Token does not contain a valid issuer".

JWTError

Bluecakes
  • 2,069
  • 17
  • 23

3 Answers3

2

The error means the id token from the OIDC provider has an issuer (iss) claim that doesn’t match the issuer in the well known OIDC config endpoint. You can add an issuer element to the metadata of TUO-OpenIdConnect technical profile to override it with the value that appears in the token.

https://learn.microsoft.com/en-us/azure/active-directory-b2c/openid-connect-technical-profile#metadata

issuer : The unique identifier of an OpenID Connect identity provider. The value of issuer metadata takes precedence over the issuer specified in the OpenID well-known configuration endpoint. If specified, Azure AD B2C checks whether the iss claim in a token returned by the identity provider is equal to the one specified in the issuer metadata.

Jas Suri - MSFT
  • 10,605
  • 2
  • 10
  • 20
  • Hi Jas, thanks for the info. The issuer that comes back from the basic flow is `https://itpauthdev.b2clogin.com/ae2201eb-e4e9-44e7-8c73-b52e37ba01f8/v2.0/` but when i add that as new metadata i receive the same error. Is there a way i can inspect the token that B2C receives before it gets to jwt.ms? – Bluecakes Feb 27 '21 at 00:09
  • 1
    That is the issuer of AAD B2C id token. The problem is the issuer of your federated IdP configured in TUO-OpenIdConnect. Find the issuer claim that the identity provider put in its id token. The value you set should be something like `https://idb.syst.ingress.ocp.c4e.citec.qld.gov.au/auth/realms/tell-us-once` as per your IdPs [metadata document](https://idb.syst.ingress.ocp.c4e.citec.qld.gov.au/auth/realms/tell-us-once/.well-known/openid-configuration). – Jas Suri - MSFT Feb 27 '21 at 08:40
  • 1
    To inspect the token the IdP send to AAD B2C, you could use [app insights](https://learn.microsoft.com/en-us/azure/active-directory-b2c/troubleshoot-with-application-insights) – Jas Suri - MSFT Feb 27 '21 at 08:46
  • Thanks Jas, your comment about the AAD issuer reminded me that i tried putting the issuer in both the Base and Extensions xml, i forgot to take it out of Extensions when i was testing the IDP's issuer. Once i took the AAD issuer out of Extensions, i was able to get an access token. – Bluecakes Feb 27 '21 at 11:25
  • hi @Bluecakes Can you pls provide some more information as I am also facing this issue with AAD as identity provider for ADB2C tenant. – Jaydeep Suryawanshi Apr 04 '22 at 05:51
1

I think the technical profile for sign in is not configured correctly. You can find a working example using OpenIdConnect in the starter pack samples.

Please refer in the samples e.g. to

Barbara
  • 191
  • 1
  • 3
  • Hi Barbara, thanks for the info. I am looking to log a user in interactively so my technical profile may look very different but these are very handy for reference, thank you. – Bluecakes Feb 27 '21 at 00:11
1

I was using ADB2C and added AAD as identity provider for Home realm discovery. HRD was working as expected and it was redirecting user to AAD(https://login.microsoftonline.com) basis on domain for authentication and after successful authentication it was redirecting to jwt.ms with "AADB2C90238: The provided token does not contain a valid issuer. Please provide another token and try again." error code. I was able to resolve this by removing below item from Metadata tag.

<Item Key="ProviderName">https://sts.windows.net/ffc9d30c-a7e7-486d-bbcd-66d88af6f4c9/</Item>

Click here for more information