Azure AD B2C Custom Policy: extension_inviteToken Not Passed to REST API Despite Correct Mapping

I’m facing a challenge with the custom policy in Azure AD B2C. Even though the extension_inviteToken claim appears in the /authorize URL and is visible in the audit logs, it fails to be sent to my REST API endpoint. The API only receives either a null value or an empty object for the InviteToken.

Here’s how I have defined the claim:

<ClaimType Id="extension_inviteToken">
  <DisplayName>Invite Token</DisplayName>
  <DataType>string</DataType>
  <DefaultPartnerClaimTypes>
    <Protocol Name="OAuth2" PartnerClaimType="extension_inviteToken" />
  </DefaultPartnerClaimTypes>
</ClaimType>

For the REST API, my technical profile is as follows:

<TechnicalProfile Id="REST-GetEmailFromInviteToken">
  <DisplayName>Get Email From Invite Token</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://api7502.ngrok.io/api/invite/validate</Item>
    <Item Key="SendClaimsIn">Body</Item>
    <Item Key="AuthenticationType">None</Item>
    <Item Key="AllowInsecureAuthInProduction">true</Item>
    <Item Key="DefaultUserMessageIfRequestFailed">Invite token missing or invalid.</Item>
  </Metadata>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="extension_inviteToken" PartnerClaimType="InviteToken" Required="true" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="email" />
    <OutputClaim ClaimTypeReferenceId="extension_accountName" />
    <OutputClaim ClaimTypeReferenceId="extension_contactName" />
    <OutputClaim ClaimTypeReferenceId="extension_inviteToken" />
  </OutputClaims>
</TechnicalProfile>

Additionally, I have set up a debugging technical profile:

<TechnicalProfile Id="SelfAsserted-DebugInviteToken">
  <DisplayName>Debug Invite Token</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider" />
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="extension_inviteToken" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="extension_inviteToken" />
  </OutputClaims>
</TechnicalProfile>

In the user journey, I have this orchestration step:

<OrchestrationStep Order="2" Type="ClaimsExchange">
  <ClaimsExchanges>
    <ClaimsExchange Id="DebugInviteToken" TechnicalProfileReferenceId="SelfAsserted-DebugInviteToken" />
  </ClaimsExchanges>
</OrchestrationStep>

I’ve ensured not to include unnecessary claim transformations and have adhered to best practices throughout.

Here are some extra details:

  • The /authorize URL contains the extension_inviteToken=... and this is confirmed by the audit log.
  • The RelyingParty PolicyProfile includes:
<InputClaims>
  <InputClaim ClaimTypeReferenceId="extension_inviteToken" />
</InputClaims>
  • The REST API expects {"InviteToken":"..."} format and works smoothly when tested with Postman.
  • All policies are uploaded in the right order, and there are no errors during the upload process.
  • Yet, the API keeps receiving either {} or { "InviteToken": null } from B2C.

I’d appreciate any guidance on why the extension_inviteToken claim isn’t being relayed from the OIDC request to the REST API, especially since it seems all configurations and mappings have been set correctly.

Check if your InputClaim is missing the DefaultValue attribute. B2C sometimes drops extension claims between steps even when they appear in logs. Add DefaultValue=“” to your InputClaim in the REST profile, or use a ClaimsResolver to pull it straight from the context. Double-check your orchestration step order too - if another step runs between getting the token and calling REST, it might wipe the claim value.

I hit the same issue recently. The problem was claim persistence - B2C wasn’t keeping the extension_inviteToken through the user journey steps. You need to grab that token from the authorize URL right at the start and make sure it sticks around. Add a QueryStringClaimsProvider technical profile at the very beginning of your user journey to extract and persist the claim from the URL parameters. Even if it shows up in audit logs, B2C might be dropping the claim value between steps. Also check for any claim transformations or validation profiles running before your REST call - they could be clearing or changing the claim. In my case, a self-asserted step was resetting the claim to null because I didn’t configure it to preserve the input value.

I’ve hit this exact issue before. Sounds like extension_inviteToken is being processed as a query parameter but isn’t properly scoped in the claims bag when your REST API call runs.

Try this: add a ClaimsTransformation step right before your REST technical profile. Copy the claim value into a temporary claim, then use that temp claim in your REST call instead. B2C gets weird with extension claims from external sources sometimes.

Also check your InputClaimsTransformations in the REST technical profile - you might need an explicit transformation to get the claim value in the right format.

One more thing: verify your REST technical profile runs in the right execution context where the extension claim’s still available. If it works in your debug step but fails in REST, you’ve got a scoping problem between those orchestration steps.