AAD B2C - Mapping SAML Attribute containing True or False to Claim with Boolean Data Type - saml-2.0

I am attempting to define an attribute that will be returned by a SAML 2.0 Identity Provider to AAD B2C and handled in a custom policy.
The goal is that the SAML attribute defines a boolean value and that AAD B2C is able to perform logic based on the value of the claim.
The SAML Attribute represents True or False as per the below:
<saml:Attribute Name="http://schemas.custom/claim/booleanexample" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>True</saml:AttributeValue>
</saml:Attribute>
When attempting to map this SAML Attribute to a claim defined within the Custom Policy ClaimsSchema with a data type of boolean, an error is thrown.
Message in App Insights:
"The data type 'String' of claim with id 'http://schemas.custom/claim/booleanexample' does not match the Data Type 'Boolean' of claimType with id 'BooleanExample' specified in the policy."
<ClaimType Id="BooleanExample">
<DataType>boolean</DataType>
</ClaimType>
I cannot see any mechanism to explicitly define a type for the attribute within SAML (it appears that AttributeValue should be of type xs:any).
I have attempted passing the usual values including 0/1, True/False, true/false with no luck.
Is it really required to handle this with a String claim, and then populate a Boolean claim using a Claims Transformation?

In the event that it helps anyone:
It appears that the claim must be mapped as a string, inspected using a CompareClaimToValue claims transformation that sets a boolean claim with the result of the comparison.
<ClaimsTransformation Id="CreateBooleanClaimFromString" TransformationMethod="CompareClaimToValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="samlBooleanClaimAsString" TransformationClaimType="inputClaim1" />
</InputClaims>
<InputParameters>
<InputParameter Id="compareTo" DataType="string" Value="true" />
<InputParameter Id="operator" DataType="string" Value="equal" />
<InputParameter Id="ignoreCase" DataType="string" Value="false" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="samlBooleanClaimAsBoolean" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>

Related

Using ADB2C phone factor technical profile to validate a phone number before creating an account

I'm trying to create a custom ADB2C policy which should follow a journey such as...
User enters a phone number to verify
User receives a code to verify the number and enters this on the view
A claim is set which is used in the next step to save to AD.
I can see I need to use the phone factor technical profile as per the Microsoft docs, but I couldn't see whether this could be done before a user has signed up as it seems to want a UserID as an input claim (see Input claims required). All the samples seem to point to MFA and editing phones already associated to a user.
Is it possible to do this?
Secondly, is there any value in mixing the one-time password technical profile in this flow or is that more for just OTP code generation (not sending and accepting the inputs from the user)?
Update
So Ive managed to create the user journey which shows the UI correctly so it seems I can enter a phone number but now when I submit it I get an error "AADB2C90154: A multi-factor verification request failed to get a session id from the service."
As far as I can tell, the session technical profile is fine so not sure what the problem could be?
I also tried disabling the session but its seems like the phone tech profile needs it?
Sample technical profile
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
<Item Key="setting.authenticationMode">sms</Item>
<Item Key="setting.autodial">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" DefaultValue="TEST" />
<InputClaim ClaimTypeReferenceId="phoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="verifiedPhone" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newVerifiedPhone" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
</TechnicalProfile>
#Raj,
I don't see how you are setting the userIdForMFA other than the default value? However, the Azure documentation isn't very clear here. Try using an inputClaimsTransformation that sets the userId Value.
For example:
Add this to the MFA technical profile:
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA2" />
</InputClaimsTransformations>
Add this to your claims transformation:
<ClaimsTransformation Id="CreateUserIdForMFA2" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="test12345#{YourTenant}.onmicrosoft.com"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userIdForMFA" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>

B2C SAML missing claims

