How to output group claims in B2C from ADFS as an identity provider - azure

I'm using ADFS as an IdP for Azure B2C through OpenID Connect.
Login works and B2C sends UPN from ADFS as socialIdpUserId claim in JWT token.
But group claims from ADFS do not work.
How to receive group claims in JWT?
Here is the setup:
ADFS claim rule: domain security groups and upn
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname",
Issuer == "AD AUTHORITY"] =>
issue(store = "Active Directory",
types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "http://schemas.xmlsoap.org/claims/Group"),
query = ";userPrincipalName,tokenGroups(longDomainQualifiedName);{0}",
param = c.Value);
Client permissions are set to openid and allatclaims
New group claim type definition in TrustFrameworkBase policy in ClaimsSchema:
<ClaimsSchema><ClaimType Id="group">
<DisplayName>group</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="group" />
<Protocol Name="OpenIdConnect" PartnerClaimType="group" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
</DefaultPartnerClaimTypes>
</ClaimType></ClaimsSchema>
Output group claim definition in TechnicalProfile in TrustFrameworkExtensions policy:
<OutputTokenFormat>JWT</OutputTokenFormat><OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="UPN" />
<OutputClaim ClaimTypeReferenceId="group" PartnerClaimType="group" />
</OutputClaims>
Output group claim definition in TechnicalProfile in SignUpOrSignIn policy file
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" />
<OutputClaim ClaimTypeReferenceId="group" />
<OutputClaim ClaimTypeReferenceId="authmethod" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
But there is no group claim comes with JWT token! Why?

Here is how to issue group claims out of B2C:
1. Define a new claim type in for groups in the Base policy file. This definition should be at the end of < ClaimsSchema > element (yes, the man who wrote about stringCollection was write!)
<ClaimType Id="IdpUserGroups">
<DisplayName>Security groups</DisplayName>
<DataType>stringCollection</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="groups" />
<Protocol Name="OpenIdConnect" PartnerClaimType="groups" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
</DefaultPartnerClaimTypes>
</ClaimType>
Use this new defined claim in the < OutputClaims > in the extenstion policy in < ClaimsProvider > definition for ADFS
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn" />
<OutputClaim ClaimTypeReferenceId="IdpUserGroups" PartnerClaimType="http://schemas.xmlsoap.org/claims/Group" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="SAML fmdadfs4.local"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="SAML ADFS4 fmdadfs4.local" />
</OutputClaims>
Use the same claim in the < OutputClaims > dfinition in relyng party definition under < RelyngParty > elemnt in your SignIn policy file
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" />
<OutputClaim ClaimTypeReferenceId="IdpUserGroups" />
<OutputClaim ClaimTypeReferenceId="identityProvider" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="userPrincipalName" />
Issue group claims from ADFS as it is shown here

Looks like OP simply has the partnerclaimtype misspelled.
Not certain because you may have mapped something non-standard, but I'm thinking you just need to change your PartnerClaimType from group to groups.
<ClaimType Id="groups">
<DisplayName>Groups</DisplayName>
<DataType>stringCollection</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OpenIdConnect" PartnerClaimType="groups" />
</DefaultPartnerClaimTypes>
<UserHelpText>List of group memberships</UserHelpText>
</ClaimType>
Once you define the ClaimType, you don't need to specify the PartnerClaimType anywhere else - unless you're overriding the value.
I'd also consider using the DefaultValue="" attribute so you can check your policy is properly executing the output claim.
OutputClaim ClaimTypeReferenceId="groups" DefaultValue="no groups assigned

Related

Azure B2C SAML Email Claim

I have successfully setup Azure B2C using the local account start packs using B2C as the IDP, we've integrated with a partner and can access their application. How do we go about Passing the local IDP accounts 'email' attribute to the partner as a claim. They're getting first, last name and display name without issue but not receiving the email claim.
Similar issue to this: Azure B2C SAML Custom Policy Assert Email
I've inserted this into the BaseFramework
<ClaimType Id="email">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="email" />
<Protocol Name="OpenIdConnect" PartnerClaimType="email" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email" />
</DefaultPartnerClaimTypes>
...
</ClaimType>
And this Technical Profile
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<!-- Optional claims -->
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
and then this in the SAML flow:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInWithCA" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId" />
</OutputClaims>
<SubjectNamingInfo ClaimType="objectId" ExcludeAsClaim="true" />
</TechnicalProfile>
</RelyingParty>
But it's still not working
As above modified as per documentation
You didn’t need to make any of these changes, except in the third snippet you needed to put this
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
This takes the signInNames.emailAddress and issues it as “email” into the token.
Currently you defined a claim called email, read a claim called signInNames.emailAddress, then try to issue a claim called email into the token, which will be null.

