How can I get a value of an open / directory extension in an Azure AD B2C custom policy? - azure-active-directory

I'm using Microsoft Graph API to set a custom value (string) on a user.
I've tried using both open & directory extensions to store the data and both seem to work fine in the API level as I'm able to get the data back on a user.
What I'm trying to do next is to get the value of this extension into a B2C custom policy claim. I haven't been able to find any documentation that shows how to get an open extension's value in a custom policy even though the docs state it is supported so I've tried doing it with a directory extension.
The extension's name is extension_{appId}_myString and was created through this HTTP call:
POST https://graph.microsoft.com/v1.0/applications/graph-app-object-id/extensionProperties
{
"name": "myString",
"dataType": "String",
"targetObjects": [
"User"
]
}
I've added the claim type definition as follows:
<ClaimType Id="myString">
<DisplayName>This is my string</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OpenIdConnect" PartnerClaimType="extension_myString" />
<Protocol Name="OAuth2" PartnerClaimType="extension_myString" />
</DefaultPartnerClaimTypes>
</ClaimType>
My user journey definition:
<UserJourney Id="SignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.selfasserted">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingID" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
I've added this output claim in the technical profiles of steps 1 & 2:
<OutputClaim ClaimTypeReferenceId="myString" PartnerClaimType="extension_myString" />
My AAD-Common technical profile has the b2c-extensions-app client ID & object ID metadata items and I'm able to successfully complete the authentication process and get to the JWT screen but my extension's value isn't shown there.
I don't understand if I did something wrong or just missed another place in which the custom claim should be added in order for it to be shown in the token. Is there some kind of way to "print" the value of the claim to see if it's present in the policy's runtime? At least I know the value is there and I only need to put it in the token..
One more thing that I'm not sure of is the creation of the extension value through Graph API. I've used the graph application's credentials to get the Graph API token and also used it's object ID to create the directory extension on the user object. On the other hand, there's the b2c-extensions-app which is the app that queries the data during the custom policy's runtime. From my understanding both apps work against the same Active Directory instance so it shouldn't be a problem but maybe I misunderstood something.

You can’t. Open extensions are supported by MS Graph API. B2C policy uses AAD Graph behind the scenes, it can only fetch the old extension attribute type. To fetch an open extension, you’d have to call your own api, which in turn calls MS Graph API to fetch this open extension.
Instead of using an open extension use the older type:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-operations#application-extension-directory-extension-properties
https://learn.microsoft.com/en-us/graph/api/resources/extensionproperty?view=graph-rest-1.0

For configuring a custom claim in Azure B2C custom profile, as per https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes?pivots=b2c-custom-policy#create-a-custom-attribute-through-azure-portal, you must prefix the claim type ID with extension_ to allow the correct data mapping
Within Claims Schema the claim should be defined as:
The following example demonstrates the use of a custom attribute in Azure AD B2C custom policy in a technical profile, input, output, and persisted claims.
Thanks

Related

Multiple open id connect ad providers in a CMS setup