I'm attempting to use B2C Custom Policies to configure B2C as my SAML Idp. As a test I've set up our on premise ADFS environment as the SAML RP which seems to be required in order for the B2C logon page to work (B2C SAML doesnt support an Idp initiated session).
I've been following the guides at https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-get-started-custom and https://github.com/Azure-Samples/active-directory-b2c-advanced-policies/blob/master/Walkthroughs/RP-SAML.md to set up my B2C environment.
I get as far as showing the B2C sign-in page that the end-user receives however after I enter the credentials of an account into the B2C sign-in page I get redirected back to my RP with a SAML token however it is not parsing any of the configured claims.
The SAML token is showing the following error:
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder" />
<samlp:StatusMessage>Id:410906d7-639d-4828-b28d-22f84dfa617b ; Message: Policy &apos;B2C_1A_signup_signin_saml' in tenant ' mytenant.onmicrosoft.com'' specifies the claim 'sub' for the SubjectNamingInfo, but the claim is either not present or is null.</samlp:StatusMessage>
<IsPolicySpecificError>true</IsPolicySpecificError>
</samlp:Status>
My SignUpOrSigninSaml.xml RP config is as follows:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInSaml"/>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2" />
<Metadata>
<Item Key="PartnerEntity">https://adfs-test.mycorporation.com.au/FederationMetadata/2007-06/FederationMetadata.xml</Item>
<Item Key="KeyEncryptionMethod">Rsa15</Item>
<Item Key="DataEncryptionMethod">Aes256</Item>
<Item Key="XmlSignatureAlgorithm">Sha256</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
</OutputClaims>
<!-- The ClaimType in the SubjectNamingInfo element below is a reference to the name of the claim added to the claims bag used by the token minting process.
This name is determined in the following order. If no PartnerClaimType is specified on the output claim above, then the DefaultPartnerClaimType for the protocol specified in the claims schema if one exists is used, otherwise the ClaimTypeReferenceId in the output claim is used.
For the SubjectNamingInfo below we use the DefaultPartnerClaimType of http://schemas.microsoft.com/identity/claims/objectidentifier, since the output claim does not specify a PartnerClaimType. -->
<!-- <SubjectNamingInfo ClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/> -->
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
I've tried a few different configurations for the "SubjectNamingInfo" such as:
<SubjectNamingInfo ClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
<SubjectNamingInfo ClaimType="sub" />
<SubjectNamingInfo ClaimType="name" />
But they all produce the same error.
I believe that once the SubjectNamingInfo issue is sorted the configured OutputClaims will show.
Anyone know how I can resolve this so I am able to see the claims of my user account in the token?
---Edit---
I attempted to add "sub" as an output claim however due to it not being defined in the Base file B2C wont allow it. Alternatively I tried changing the subject naming info to a claim that is already defined as an Output claim
<SubjectNamingInfo ClaimType="givenName" />
However I still seem to get the same error:
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder" />
<samlp:StatusMessage>Id:a3fe7ab0-4483-45b6-93f8-e75b539a3aea ; Message: The relying party technical profile of policy &apos;mytenant.onmicrosoft.com' in tenant 'B2C_1A_signup_signin_saml' specifies the claim type 'givenName' as the subject naming info claim, but the claim is not present or is null.</samlp:StatusMessage>
<IsPolicySpecificError>true</IsPolicySpecificError>
It isn't common to use the sub claim as the <saml:Subject><saml:NameID> element for a SAML assertion.
It is recommended to use the objectId claim as follows.
1) Ensure the objectId claim is declared with a partner claim for the SAML2 protocol:
<ClaimType Id="objectId">
<DisplayName>Object Identifier</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="oid" />
<Protocol Name="OpenIdConnect" PartnerClaimType="oid" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" />
</DefaultPartnerClaimTypes>
</ClaimType>
2) Add the objectId claim to the <OutputClaims /> collection for the relying party technical profile and set the SubjectNamingInfo element:
<RelyingParty>
<TechnicalProfile Id="PolicyProfile">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
</OutputClaims>
<SubjectNamingInfo ClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
More information about SubjectNamingInfo
The ClaimType attribute for the SubjectNamingInfo element references a claim type that must be declared as an output claim for the technical profile.
This claim type is referenced by name with the following precedence:
1) If the PartnerClaimType attribute for the OutputClaim element is specified, then the ClaimType attribute for the SubjectNamingInfo element must be set to the ClaimTypeReferenceId attribute for this OutputClaim element:
<RelyingParty>
<TechnicalProfile Id="PolicyProfile">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" />
...
</OutputClaims>
<SubjectNamingInfo ClaimType="objectId" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
2) If the PartnerClaimType attribute for the OutputClaim element is not specified, then the ClaimType attribute for the SubjectNamingInfo element must be set to the DefaultPartnerClaimType attribute for the ClaimType element that is referenced by the ClaimTypeReferenceId attribute for this OutputClaim element:
<ClaimType Id="objectId">
<DefaultPartnerClaimTypes>
<Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" />
</DefaultPartnerClaimTypes>
</ClaimType>
<RelyingParty>
<TechnicalProfile Id="PolicyProfile">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
...
</OutputClaims>
<SubjectNamingInfo ClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>

How to select only specific active directory groups to sync with Sitecore