How to pass refresh token of a third party IDP to the application via Azure AD B2C?

I’m working on an application which can read files of a given OneDrive account.
We use Azure AD B2C as the identity provider. Users can login to the application using their Microsoft account. For that we have enabled Microsoft as an Identity Provider in my AAD B2C tenant.
When a given user is login using their Microsoft account, application should be able to get both access_token and refresh_token which enables us to communicate with MS Graph API, in order to fetch file details.
Using custom polices we were able to fetch access_token. However, we cannot fetch the refresh_token.
This is how ClaimsSchema is defined in TrustFrameworkExtensions.xml :
<ClaimsSchema>
<ClaimType Id="ms_access_token">
<DisplayName>MS access token</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="{oauth2:access_token}" />
<Protocol Name="OpenIdConnect" PartnerClaimType="{oauth2:access_token}" />
</DefaultPartnerClaimTypes>
<UserHelpText>access token form 3rd party MS AD.</UserHelpText>
</ClaimType>
<ClaimType Id="ms_refresh_token">
<DisplayName>MS Refresh token</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="{oauth2:refresh_token}" />
<Protocol Name="OpenIdConnect" PartnerClaimType="{oauth2:refresh_token}" />
</DefaultPartnerClaimTypes>
<UserHelpText>refresh token form 3rd party MS AD.</UserHelpText>
</ClaimType>
</ClaimsSchema>
Also in the same file, under the TechnicalProfile of Microsoft login, following OutputClaims node is added (some child nodes are removed for clarity):
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ms_access_token" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="ms_refresh_token" DefaultValue="" />
</OutputClaims>
Then under the relevant RelyingParty node following OutputClaims node is added (some child nodes are removed for clarity):
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ms_access_token" PartnerClaimType="ms_idp_access_token"/>
<OutputClaim ClaimTypeReferenceId="ms_refresh_token" PartnerClaimType="ms_idp_refresh_token"/>
</OutputClaims>
According to documentation there is no claim resolver for refresh_token.
Any suggestion to get this work?
I think that the only way you could do this would be via a custom api, that uses a password flow to authenticate the user. You can then return the refresh_token, access_token etc back from the API. I've not been able to figure any other way to do this.
Finally I was able to get this resolved by the help from a Microsoft employee in MS forum.
As of now, Azure AD B2C doesn't support this with OIDC providers. Therefore we have to enable Microsoft as an OAuth2 Identity Provider, instead of OIDC. For that we have to define a ClaimProvider like this:
<ClaimsProviders>
<ClaimsProvider>
<Domain>live.com</Domain>
<DisplayName>Microsoft Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="MSA-OAuth">
<DisplayName>Microsoft Account</DisplayName>
<Protocol Name="OAuth2"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://login.live.com/oauth20_token.srf</Item>
<Item Key="authorization_endpoint">https://login.live.com/oauth20_authorize.srf</Item>
<Item Key="ClaimsEndpoint">https://graph.microsoft.com/v1.0/me</Item>
<Item Key="ClaimsEndpointAccessTokenName">access_token</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="client_id"><!-- your client id --></Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="scope">openid profile email offline_access files.read</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_SampathAdCustomPolicyAppKey"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="live.com" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="id"/>
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="ms_access_token" PartnerClaimType="{oauth2:access_token}"/>
<OutputClaim ClaimTypeReferenceId="ms_refresh_token" PartnerClaimType="{oauth2:refresh_token}"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
Note that the {oauth2:refresh_token} claims resolver returns a JSON string such as:
{
"r": "<refresh_token>",
"d": "live.com"
}
You can read the r property from this JSON string as follows.
Create a GetClaimFromJson claims transformation:
<ClaimsTransformation Id="GetRefreshTokenFromRefreshTokenResult" TransformationMethod="GetClaimFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="ms_refresh_token" TransformationClaimType="inputJson" />
</InputClaims>
<InputParameters>
<InputParameter Id="claimToExtract" DataType="string" Value="r" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ms_refresh_token" TransformationClaimType="extractedClaim" />
</OutputClaims>
</ClaimsTransformation>
Invoke this claims transformation from the MSA-OAuth technical profile as an output claims transformation:
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="GetRefreshTokenFromRefreshTokenResult" />
</OutputClaimsTransformations>

Only "email" field can be passed via "id_token_hint" to B2C custom policy

