Shanky Munjal

SAML based SSO with Azure AD B2C as an IDP

Among the many perks of working in an agile environment, one is to constantly evolve with challenging tasks. While working on my project, there was one such requirement where we needed to use another application without signing again.

SAML based SSO with Azure

I couldn’t find its implementation online except for these two documents which were very helpful-

  1. https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-get-started-custom
  2. https://github.com/Azure-Samples/active-directory-b2c-advanced-policies/blob/master/Walkthroughs/RP-SAML.md

So my most of the code would be from above documents except some tweaks which I had to do because I not getting results as per my requirements.

We were using AD B2C to signup and sign-in to the application. We had already inbuilt policies for sign in and sign up but for SAML we were required to create new Custom Policy.

Let’s follow these steps to create Custom Policy to support SAML SSO.

Step 1 – Create Policy Keys and IdentityExperienceFramework Application

Follow first 3 points of this URL.

We do not need to change anything and these steps are clear and explanatory.

Step 2 – Create Certificate

(Reference)

We need to create certificates to sign the SAML response.

  1. Create the cert using makecert
    • makecert -r -pe -n “CN=yourappname.yourtenant.onmicrosoft.com” -a sha256 -sky signature -len 2048 -e 12/21/2018 -sr CurrentUser -ss My YourAppNameSamlCert.cer
    • Go to cert store “Manage User Certificates” > Current User > Personal > Certificates > yourappname.yourtenant.onmicrosoft.com
    • Right-click > All Tasks > Export
    • Yes, export the private key
    • Defaults (PFX and first checkbox)
  2. Go to your Azure AD B2C tenant. Click Settings > Identity Experience Framework > Policy Keys.
  3. Click +Add, and then click Options > Upload.
  4. Enter a Name (for example, YourAppNameSamlCert). The prefix B2C_1A_ is automatically added to the name of your key.
  5. Upload your certificate using the upload file control.
  6. Enter the certificate’s password.
  7. Click Create.
  8. Verify that you’ve created a key (for example, B2C_1A_YourAppNameSamlCert).

Step 3 – Download Sample policies

Go to the URL and download following 3 files. We will modify these policies based on our requirement in next steps.

  1. Base file- Iamnahealth.onmicrosoft.com_B2C_1A_base.xml
  2. Base Extension file- Iamnahealth.onmicrosoft.com_B2C_1A_base_Extensions.xml
  3. Signin file- Iamnahealth.onmicrosoft.com_signin.xml

Step 4 – Update Base file

  1. Open base file.
  2. Replace all instances of “Iamnahealth” with your tenantid
  3. There would be with ID = “JwtIssuer”. Add following just after the JwtIssuer TechnicalProfile

<TechnicalProfile Id="Saml2AssertionIssuer">
   <DisplayName>Token Issuer</DisplayName>
   <Protocol Name="None" />
   <OutputTokenFormat>SAML2</OutputTokenFormat>
   <Metadata>
      <Item Key="IssuerUri">https://login.microsoftonline.com/te/TenantID.onmicrosoft.com/B2C_1A_signin</Item>
   </Metadata>
   <CryptographicKeys>
      <Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_YourAppNameSamlCert" />
      <Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_YourAppNameSamlCert" />
   </CryptographicKeys>
   <InputClaims />
   <OutputClaims />
</TechnicalProfile>

Above code is taken from GitHub. But the IssuerUri is different. The IssuerUri that is in the document was not working. I was getting an error at SP side. So I changed it to above URL.

4. There would be UserJourney with ID = “signIn”. Add following just after the signIn UserJourney

<UserJourney Id="SignInSaml">
   <OrchestrationSteps>
      <OrchestrationStep Order="1" Type="ClaimsExchange">
         <ClaimsExchanges>
            <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
         </ClaimsExchanges>
      </OrchestrationStep>
      <OrchestrationStep Order="2" Type="ClaimsExchange">
         <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
               <Value>authenticationSource</Value>
               <Value>localAccountAuthentication</Value>
               <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
         </Preconditions>
         <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadWithAltSecId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId" />
         </ClaimsExchanges>
      </OrchestrationStep>
      <OrchestrationStep Order="3" Type="ClaimsExchange">
         <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
               <Value>authenticationSource</Value>
               <Value>socialIdpAuthentication</Value>
               <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
         </Preconditions>
         <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
         </ClaimsExchanges>
      </OrchestrationStep>
      <OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Saml2AssertionIssuer" />
   </OrchestrationSteps>
   <ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>

 

Step 5 – Update Base_Extension file

Replace base extension file with the following content-