The case
Currently trying to setup two AD providers (both on OpenID Connect (OIDC) protocol).
One would need to handle users for the back office and the other should be used in all other cases to login external users with different roles. Content on the website is only accessible for certain roles.
The setup
EPiServer (CMS)
.Net 4.7.2 framework
Important notes:
Both open id connect middlewares are currently set to authentication mode 'active' .
Status
Both AD providers can be challenged directly where the right login portal shows, however there are some problems i cannot explain. Working with two open id connect middelwares is a first for me.
Problem 1
In a CMS setup certain location/paths are defined in the web.config like the following
<location path="episerver">
<system.web>
<authorization>
<allow roles="WebEditors, WebAdmins, Administrators" />
<deny users="*" />
</authorization>
</system.web>
When the '/episerver' path is hit it triggers an authentication flow. If one middleware has been marked to run in the pipeline stage 'authentication' that middleware seems to always handle such requests, however if both middleware are registered to run in same pipeline stage 'authentication' the last registered middleware (in the owin startup file) always seems to handle all requests. Why is that ?
Problem 2
When the first middleware is registered to run for the authentication pipeline stage, and the second is registered to run for a later pipeline stage (the default seems to be 'PreRequestHandlerExecute' when its not specified) another strange behaviour happens.
When any controller that has a '[Authorize]' attribute assigned is hit with a request, my guess would be the first middleware would be challenged because it is the first 'active' middleware defined in the Owin pipeline, however it is the second middleware that is hit. I can't find any explaination on this strange behaviour ?
You need to configure the back-office azure ad config to only trigger on /episerver and the regular users on everything else in startup.cs
Both can use the same cookie auth setup but will need 2 instances of OpenIdConnectAuthenticationOptions, one for each azure ad.
So you can do like this:
app.MapWhen(!ctx.Request.Path.StartsWith("/episerver"), cmsApp =>
{
cmsApp.UseOpenIdConnectAuthentication(cmsOptions);
}
app.Map("/episerver", editorApp =>
{
editorApp.UseOpenIdConnectAuthentication(editorOptions);
}

Azure AD B2C custom Sign Up invitation policy is returning 401 when trying to hit metadata endpoint

I implemented what is described here: Signup with email invitation
This works flawlessly when the Azure app service uses the B2C Azure AD for authentication\login, that is, the invitation e-mail is sent when I'm logged in through an account authenticated by the B2C AD.
Now I have another app service pointing to a B2B Azure AD for user login\authentication and so the invitation e-mail is sent when I'm logged in through an account authenticated by the B2B AD.
The email with the invitation link that points to the B2C AD is sent correctly. However after clicking the link, the B2C signup invitation policy takes action and I'm presented with a 401 error (unauthorized). This looks like the B2C policy can't access the metadata endpoint set in the policy configs for any reason...
The message being displayed is:
Correlation ID: 78292d04-7184-42d0-ac2d-a60bb98a532f
Timestamp: 2019-09-20 16:19:25Z AADB2C: The metadata endpoint
'https://mywebsite.azurewebsites.net/.well-known/openid-configuration'
returned the following status code: '401'
This is the custom signup invitation policy <ClaimsProvider> in which this metadata endpoint is specified:
<!--This technical profile specifies how B2C should validate the token, and what claims you want B2C to extract from the token.
The METADATA value in the TechnicalProfile meta-data is required.
The “IdTokenAudience” and “issuer” arguments are optional (see later section)-->
<ClaimsProvider>
<DisplayName>ID Token Hint ClaimsProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName>ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<!-- Action required: replace with endpoint location -->
<Item Key="METADATA">https://mywebsite.azurewebsites.net/.well-known/openid-configuration</Item>
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<!-- <Item Key="issuer">your_optional_issuer</Item> -->
</Metadata>
<OutputClaims>
<!--Read the email claim from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="shardTenantId"/>
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
My question is: why this 401 (unauthorized) is being thrown? When I try to access the metadata endpoint: https://mywebsite.azurewebsites.net/.well-known/openid-configuration it opens just fine and returns the required json output:
{
issuer: "https://mywebsite.azurewebsites.net/",
jwks_uri: "https://mywebsite.azurewebsites.net/.well-known/keys",
id_token_signing_alg_values_supported: [
"RS256"
]
}
This is the web API method that outputs the json described above:
[AllowAnonymous]
[Route(".well-known/openid-configuration", Name = "OIDCMetadata")]
[HttpGet]
public HttpResponseMessage Metadata()
{
var json = JsonConvert.SerializeObject(new OidcModel
{
// The issuer name is the application root path
Issuer = InvitationHelper.GetBaseUrl(),
// Include the absolute URL to JWKs endpoint
JwksUri = Url.Link("JWKS", null),
// Sample: Include the supported signing algorithms
IdTokenSigningAlgValuesSupported = new[] { SigningCredentials.Value.Algorithm }
});
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
}
I even tried adding [AllowAnonymous] annotation to the API method but that didn't make any change.
Why can't the custom signup invitation policy access the metadata endpoint that is in an app service hosted on a different B2B AD tenant?
NOTE 1: added Application Insights instrumentation to this custom policy and the only message I saw there was Fatal Exception 401 without any more valuable info:
{"Kind":"FatalException","Content":{"Time":"4:15 PM","Exception":{"Kind":"Handled","HResult":"80131500","Message":"The
metadata endpoint
'https://mywebsite.azurewebsites.net/.well-known/openid-configuration'
returned the following status code:
'401'","Data":{"IsPolicySpecificError":false,"uri":"https://mywebsite.azurewebsites.net/.well-known/openid-configuration"}​}}​}
NOTE 2: if you look at the <ClaimsProvider> above there are these 2 settings:
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<!-- <Item Key="issuer">your_optional_issuer</Item> -->
I'm not sure how to use these other 2 settings as those were not described in the doc # GitHub.
Maybe the issuer is the problem... I'm trying things here but nothing is working so far. So I decided to ask this question. I hope someone can shed some light.
I got in contact with Jas Suri through LinkedIn. He's the guy that did the last commit in that GitHub repo.
He asked me for the real metadata endpoint (the one I posted here is a dummy URL).
When he tried to access that URL he was prompted to login... what!? Login!? Yep. That answered the question. I got SSO here on my developer box and that's why I didn't catch that... but wait: I had [AllowAnonymous] annotation in that web API method, right? So why was it still asking for login!?
He promptly asked me if I had enabled Authentication on the app service level... I went to Azure Portal and checked the app service Authorization/Authentication settings. Guess what... I had it turned ON but the problem was not that directly. See the highlighted setting below:
We can keep it on, but the real problem was that sometime ago I had set
Action to take when request is not authenticated
Log in with Azure Active Directory
That was overrides the [AllowAnonymous] annotation placed on the web API method.
As soon as I selected in the dropdown:
Allow Anonymous requests (no action)
and saved, then the B2C policy started working as expected.
Anonymous access is enabled on the App Service app. Users will not be
prompted for login.

AADSTS700016: Application with identifier 'some_id' was not found in the directory 'some_another_id'

I need a federated authentication with custom policy (when user authenticated I need him to appear marked as Federated in b2c users, not Others or something else what I could achieve with single tenant), I had it before with default policy setup in azure as OpenId provider, but did not find how to do FEDERATION Authentication with OpenId in custom policy, so I did it with SAML and below what I've got.
I tried Single tenant and it is not what I need. Also problem is not with signing key, because I've already had this issue and resolved this. I created self signed certificate, uploaded it to AAD application first and to b2c policy keys after.
I think the application which authenticate the federation is not an issue, because I works with default policy.
<TechnicalProfile Id="Office-SAML2">
<DisplayName>Microsoft Office 365</DisplayName>
<Description>Login with your ADFS account</Description>
<Protocol Name="SAML2"/>
<Metadata>
<Item Key="WantsSignedRequests">false</Item>
<Item Key="PartnerEntity">https://login.microsoftonline.com/<b2c tenant id>/federationmetadata/2007-06/federationmetadata.xml</Item>
<Item Key="XmlSignatureAlgorithm">Sha256</Item>
</Metadata>
<CryptographicKeys>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_SamlCert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_SamlCert"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name"/>
<OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name"/>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email"/>
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="https://sts.windows.net/<b2c tenant id>/" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
I have been doing everything through this reference
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-custom-setup-adfs2016-idp
I ran in to this problem because I was using the ID of in the Client secret section instead of using the Application (client) ID.
I have faced this error below are the possibilities:
This error means you have made a mistakes while configuring the Client ID OR tenant ID in your code, First please confirm that both are same as your azure portal application client and tenant.
In my case i had entered wrong client id in my code.
Please check this https://github.com/MicrosoftDocs/azure-docs/issues/24673
The problem here is that the AAD Application Registration has an identifier URI that does not match that being requested in the authentication request. You can fix this by:
Copying the URL in the obscured screenshot
Navigating to Azure AD Blade in the Azure AD Tenant to which you want to federate with 3. Access the Enterprise Applications menu, find the App Registration by ApplicationId(ClientId)
Open the Single Sign-On menu.
Paste the URL from the error into the Identifier (Entity ID) option
You also need the correct Reply URL:
https://contoso.b2clogin.com/te/contoso.onmicrosoft.com/<policyd of Base>/samlp/sso/assertionconsumer
You are much better off using OpenId (which also allows for multi tenant access):
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-setup-aad-custom
For me, the problem was that my application Identifier (Entity ID) from Basic SAML Configuration menu, didn't match with the one declared by the application provider.
Adding to this, for me I had to select the following option from the Authentication options.
Supported Account Types Option

Azure AD B2C Sign-in Custom Policy remember user

We have a Sign-in Custom Policy setup in Azure AD B2C that customers use to log in our application.
In the Standard B2C policies, users are rememberd and a menu is provided with the list of email addresses that have logged in from a particular machine (and the option to forget them), as in the following screenshot:
List of Users
Our Custom Sign-in Policy works but users have to re-enter their email address every time from their machines. How can we achieve the same behaviour with a Custom Policy?
Here is the technical profile:
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<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="SignUpTarget">SignUpWithLogonEmailExchange</Item>
<Item Key="setting.operatingMode">Email</Item>
<Item Key="setting.showSignupLink">False</Item>
<Item Key="setting.showContinueButton">True</Item>
<Item Key="setting.showCancelButton">True</Item>
<Item Key="EnforceEmailVerification">False</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</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>
EDIT:
Having played around with the new "User Flows" which are currently in the Preview phase in Azure AD B2C, it seems that there are two different versions of the Sign in user journey that are offered:
Sign in: which does not allow for UI customisation (aside from the "Company Branding" feature) and is consistent with the behaviour that I report in the screenshot above.
Sign in v2: Which allows for UI customisation with custom cshtml pages and is consistent with the behaviour I observe when using custom policies. This type of Sign in user flow is not visible or accessible from the existing built-in policies at the moment.
So it seems that the Sign in v2 is the version that is used within custom policies for the sign in user journeys, as it is the version that allows for UI customisation.
SignInV1 vs SignInV2
Built-in policies have been rebranded as User flows. The new ui of Azure Ad B2C is moving into GA. It has some v2 user flows which are in preview currently and will be moved to GA in due course of time.
There is a difference in the way SignInv1 and SignInv2 user flows work. SignIn v1 'interactively' federates with default sign-in experience of Azure Active Directory (AAD), which has "Keep me sign in" (KMSI) feature. SignInV2 has additinal feature of UX customizability. To provide that B2C 'non-interactively' federates with AAD.
As a result of this customizability, there is currently a feature gap between signinv1 and v2
- KMSI
- Forced password reset.
There are plans to work on these features.
Custom policies
This page will tell how to enable KMSI for custom policies.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-reference-kmsi-custom

How to direct B2C Federated user to Microsoft common login endpoint

I am using Azure AD B2C and have added sign in/sign up with custom templates and policies. I am using msal.js on the frontend.
When any user tries to login to our application, we have process to know if the domain is already federated with Microsoft B2C. Once we identify the user, we want to direct the user to Microsoft common login endpoint by passing the login_hint, so that they are forced to use work account and get the id_token.
I am trying to get the endpoint working (need to pass login_hint, don't know how)
directauthority ="https://login.microsoftonline.com/common/oauth2/authorize?"+ this.Config.signUpSignInPolicy;
clientApplication = new Msal.UserAgentApplication(
this.tenantConfig.clientID, this.directauthority,
);
I got an error "endpoints_resolution_error:Endpoints cannot be resolved"
How can I resolve this?
Please suggest
Thanks,
You can add the "domain_hint" and "login_hint" parameters to the Azure AD B2C request:
https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize?p=<policy>&...&domain_hint=onmicrosoft.com&login_hint=me#contoso.com
If you set the "domain_hint" parameter to the Azure AD technical profile's <Domain /> value, then Azure AD B2C will redirect to the Azure AD endpoint without showing the sign-up or sign-in page to the end user.
To pass the "login_hint" parameter through from Azure AD B2C to the Azure AD endpoint:
1) Create a "loginHint" claim type:
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="loginHint">
<DisplayName>Login Hint</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
2) Add the "loginHint" input claim to the Azure AD technical profile:
<ClaimsProviders>
<ClaimsProvider>
<Domain>onmicrosoft.com</Domain>
<DisplayName>Work Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="WorkProfile">
<DisplayName>Work Profile</DisplayName>
<Protocol Name="OpenIdConnect"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
...
</Metadata>
<CryptographicKeys>
...
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="loginHint" PartnerClaimType="login_hint" DefaultValue="{OIDC:LoginHint}" />
</InputClaims>
<OutputClaims>
...
</OutputClaims>
<OutputClaimsTransformations>
...
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
The "loginHint" input claim references a claims resolver that sets this input claim value to the "login_hint" parameter of the Azure AD B2C request.

Resources