Let say the Active Directory have Group A, B and C.
How to specified like only Group A sync to Sitecore?
Thanks for any help! :)
If you just want to get the members in a specific group, you can do this using a customFilter.
If for membership, you can add the following under your membership element in the web.config:
<add name="ad"
type="LightLDAP.SitecoreADMembershipProvider"
connectionStringName="ManagersConnString"
applicationName="sitecore"
minRequiredPasswordLength="1"
minRequiredNonalphanumericCharacters="0"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="false"
connectionUsername="user"
connectionPassword="12345"
attributeMapUsername="sAMAccountName"
enableSearchMethods="true"
customFilter="(memberOf=cn=test role 1,OU=CRM,DC=VM)"
/>
Just ensure that you have a connection string configured for your AD, which would be something like: <add name="ManagersConnString" connectionString="LDAP://testsrv/OU=Managers,DC=testdomain,DC=sitecore,DC=net" />
And ensure that your custom filter gets to the specific group you're trying to allow access for. I used LDAP Browser to navigate to my groups using a simple GUI and then copied the path.
See more in the documentation about customFilters in section 4.1.
Try to specify your group in the connection string:
<connectionStrings>
<add name="ManagersConnString"
connectionString="LDAP://testsrv/OU=Managers,DC=testdomain,DC=sitecore,DC=net" />
</connectionStrings>
This example is copied from the Documentation (see chapter 2.1.3). In this example, Managers is just a sample organization unit. But this is a normal LDAP connection string, so you can insert and filter there whatever you want.

Is it possible to enable schema validation for inbound or outbound xml only in Apache CXF 2.5.2?

I have created a web service endpoint using Apache CXF 2.5.2, but I am having some issues with schema validation and MTOM interacting. If I enable MTOM and schema validation I must use the base64Binary type directly, however I am trying to conform to a fixed spec where the MTOM field also has a "contentType" attribute.
<jaxws:properties>
<entry key="mtom-enabled" value="true"/>
<entry key="schema-validation-enabled" value="true"/>
</jaxws:properties>
Is it possible to only enable schema validation for inbound or outbound messages? For example something like:
<entry key="schema-validation-enabled" value="inbound"/>
Alternatively is there an alternate way of achieving this, such as overriding the outbound message validation?
Thanks.
Since Apache CXF 3.0 this is sort of possible. You can't disable the validation on an in/outbound basis, but you can ignore the validation errors selectively (so you're still getting the performance hit).
You configure reader (inbound) & writer (outbound) validation event handlers in the CXF configuration.
<jaxws:properties>
<!-- Validation of the SOAP Message-->
<entry key="schema-validation-enabled" value="true" />
<entry key="jaxb-reader-validation-event-handler">
<bean class="com.example.cxf.InboundValidationEventHandler" />
</entry>
<entry key="jaxb-writer-validation-event-handler">
<bean class="com.example.cxf.OutboundValidationEventHandler" />
</entry>
</jaxws:properties>
Create the ValidationEventHandlers like this and return true. Returning true informs CXF to ignore a single validation error and continue validation.
package com.example.cxf;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
public class InboundValidationEventHandler implements ValidationEventHandler {
public boolean handleEvent(ValidationEvent event) {
String message = event.getMessage();
Throwable t = event.getLinkedException();
System.out.println("Ignoring Inbound Validation EVENT : " + message);
// ignore
return true;
}
}

Spring Security 3.1 ActiveDirectoryLdapAuthenticationProvider returning partial result exception

I am trying to authenticate users to an Active Directory Instance using spring security, I am getting an Partial Results Exception. I am going around in circles trying to figure this out. Below is my config.
security-app-context
<authentication-manager erase-credentials="true">
<authentication-provider>
<user-service>
<user name="admin#damien.com" authorities="ROLE_ADMINISTRATOR" password="123admin123" />
</user-service>
</authentication-provider>
<authentication-provider ref="ldapActiveDirectoryAuthProvider"/>
</authentication-manager>
<bean id="ldapActiveDirectoryAuthProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="myDomain.com" />
<constructor-arg value="ldap://ldapurl:389/" />
<property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>
Error I am getting
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 1, actual 0 org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:239)
I am struggling to find examples and the documentation indicates I am working in the right direction.
This is from the logs
SpringSecurityLdapTemplate.java 213 - Searching for entry under DN '', base = 'dc=myDomain,dc=com', filter = '(&(objectClass=user)(userPrincipalName={0}))'
and this is what I would expect that to look like on a successful attempt from some scripts that work
Searching for entry under DN 'OU=Users and Groups,DC=one,DC=two,DC=myDomain,DC=com', base = 'OU=Users and Groups,DC=one,DC=two,DC=myDomain,DC=com', filter = '(&(objectClass=user)(userPrincipalName={0}))'
Do I need to get the DN populated? How? I have looked through the ActiveDirectoryLdapAuthenticationProvider properties and don't see a way? Also the base is off but myDomain.com is the correct domain for users e.g john.doe#myDomain.com. Has anyone come across a similar problem?
To solve this I used the default LDAP provider which enables user search base to be specified, specifying the user search base and user search filter.
<ldap-authentication-provider
user-search-base="OU=Users and Groups,DC=abc,DC=myDomain,DC=com"
user-search-filter="userPrincipalName={0}" />
A user would then be logging in with john.doe#myDomain.com but the usersearch base is more specific(abc.myDomain.com). I believe AD Spring was falling down due to this.

Resources