I'm following this article to pass email and displayName as id_token_hint to my custom policy. Following is the technical profile I'm using to extract the data:
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName> My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_ClientAssertionSigningKey" />
</CryptographicKeys>
<OutputClaims>
<!--Sample: Read the email cliam from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="newUserEmail"/>
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName"/>
</OutputClaims>
</TechnicalProfile>
Problem is, I'm only able to extract the email value. The displayName claim type not getting extracted. I checked the id_token_hint value in jwt.ms, value for both email and displayName is present there. How can i solve this?
You must also add the incoming claims as the input claims for the RelyingParty technical profile.
Example
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<!-- The following claims are read from the ID token -->
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="newUserEmail" />
<InputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
...
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>

How do I get Azure AD B2C's built-in claims to be returned in the token when I'm using a custom policy?

I've been using the built-in SignUpOrSignIn policy for a while, but I'm now moving to a custom policy.
When I set up the built-in policy, I was able to choose from a list of built-in application claims (like displayName and jobTitle), and select which ones I wanted to be returned in the token when the user signed in.
Now I'm setting up the custom policy I want to do the same thing, but I can't get it to work.
So far, in TrustFrameworkBase I have added a ClaimType of jobTitle:
<ClaimType Id="jobTitle">
<DisplayName>Job Title</DisplayName>
<DataType>string</DataType>
<UserHelpText>Job title.</UserHelpText>
</ClaimType>
I've added the following OutputClaim to the TechnicalProfile with ID login-NonInteractive:
<OutputClaim ClaimTypeReferenceId="jobTitle" PartnerClaimType="jobTitle" />
And I've added the following OutputClaim to the TechnicalProfile with ID SelfAsserted-LocalAccountSignin-Email:
<OutputClaim ClaimTypeReferenceId="jobTitle" />
But the jobTitle claim doesn't come through with the others in the token. I've done the same for given_name and that does work. If I change the first OutputClaim to:
<OutputClaim ClaimTypeReferenceId="jobTitle" PartnerClaimType="given_name" />
then a jobTitle claim does come through, but with the value of the given_name claim. This implies I'm just using the wrong PartnerClaimType but there doesn't seem to be a list of them anywhere.
How can I get the built-in job title attribute to be returned as a claim in the token when the user signs in using their local B2C account?
If you only want to read the jobTitle claim (or other claims) for the user and then issue it (or them) in the token, then you must:
1) Declare the jobTitle claim:
<ClaimType Id="jobTitle">
<DisplayName>Job Title</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="job_title" />
<Protocol Name="OpenIdConnect" PartnerClaimType="job_title" />
</DefaultPartnerClaimTypes>
</ClaimType>
2) Add the jobTitle claim as an output claim to the AAD-UserReadUsingObjectId technical profile:
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="jobTitle" />
</OutputClaims>
...
</TechnicalProfile>
3) Add the jobTitle claim as an output claim to the relying party technical profile:
<RelyingParty>
...
<TechnicalProfile Id="PolicyProfile">
...
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="jobTitle" />
</OutputClaims>
...
</TechnicalProfile>
</RelyingParty>

Azure AD B2C pre-populate a custom attribute in the SignUp policy

Does Azure AD B2C support pre-populating a custom attribute in the SignUp Policy when called from the Web application (ASP.Net MVC)?
We can create a custom SignUp attribute but we weren't able to find a specification in the documentation how to pass value to populate the custom attribute. If this is not supported out of the box, does anybody found a workaround?
Here are some more details for the context in case somebody has faced a similar scenario and found a useful solution:
We explore the options to solve the following scenario with Azure AD B2C: a registered user invites another person to signup to the application by sending an invitation email which has the url to the application’s login page along with a special invitation code(guid) as a query param, so it can click on the link and to be redirected to the Signup page. After the invited person creates an account, we need to use the code in order to associate the newly created user to the user who sent the invitation.
Currently this is implemented in the ASP.Net using the default identity provider (storing the user data in database with AspNet... tables). With replacing the local identity provider with the Azure AD B2C, we are loosing the context during the round-trip to the Azure AD B2C Signup page. The user clicks on the link on the email and gets to the SIgnUp page but the invitation code is not pre-populated.
A working sample of an invitation flow 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. It could also contain the invitation code.
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. It could also pass the invitation code.
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 registers the local account with the verified email address:
<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>
Before the local account is registered by the AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists validation technical profile, the CreateEmailFromVerifiedEmail claims transformation copies the verified_email claim to the email claim:
<ClaimsTransformation Id="CreateEmailFromVerifiedEmail" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_VerifiedEmail" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
To save the invitation code against the local account, you must:
Add the "extension_InvitationCode" claim to the claims schema
Add it as an input claim to the Invitation policy
Add it as an input claim to the LocalAccount-Registration-VerifiedEmail technical profile
Add it as a persisted claim to the AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExist technical profile

Resources