3

I want to programatically set user attributes for the sign up policy. I saw a previous question (Pass parameters to Sign-up policy) asked over a year ago and it was not possible at the time. Any update on this?

Is this possible with the AuthenticationProperties.Dictionary property? Something like this?

HttpContext.GetOwinContext().Set("Policy", Startup.SignUpPolicyId);

var authenticationProperties = new AuthenticationProperties();
authenticationProperties.Dictionary.Add("myattribute", "myvalue");

HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
Saca
  • 10,355
  • 1
  • 34
  • 47
Kevin
  • 333
  • 2
  • 14

1 Answers1

5

This can be implemented using a custom policy.

A working sample of passing an input claim from a relying party application to a custom policy (e.g. an invitation flow as a sign-up policy) is here.

In the WingTipGamesWebApplication project, the InvitationController controller class has two action methods, Create and Redeem.

The Create action method sends a signed redemption link to the email address for the invited user. This redemption link contains this email address.

The Redeem action method handles the redemption link. It passes the email address, as the verified_email claim in a JWT that is signed with the client secret of the Wingtip Games application (see the CreateSelfIssuedToken method in the Startup class in the WingTipGamesWebApplication project), from the redemption link to the Invitation policy.

The Invitation policy can be found at here.

The Invitation policy declares the verified_email claim as an input claim:

<RelyingParty>
  <DefaultUserJourney ReferenceId="Invitation" />
  <TechnicalProfile Id="Invitation">
    <InputTokenFormat>JWT</InputTokenFormat>
      <CryptographicKeys>
        <Key Id="client_secret" StorageReferenceId="WingTipGamesClientSecret" />
    </CryptographicKeys>
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="extension_VerifiedEmail" />
    </InputClaims>
  </TechnicalProfile>
</RelyingParty>

The extension_verifiedEmail claim type, which is declared as a read-only field (so that it can't be modified by the end user), is mapped to the verified_email input claim:

<BuildingBlocks>
  <ClaimsSchema>
    <ClaimType Id="extension_VerifiedEmail">
      <DisplayName>Verified Email</DisplayName>
      <DataType>string</DataType>
      <DefaultPartnerClaimTypes>
        <Protocol Name="OAuth2" PartnerClaimType="verified_email" />
        <Protocol Name="OpenIdConnect" PartnerClaimType="verified_email" />
        <Protocol Name="SAML2" PartnerClaimType="http://schemas.wingtipb2c.net/identity/claims/verifiedemail" />
      </DefaultPartnerClaimTypes>
      <UserInputType>Readonly</UserInputType>
    </ClaimType>
  </ClaimsSchema>
</BuildingBlocks>

The Invitation user journey can be found in here.

The second orchestration step of the Invitation user journey executes the LocalAccount-Registration-VerifiedEmail technical profile:

<UserJourney Id="Invitation">
  <OrchestrationSteps>
    ...
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <ClaimsExchanges>
        ...
        <ClaimsExchange Id="LocalAccountRegistrationExchange" TechnicalProfileReferenceId="LocalAccount-Registration-VerifiedEmail" />
      </ClaimsExchanges>
    </OrchestrationStep>
  </OrchestrationSteps>
</UserJourney>

The LocalAccount-Registration-VerifiedEmail technical profile copies from the extension_verifiedEmail claim to the email claim and then displays the sign-up form with the verified email address (the extension_verifiedEmail claim):

<TechnicalProfile Id="LocalAccount-Registration-VerifiedEmail">
  <DisplayName>WingTip Account</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.localaccount.registration</Item>
    <Item Key="IpAddressClaimReferenceId">IpAddress</Item>
    <Item Key="language.button_continue">Create</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="TokenSigningKeyContainer" />
  </CryptographicKeys>
  <InputClaimsTransformations>
    <InputClaimsTransformation ReferenceId="CreateEmailFromVerifiedEmail" />
  </InputClaimsTransformations>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="extension_VerifiedEmail" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="extension_VerifiedEmail" Required="true" />
    <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
    <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
    <OutputClaim ClaimTypeReferenceId="displayName" Required="true" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
    <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
    <OutputClaim ClaimTypeReferenceId="newUser" />
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="sub" />
    <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists" />
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SSOSession-AzureActiveDirectory" />
</TechnicalProfile>

This LocalAccount-Registration-VerifiedEmail technical profile references the AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists validation technical profile that saves the local account with the verified email address (the email claim):

<TechnicalProfile Id="AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists">
  <Metadata>
    <Item Key="Operation">Write</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
  </InputClaims>
  <PersistedClaims>
    <PersistedClaim ClaimTypeReferenceId="displayName" />
    <PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
    <PersistedClaim ClaimTypeReferenceId="givenName" />
    <PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
    <PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
    <PersistedClaim ClaimTypeReferenceId="surname" />
    <PersistedClaim ClaimTypeReferenceId="verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" />
  </PersistedClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
    <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
  </OutputClaims>
  <OutputClaimsTransformations>
    <OutputClaimsTransformation ReferenceId="CreateSubject" />
  </OutputClaimsTransformations>
  <IncludeTechnicalProfile ReferenceId="AzureActiveDirectoryStore-Common" />
  <UseTechnicalProfileForSessionManagement ReferenceId="SSOSession-AzureActiveDirectory" />
</TechnicalProfile>
Chris Padgett
  • 14,186
  • 1
  • 15
  • 28
  • 2
    Uservoice feedback: [make it easier to pass in claims](https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/32070931-make-it-easier-to-pass-claims-into-a-policy) – spottedmahn Oct 30 '17 at 14:36
  • The feature suggestion must have been getting a lot of votes as they appear to have deleted it. – Neutrino Nov 25 '21 at 20:38
  • Feedback link update: [make it easier to pass in claims](https://feedback.azure.com/d365community/idea/9750399e-bf25-ec11-b6e6-000d3a4f0789) – spottedmahn May 05 '22 at 13:00