Multi tenant Azure B2C Login - how to get external user email address - azure

I am struggling to figure out how to configure Azure B2C for multi-tenant authentication, in particular getting access to the email address of a user that is logging in via an external Azure AD (we're interested in allowing our customers to log in either via a "Local Account" (email address, managed by B2C) or their own Azure AD).
A key part of the issue I am trying to result is the passing of the logged in users email address through to a REST endpoint where our application needs to do some things internally to inject additional application specific claims, which are used later on. Apart from our REST endpoint receiving the email address, everything else is working.
I've got a "Common AAD" technical profile setup like this:
<TechnicalProfile Id="Common-AAD">
<DisplayName>Work Account</DisplayName>
<Description>Login with your Work Account</Description>
<Protocol Name="OpenIdConnect"/>
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration</Item>
<Item Key="client_id">my_client_id</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid email profile</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" 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="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="signInName" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="upn" PartnerClaimType="upn" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>
</TechnicalProfile>
In the orchestration, I am instructing B2C to pass in a bunch of these claims to an application-hosted REST API so that we can do our internal processing:
<TechnicalProfile Id="REST-GetProfile-Dev">
<DisplayName>Do some custom logic</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://the-endpoint.com</Item>
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="objectId" />
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="sub" />
<InputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<InputClaim ClaimTypeReferenceId="userPrincipalName" />
<InputClaim ClaimTypeReferenceId="displayName" />
<InputClaim ClaimTypeReferenceId="otherMails" />
<InputClaim ClaimTypeReferenceId="upnUserName" />
<InputClaim ClaimTypeReferenceId="alternativeSecurityId" />
<InputClaim ClaimTypeReferenceId="upn" />
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="socialIdpUserId" />
<InputClaim ClaimTypeReferenceId="identityProvider" />
<InputClaim ClaimTypeReferenceId="authenticationSource" />
</InputClaims>
<OutputClaims>
<!-- bunch of app specific claims -->
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
However, I can never seem to get an email address, or anything that contains the email address of the logged in user passing through.
I've tried to track through the processing that's defined in the Custom Policy XML files, and it's challenging. To be honest, I've been researching this and have tried adding all kinds of additional claims to outputs from various steps, but it's just not working for me.
Any help in detailing how to get the email address of a user logged in via an external Azure AD passed into a REST orchestration step would be much appreciated.
Thanks.
** Big Edit **
In response to Jas Suri, I have reset everything, applied the changes below as suggested, but am still not seeing this work.
Here's my TrustFrameworkBase.xml:
TrustFrameworkBase.xml
Here's my TrustFrameworkExtensions.xml:
TrustFrameworkExtensions.xml
Here's my Relying Party (SignInSignUpMulti.xml) file:
SignInSignUpMulti.xml
Now, looking at my scenarios:
When I sign in using a "local" account, I see this type of information pass through to my rest endpoint during the user journey:
{
"objectId": "1e91bfba-17a1-43b6-a451-9896fc3c1061",
"signInNames.emailAddress": "email#example.com",
"displayName": "User DispName",
"signInName": "email#example.com",
"authenticationSource": "localAccountAuthentication"
}
That's perfect. I can take this information and gather additional claims to return and all works exactly as I want.
When I sign in as an AD Account that's attached to my org, I get this:
{
"objectId": "a_guid",
"sub": "Not supported currently. Use oid claim.",
"userPrincipalName": "cpim_a_guid#TENANT.onmicrosoft.com",
"displayName": "ThisIs Correct",
"upnUserName": "14218711-5dd1-4a81-8e04-77bd08298aaf",
"alternativeSecurityId": "{\"type\":6,\"identityProvider\":\"https://login.microsoftonline.com/a_guid/v2.0\",\"key\":\"a_key\"}",
"identityProvider": "https://login.microsoftonline.com/a_guid/v2.0",
"authenticationSource": "socialIdpAuthentication"
}
I am missing an email (or the users Sign In) address.
And, the same happens when I try to sign in as an external AD:
{
"objectId": "a_guid",
"sub": "Not supported currently. Use oid claim.",
"userPrincipalName": "cpim_a_guid#TENANT.onmicrosoft.com",
"displayName": "ThisIs Correct",
"upnUserName": "9c865de4-2898-4b18-998b-7fa151f6623d",
"alternativeSecurityId": "{\"type\":6,\"identityProvider\":\"https://login.microsoftonline.com/a_guid/v2.0\",\"key\":\"a_key\"}",
"identityProvider": "https://login.microsoftonline.com/a_guid/v2.0",
"authenticationSource": "socialIdpAuthentication"
}
If I can work out how to pass through the email address or signin address, then I would be extremely happy.
Event during debugging, if I cause the user to be signed in anyway, I inspect the User.Identity, and while I see claims that my rest api is returning during the journey, I still don't have any claim that resembles the email address I am expecting (hoping) to see.
I can definitely work either way - an email address passed to the REST API, or the email address appearing in the final claim set that the application receives.
Any assistance would be much appreciated.

From Azure AD, all users will come back with a unique_name claim, which is the UPN in their Azure AD. You could also rely on this. If you rely on the email claim from AAD, it will only be present if the user has an Exchange Online inbox. You also have to set it up as an optional Azure AD claim in the AAD Multi Tenant App registration.
Usually the UPN and Email are the same in an Azure AD. So in the AAD technical profile, you could add this output claim to capture the AAD UPN:
<OutputClaim ClaimTypeReferenceId="aadUPN" PartnerClaimType="unique_name"/>
Then in the relying party secion, add this output claim:
<OutputClaim ClaimTypeReferenceId="aadUPN" PartnerClaimType="UPNfromAAD"/>

Related

Azure AD B2C AAD-UserReadUsingAlternativeSecurityId technical profile is throwing error

I have a custom policy with below technical profile to enable login from multiple Azure AD tenants. When I login with a valid work account the technical profile AAD-UserReadUsingAlternativeSecurityId is throwing an error. I have included the app insights output below. Since I have no control on how Azure AD B2C builds the graph api lookup within the technical profile, how can I go about finding and fixing the actual issue?
I checked the claim transformation that builds up the alternative security ID and found nothing, followed the starter pack and did not change anything
<TechnicalProfile Id="Common-AAD">
<DisplayName>Active Directory</DisplayName>
<!-- <DisplayName>Sign In</DisplayName> -->
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="client_id">dcaee4b4-61d6-45e6-88b2-b35a81e93077</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="METADATA">https://login.microsoftonline.com/common/.well-known/openid-configuration</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The key below allows you to specify each of the Azure AD tenants that can be used to sign in. Update the GUIDs below for each tenant. -->
<Item Key="ValidTokenIssuerPrefixes">
https://sts.windows.net/{GUID1-masked},
https://sts.windows.net/{GUID2-masked},
https://sts.windows.net/{GUID3-masked},
https://sts.windows.net/{GUID4-masked}
</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<!-- <Item Key="ValidTokenIssuerPrefixes">https://sts.windows.net/</Item>-->
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AZB2CSecret" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
<!-- <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="sub" /> -->
<!-- Mark: issuerUserId below originally mapped to sub -->
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="unique_name" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<!-- <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" /> -->
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
"AAD Request to https://graph.windows.net/34d8169d-0e97-4cc7-a8d0-57c29404f1b1/users?api-version=1.6-integrationOnly&%24filter=alternativeSecurityIds%2fany(x%3ax%2ftype+eq+6+and+x%2fidentityProvider+eq+%27https%3a%2f%2fsts.windows.net%{GUID-MASKED}%2f%27+and+x%2fkey+eq+X%2738623361343134642D623331622D343638322D396662382D663461376161666164643966%27) using method GET as request body is malformed.\r\nResponse: \n{"odata.metadata":"https://graph.windows.net/34d8169d-0e97-4cc7-a8d0-57c29404f1b1/$metadata#directoryObjects","value":[]}\r\n",
And after policy runs the error that is displayed to the app/user is : AADB2C99002: An account could not be found for the provided user ID. which is normal I guess as the user look up fails
"Open your browser and go to the OpenID Connect metadata URL for the tenant. Find the issuer object and record its value. It should look similar to https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/.well-known/openid-configuration".
When I do that, I get an issuer like:
"https://login.microsoftonline.com/GUID/v2.0"
But you have:
https://sts.windows.net/{GUID1-masked}
(The documentation seems wrong anyway - their issuer is the "well-known" address).
You shouldnt be calling AAD-UserReadUsingAlternativeSecurityId, you chould instead call AAD-UserReadUsingAlternativeSecurityId-NoError in the case of social logins, like here.
See the social login journey here for comparison from the Starter Pack.
There is a full explanation here of how this works in the readme.

Is there a way for use UserName instead of email in Password Reset flow in Azure AD B2C?

I'm customizing the PasswordReset flow in azure ad b2c using custom policies, but i can't find a way for use UserName instead Email for restore password. I've tried to use input signInName instead of email in the technical profile AAD-UserReadUsingEmailAddress, but still shows the email in the form.
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" />
</InputClaims>
<OutputClaims>
<!-- Required claims -->
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<!-- Optional claims -->
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="accountEnabled" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertAccountEnabledIsTrue" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
It's possible to do this with userName?
This technical profile is the implementation to READ the account. What you are trying to achieve is to show the Username text box first and foremost. To display something on screen, you need to modify a selfAsserted technical profile.
The key is to change the operating mode to Username in the selfAsserted technical profile which asks the user for their identifer (which from the starter pack is: LocalAccountDiscoveryUsingEmailAddress), the latest key name is setting.operatingMode, reference here, set it to username. Then the textbox validation will be for username.
There is a complete sample here, and you can quick deploy using this link.

Find existing user by email in Azure Active Directory B2C Custom Policy

I'm creating a custom policy in Azure AD B2C to let invited users sign in via another Azure AD (or even ADFS). The problem I have is that a new user gets created when they sign in (rather than the invited user). I have found that I have been trying to find existing user in my AAD using alternativesecurityid or objectid and both of these are not matching. So I think I need to find an existing user by email, and not any IDs. This too, is not working, because I can see my invited user's email is sitting in otherMails and mail properties (via GraphAPI), and apparently I cannot query B2C via these fields.
<TechnicalProfile Id="AAD-ReadUserByEmail">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="emailFromSocialAccount" PartnerClaimType="mail" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
Only <InputClaim ClaimTypeReferenceId="emailFromSocialAccount" PartnerClaimType="signInNames.emailAddress" Required="true" /> passes validation, but this field is not having any data.
How do I find the invited user?

Any way to provide default values for inputs in an Azure AD B2C custom policy?

I am working on integrating Azure AD B2C into an existing shopping cart application, replacing an existing user identity solution.
I have already created a custom policy to implement user registration / sign-up and integrated it into the normal account creation process. However, I am having a problem with integrating registration during the checkout process.
With the old IDP, the checkout process first collects the user's name and email address. After that has been collected, the user is given the option to create an account with that information (if they are not already logged in). In order to avoid confusing double-entry of the email address, I would like to pass the email address that the user already entered to the B2C sign-up policy and have it fill in the email address input on the form.
Is there any way to do this? I don't find anything like this being addressed in the B2C documentation.
You can use a sign up policy with a claims resolver. Send the email in the query parameter. It’ll prepopulate the email into the text box.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview
Based on the documentation linked by #JasSuri, I was able to come up with a solution.
To implement this, you modify the sign-up technical profile. You must add three things:
Add item IncludeClaimResolvingInClaimsHandling with value true to the metadata
Add a DefaultValue attribute to the email input claim with an appropriate claims resolver notation as the value
Add an AlwaysUseDefaultValue attribute to the email input claim with true as the value
I used an Oauth2 key-value claims resolver (which supports arbitrary query string parameters) and a query parameter named register_email.
The resulting technical profile looks like this:
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item> <!-- ADD THIS -->
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<!-- ADD DefaultValue AND AlwaysUseDefaultValue ATTRIBUTES BELOW -->
<InputClaim ClaimTypeReferenceId="email"
DefaultValue="{OAUTH-KV:register_email}"
AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

How to add Slack as an Identity Provider in AAD B2C?

I'd like to add Slack as an Identity Provider to my app using AAD B2C. Is there any example of how to build the Technical Profile for that?
I was able to create a Technical profile to connect to Slack, but I'm having a problem with sending a user_scope parameter instead of the scope parameter. The OAuth2 technical profile only supports "scope" parameter and I'm not able to find a way of sending "user_scope" instead.
I was also able to connect using endpoints that didn't include "v2" in them, but I'm not sure that's the right approach
This is the technical profile I'm using in the ClaimsProvider now. I have anothe ClaimsProvider for AAD multitenant. Didn't modify the relying party at all for Slack.
<TechnicalProfile Id="Slack-OAUTH">
<DisplayName>Slack</DisplayName>
<Protocol Name="OAuth2" />
<Metadata>
<Item Key="ProviderName">slack</Item>
<Item Key="authorization_endpoint">https://slack.com/oauth/v2/authorize</Item>
<Item Key="AccessTokenEndpoint">https://slack.com/api/oauth.v2.access</Item>
<Item Key="ClaimsEndpoint">https://slack.com/api/users.identity</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="client_id">ClientID</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="REFERENCE_TO_SECRET_IN_B2C_KEYS" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="user_scope" DefaultValue="identity.basic" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="slack" />
<OutputClaim ClaimTypeReferenceId="user" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetIdClaimFromJson" />
<OutputClaimsTransformation ReferenceId="GetNameClaimFromJson" />
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
The call to slack works and I'm getting a code back and being redirected to aadb2c, but then I get an exception back. Put the policy in development mode to drop logs into App Insights and got an exception saying: An invalid OAuth response was received: '{0}'.
'Message':'The given key was not present in the dictionary.','Data':{}​
To enable sign-in for users from a Slack account, you need to create a Slack application project.
Sign in to the Slack API with your account credentials.
Select Create New App.
Enter an App Name and select a Development Slack Workspace.
Select Create App.
Under App Credentials, copy the values of Client ID and Client secret. You will need both of them to configure Slack as an identity provider in your tenant. Client secret is an important security credential.
Under Features, select OAuth and Permissions.
Add a new Redirect URL, enter https:/your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/oauth2/authresp.
Replace your-tenant-name with the name of your tenant. You need to use all lowercase letters when entering your tenant name even if the tenant is defined with uppercase letters in Azure AD B2C.
Click Save URLs.
Create a policy key
You need to store the client secret that you previously recorded in your Azure AD B2C tenant.
Sign in to the Azure portal.
Make sure you're using the directory that contains your Azure AD B2C tenant by clicking the Directory and subscription filter in the top menu and choosing the directory that contains your tenant.
Choose All services in the top-left corner of the Azure portal, and then search for and select Azure AD B2C.
On the Overview page, select Identity Experience Framework - PREVIEW.
Select Policy Keys and then select Add.
For Options, choose Manual.
Enter a Name for the policy key. For example, SlackSecret. The prefix B2C_1A_ is added automatically to the name of your key.
In Secret, enter your client secret that you previously recorded.
For Key usage, select Signature.
Click Create.
Slack claims provider as follows:
<TechnicalProfile Id="Slack-OAUTH">
<DisplayName>Log in with your Slack account</DisplayName>
<Protocol Name="OAuth2"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://slack.com/api/oauth.access</Item>
<Item Key="authorization_endpoint">https://slack.com/oauth/authorize</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="ClaimsEndpoint">https://slack.com/api/users.identity</Item>
<Item Key="client_id">Your Slack App Registration Client ID</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">identity.basic</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_slackfederation"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="slackAuthentication" />
<OutputClaim ClaimTypeReferenceId="user" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="slack" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetIdClaimFromJson" />
<OutputClaimsTransformation ReferenceId="GetNameClaimFromJson" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
And supporting claims transforms:
<ClaimsTransformation Id="GetIdClaimFromJson" TransformationMethod="GetClaimFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="slackOAuthResponse" TransformationClaimType="inputJson" />
</InputClaims>
<InputParameters>
<InputParameter Id="claimToExtract" DataType="string" Value="id" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" TransformationClaimType="extractedClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetNameClaimFromJson" TransformationMethod="GetClaimFromJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="slackOAuthResponse" TransformationClaimType="inputJson" />
</InputClaims>
<InputParameters>
<InputParameter Id="claimToExtract" DataType="string" Value="name" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="extractedClaim" />
</OutputClaims>
</ClaimsTransformation>
I have been struggling with this same situation where user_scope isn't a valid parameter in my .net core web app.
I tried setting the auth endpoint to:
https://slack.com/oauth/v2/authorize?user_scope=identity.basic+identity.email+identity.team
and that did get past one hurdle, but after I sign in and am redirected back to my web app, it fails to process the claims. I'll update this partial answer if I can get it working, but hopefully, it will help someone figure this out.

Resources