<TrustFrameworkPolicy xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" PolicySchemaVersion="0.3.0.0" TenantId="TenantId .onmicrosoft.com" PolicyId="B2C_1A_base_extensions" PublicPolicyUri="http://TenantId.onmicrosoft.com">
   <BasePolicy>
      <TenantId>TenantId .onmicrosoft.com</TenantId>
      <PolicyId>B2C_1A_base</PolicyId>
   </BasePolicy>
   <BuildingBlocks />
   <ClaimsProviders>
      <ClaimsProvider>
         <DisplayName>Local Account SignIn</DisplayName>
         <TechnicalProfiles>
            <TechnicalProfile Id="login-NonInteractive">
               <Metadata>
                  <Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
                  <Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
               </Metadata>
               <InputClaims>
                  <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFrameworkAppId" />
                  <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFrameworkAppId" />
               </InputClaims>
            </TechnicalProfile>
         </TechnicalProfiles>
      </ClaimsProvider>
   </ClaimsProviders>
</TrustFrameworkPolicy>

Replace all instances of

  • TenantId with your TenantId
  • ProxyIdentityExperienceFrameworkAppId with application id that you created by following Step 1
  • IdentityExperienceFrameworkAppId with application id that you created by following Step 1

Step 6 – Create Policy file

Create a new file with name “signinsaml.xml”.
Paste the following code.

<TrustFrameworkPolicy xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" PolicySchemaVersion="0.3.0.0" TenantId="TenantId.onmicrosoft.com" PolicyId="signin" PublicPolicyUri="http://TenantId.onmicrosoft.com">
   <BasePolicy>
      <TenantId>TenantId.onmicrosoft.com</TenantId>
      <PolicyId>B2C_1A_base_extensions</PolicyId>
   </BasePolicy>
   <RelyingParty>
      <DefaultUserJourney ReferenceId="SignInSaml" />
      <TechnicalProfile Id="PolicyProfile">
         <DisplayName>PolicyProfile</DisplayName>
         <Protocol Name="SAML2" />
         <SubjectAuthenticationRequirements TimeToLive="40000" ResetExpiryWhenTokenIssued="false" />
         <Metadata>
            <Item Key="PartnerEntity"><![CDATA[<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2026-12-27T23:42:22.079Z" entityID="entityID" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <md:SPSSODescriptor WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://acs_url.com/sso/sp/ACS.saml2″ index="0″ isDefault="true"/> </md:SPSSODescriptor> </md:EntityDescriptor>]]></Item>
            <Item Key="Saml2AttributeEncodingInfo"><![CDATA[<saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Attribute FriendlyName="UserPrincipalName" Name="UserId" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string"></saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement>]]></Item>
            <Item Key="Saml11AttributeEncodingInfo"><![CDATA[<saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"><saml:Attribute AttributeName="ImmutableID" AttributeNamespace="http://schemas.microsoft.com/LiveID/Federation/2008/05″><saml:AttributeValue></saml:AttributeValue></saml:Attribute><saml:Attribute AttributeName="UPN" AttributeNamespace="http://schemas.xmlsoap.org/claims"><saml:AttributeValue></saml:AttributeValue></saml:Attribute></saml:AttributeStatement>]]></Item>
            <Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
            <Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
         </Metadata>
         <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="givenName" />
            <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
            <OutputClaim ClaimTypeReferenceId="surname" />
            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
         </OutputClaims>
         <SubjectNamingInfo ClaimType="sub" />
      </TechnicalProfile>
   </RelyingParty>
</TrustFrameworkPolicy>

Replace all instances of

  • TenantId with your TenantId
  • ProxyIdentityExperienceFrameworkAppId with application id that you created by following Step 1
  • IdentityExperienceFrameworkAppId with application id that you created by following Step 1
  • https://acs_url.com/sso/sp/ACS.saml2 with the URL of the SP where you want to POST your SAML Response
  • entityId with the entityID of the SP

Step 7 – Upload Policy in Azure Portal

  • Go to https://portal.azure.com
  • Click “More Services” (at bottom left corner) and type “Azure AD B2C” and select it.
  • Click on “Identity Experience Framework – PREVIEW” and then “Upload Policy”
  • Upload Base file
  • Upload Base extension file
  • Upload signinsaml file

Note– Upload should be in the same order.

Step 8 – Run Policy

  1. Configure SAML Metadata – from below URL, you will get SAML Metadata. You will need to use SAML Metadata at SP side. https://login.microsoftonline.com/te/tenantId.onmicrosoft.com/B2C_1A_signin/Samlp/metadata
  2. Login URL –https://login.microsoftonline.com/te/tenantId.onmicrosoft.com/B2C_1A_signin/Samlp/sso/login
    Above URL will be used to initiate the SAML flow. You have to hit above URL with two query string parameters

    1. SAML_Request
    2. RelayState

Example

https://login.microsoftonline.com/te/tenantId.onmicrosoft.com/B2C_1A_signin/Samlp/sso/login?SAML_Request=base64_encoded_authnrequest_saml&RelayState=https://relay_state_url.com

Sample AuthnRequest

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" ID="F84D888AA3B44C1B844375A4E8210D9E" Version="2.0" IsPassive="false" AssertionConsumerServiceURL="https://acs_url.com/sso/sp/ACS.saml2">
   <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion" />
   <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" />
</samlp:AuthnRequest>

The above request will send the SAML Response to the ACS URL.

This way you can implement SSO. But still if you face any problems, please feel free to comment here or you can send me an email at shankymunjal89@gmail.com.