Dessus
Dessus

Reputation: 2177

OIDC-Connect technical profile only works under a self-asserted validation reference (but need it not to require a self-asserted profile)

I am hoping to create an endpoint that allows me to pass in a password as a query param (for the purpose of issuing a JWT for internal M2M usage between microservices. The password is only aimed at preventing services who should be able to have the M2M rather than being super secure as such etc.

I am stuck however with a bug or feature of b2c where I can call the login-NonInteractive profile but it only works if being called from a self-asserted technical profile via a ValidationTechnicalProfile. See working code below (but has a UI because its self asserted):

                <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>
                    </Metadata>
                    <IncludeInSso>false</IncludeInSso>
                    <!-- <InputClaims>
                      <InputClaim ClaimTypeReferenceId="signInName" />
                    </InputClaims> -->
                    <OutputClaims>
                      <OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
                      <OutputClaim ClaimTypeReferenceId="password" Required="true" />
                      <OutputClaim ClaimTypeReferenceId="objectId" />
                      <OutputClaim ClaimTypeReferenceId="authenticationSource" />
                    </OutputClaims>
                    <ValidationTechnicalProfiles>
                      <ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
                    </ValidationTechnicalProfiles>
                    <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
                  </TechnicalProfile>

When I directly call login-NonInteractive (from an orchestration step; to skip the UI that is shown the a self-asserted step), I get an error indicating that the request it sends is sent as a GET HTTP verb; but it only accepts OPTIONS and POST verbs. It seems like the POST metadata key is being ignored in this case. Below is that metadata key:

<Item Key="HttpBinding">POST</Item>

This works as said above when doing via a ValidationTechnicalProfile but not when direct called via an orchestration step.

My question is:

  1. Is there any work around to get login-NonInteractive to POST as it should (without requiring a self-asserted technical profile)?
  2. If not; how would I go about using REST technical profiles (or an OAuth2 profile) to achieve the same thing? If I understand the ODIC calls that would be made; I can probably work through this myself I think. I read the docs here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/openid-connect-technical-profile and here: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc but the last link doesn't feel like it applies; as I am trying to login as a particular user (for the purpose of validating the password mainly). That approach doesn't cover that specifically. I would also prefer to avoid ROPC if possible since its deprecated.

Here is the code for the login-NonInteractive:

        <TechnicalProfile Id="login-NonInteractive">
          <DisplayName>Local Account SignIn</DisplayName>
          <Protocol Name="OpenIdConnect" />
          <Metadata>
            <Item Key="ProviderName">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> -->

            <!-- 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" AlwaysUseDefaultValue="true" />
            <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" AlwaysUseDefaultValue="true" />
            <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>
    ```

Upvotes: 0

Views: 464

Answers (1)

Dessus
Dessus

Reputation: 2177

I have worked out what these guides (https://learn.microsoft.com/en-us/azure/active-directory-b2c/client-credentials-grant-flow?pivots=b2c-custom-policy) etc are advocating now so am posting my answer here.

My confusion came from:

The client credentials are calling into the policy directly in the guides example. I had wrongly assumed that a JWT was being generically issued and then later passed into the custom policy in a second step (ie with similar url format that is used in the portal when hitting an endpoint directly). This confusion was probably fostered too by the fact my custom policy didn't work in a way that works with client credentials. It worked when calling it via the portal; but didn't when using client credentials with it strangely. To solve this I recommend just having a basic replying party JWT issuer and not much else (the code sample here helps somewhat but isn't in a working state as such https://github.com/azure-ad-b2c/samples/tree/master/policies/client_credentials_flow):

    <RelyingParty>
        <DefaultUserJourney ReferenceId="ClientJourney" />
        <TechnicalProfile Id="PolicyProfile">
            <DisplayName>PolicyProfile</DisplayName>
            <Protocol Name="OpenIdConnect" />
            <OutputClaims>
                <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" AlwaysUseDefaultValue="true" DefaultValue="myApp" />
                <OutputClaim ClaimTypeReferenceId="issuingChannel" AlwaysUseDefaultValue="true" DefaultValue="myAdditionalProperty" />
            </OutputClaims>
            <SubjectNamingInfo ClaimType="sub" />
        </TechnicalProfile>
    </RelyingParty>

Essentially the flow is that the client_secret etc being used on the {policy}/oauth2/v2.0/token in step 3 of the first link where you perform the following action:

curl --location --request POST 'https://<your-tenant>.b2clogin.com/<your-tenant>.onmicrosoft.com/<policy>/oauth2/v2.0/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--form 'grant_type="client_credentials"' \
--form 'client_id="<client ID>"' \
--form 'client_secret="<client secret>"' \
--form 'scope="<Your API id uri>/.default"'

is hitting the policy directly (where as through the portal you use endpoints to hit your policy like this: https://{tenantName}.b2clogin.com/{tenantName}.onmicrosoft.com/oauth2/v2.0/authorize which had me thinking that I needed two calls when I only needed the one in the example.

Upvotes: 0

Related Questions