Azure Active Directory

Azure Active Directory is a Microsoft Azure service which provides identity and access management. Cloud CMS supports single sign on with Azure AD using SAML 2.0.

Set up Azure

Before configuring the Cloud CMS Single Sign-On, you’ll need to set up a few things on Microsoft Azure Active Directory:

Create an Azure Account

If not already present, create an Azure Account using the Azure portal. Log into the portal and go to Azure Active Directory.

Register your App

Go to the App Registrations tab in your directory to create a new application.

Click New Registration button at the top panel.

Enter the Name for the application and Sign on URL.

Enter the Redirect URI for your tenant.
It will look like: https://{subdomain}.cloudcms.net/saml

Click on Register.

Configure your App

Once the Application is created, you will need to copy down some values. These are:

  • Application ID
  • SAML-P sign-on endpoint

The Application ID is shown on the overview page:

Here, the Application ID is 3efd9d97-26e4-4837-bd75-91a34c2da4ee but you will have a different value.

To get the SAML-P sign-on endpoint, click on Endpoints.

Copy the URL for the SAML-P sign-on endpoint field. This is might look like:

https://login.microsoftonline.com/3770f585-1d9b-44d4-9016-2428d0082154/saml2

Configure Cloud CMS

Next, let's configure Cloud CMS.

  • Log in to Cloud CMS as your manager account. You can do this by going to https://{subdomain}.cloudcms.net/login.

Then:

  • Go to Platform Settings > SSO.
  • Select SAML 2.0 from the list as below:
  • In the SAML SSO URL field, paste the URL copied above (the value for the SAML-P sign-on endpoint field).
  • In the SAML Issuer field, enter the Application ID of the application created.
  • In the User Primary Identifier Field field, enter nameID.

In the User Field Mappings section, add the following mappings:

  • User Property = name and Field Value = http://schemas.microsoft.com/identity/claims/displayname
  • User Property = email and Field Value = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • User Property = firstName and Field Value = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
  • User Property = lastName and Field Value = http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname

Save your changes.

Try it out

You can now verify that Cloud CMS is configured to use SAML 2.0 with Azure:

  • Log out of your current Cloud CMS account
  • Log back in
  • While Logging in again, you will be re-directed to Microsoft Azure portal.
  • Enter the username and password of your account.
  • If the credentials match, you will be redirected back to Cloud CMS.
  • Cloud CMS will automatically log you in and create your user if it doesn't yet exist.
  • Proceed and may the force be with you.

Technical Notes

We've collected some technical notes here to help with integrating to Azure AD.

Redirect URI

When Cloud CMS redirects to Azure AD, it will pass a "redirect URI" to Azure. This must match the value that you have configured for your Redirect URI within Azure. Typically, this will be:

https://{subdomain}.cloudcms.net/saml

SAML Assertion

Once you've authenticated to Azure, Azure will perform a POST back to this URL. The POST will contain a request parameter called SAMLResponse. This value is a Base64 encoded XML string that contains a SAML assertion. It contains validation of the authenticated user and also contains multiple claims about the user. These consist of user properties and/or group information.

If you're using a browser, you can open up the Dev Tools of the browser to inspect the assertion POST. Make sure to set logs to persist so that the browser doesn't clear the logging between domain redirects.

The JWT token might look something like:

SAMLResponse: PHNhbWxwOlJlc3BvbnNlIElEPSJfOGZjMzNmMTMtYzI0YS00NzA0LWJlODEtNWRlMmViNDhmZGVhIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyMC0wNi0wMlQxNDo1OTozOC45OTdaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2xvY2FsaG9zdC9zYW1sL2NvbnN1bWUiIEluUmVzcG9uc2VUbz0iX2UyMWViODJhNzVmNDM0ODdmZGYyIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwczovL3N0cy53aW5kb3dzLm5ldC8zNzcwZjU4NS0xZDliLTQ0ZDQtOTAxNi0yNDI4ZDAwODIxNTQvPC9Jc3N1ZXI+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PEFzc2VydGlvbiBJRD0iXzFjMTA0YTI5LTBiOWUtNDA3ZC05ZjRlLWJhNjExYTcwYzEwMCIgSXNzdWVJbnN0YW50PSIyMDIwLTA2LTAyVDE0OjU5OjM4Ljk4MloiIFZlcnNpb249IjIuMCIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxJc3N1ZXI+aHR0cHM6Ly9zdHMud2luZG93cy5uZXQvMzc3MGY1ODUtMWQ5Yi00NGQ0LTkwMTYtMjQyOGQwMDgyMTU0LzwvSXNzdWVyPjxTaWduYXR1cmUgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxTaWduZWRJbmZvPjxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48UmVmZXJlbmNlIFVSST0iI18xYzEwNGEyOS0wYjllLTQwN2QtOWY0ZS1iYTYxMWE3MGMxMDAiPjxUcmFuc2Zvcm1zPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L1RyYW5zZm9ybXM+PERpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxEaWdlc3RWYWx1ZT5uM1VLNlhZcUV5dFpwdjA3R1dxdEt4MkJSekYvRDErcndmMnlrYW5FQzl3PTwvRGlnZXN0VmFsdWU+PC9SZWZlcmVuY2U+PC9TaWduZWRJbmZvPjxTaWduYXR1cmVWYWx1ZT5INGxvdjBMb3FMVk1YS0VPb3ZVSURMSlJTL09LOGx3QlpFclJ5UmJxZXlKUGNjalZucHZ1NVcxcUUvRjhNL2pVam5PZjl5azlrckE1N1d1dkFnUUcxcE5XL3Jsb2J4SWtqd08wRlRvcUJtQm9Ldk1LeWRYT2V6TWRVd3dEcEt0QVNTSDZnNDNDNlBMSmI5TG5yYUVhdEpXNWhIdjZXZ2U4OXMyRjZsWmlpamdTM1NKYlpkRi83OHorUVU1ODhTMk40WVEwbzNEMWdNMFA0S3NJTHZsOVgxWGdIZ3hvaGowMk4zd0k2bjlzSGM4Z1dTQ295WmR4Z3BUM2hqcUNmN2dFcGh1VjdlbFNMMjEyWi9DRU4xa3R5a1NWSG5OMzhteTh2ZTVvbHFRc2pHb1pTSXFuSG40Z3dZOEEyaTRJVFU2WU1PRW93MWdPZFV0RGFtK0tKUm1EZmc9PTwvU2lnbmF0dXJlVmFsdWU+PEtleUluZm8+PFg1MDlEYXRhPjxYNTA5Q2VydGlmaWNhdGU+TUlJREJUQ0NBZTJnQXdJQkFnSVFYVm9najlCQWY0OUlwdU9TSXZ6dE5EQU5CZ2txaGtpRzl3MEJBUXNGQURBdE1Tc3dLUVlEVlFRREV5SmhZMk52ZFc1MGN5NWhZMk5sYzNOamIyNTBjbTlzTG5kcGJtUnZkM011Ym1WME1CNFhEVEl3TURNeE56QXdNREF3TUZvWERUSTFNRE14TnpBd01EQXdNRm93TFRFck1Da0dBMVVFQXhNaVlXTmpiM1Z1ZEhNdVlXTmpaWE56WTI5dWRISnZiQzUzYVc1a2IzZHpMbTVsZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTmZMbWR6OXlJRHNrcFp6ck1YaURlVmxDczc1WnVucnp3ekJXNWx6N1V4ZEJqSHU3UTlpVDMyb3RsQnArK0xPd0JjS3NWanVRMEdVYnVsWDBGTHNmTGpFZUNlNThadFNuLy8rNlZSRlNTY2c3aStXdkR3RVVXRUxSK3ZNUHRDR2NYQlRwSUxFblliU016ME5vNCtKcGtjMWx5TUlmRFAvS1NlcW9qbzc0eGZXNFJLdEFndjM5dXdaNVl6MmhaL0ljV092YVFxTVhwMWxxaFhMRklSV2J3akxZWVVibXdHd1lwUTYrK0NtbDB1Y1FvTWtnWVQ4OEhwQS9melhRbExnckhhbXIzZUUvbFZwMjZaV3dmR0xBdmtkTkJhYlFSU3JrOGsvYzZCbVkxbVlwVUZabys3OTVQSTE2bUFkcDFpb0V3SDhJNW9zaXMrL0JSNUdoUHB3aUE4Q0F3RUFBYU1oTUI4d0hRWURWUjBPQkJZRUZGOE1ER2tsT0doR05WSnZzSEhSQ2FxdHpleGNNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUNLa2Vndy9tZHBDVmwxbE9wZ1U0RzlSVCsxZ3RjUHFaSzlrcGltdURnZ1NKanU2S1VRbE9DaTUvbElINURDenBqRmRtRzE3VGpXVkJOdmU1a293bXJoTHpvdlkwWWtrNys2aFlUQks4ZE5OU21kNFNLN3pZKyswYURJdU96SFAyQ3VyK2tnRkMwZ2V6NTB0UHpvdExEdE1tcDQwZ2tuWHV6bHR3SmZlek5TdzNnTGdsakRzR0djRElYSzNxTFNZaDQ0cVN1Ukd3dWxjTjJFSlVaQkk5dEl4b09EcGFXSElOOCt6MnVadmY4SkJZRmpBMytuOUZSUW41MVgxNkNUY2pxNFFSVGJOVnBnVnVRdXlhWW5FdHgwWm5Edmd1QjNSakdTUElYVFJCa0xsMng3ZTgvNnVBWjZ0Y2h3OHJoY090UHNGZ0p1b0pva0dqdmNVU1IvNkVxZDwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvU2lnbmF0dXJlPjxTdWJqZWN0PjxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPm1pY2hhZWwudXpxdWlhbm9AY2xvdWRjbXMuY29tPC9OYW1lSUQ+PFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfZTIxZWI4MmE3NWY0MzQ4N2ZkZjIiIE5vdE9uT3JBZnRlcj0iMjAyMC0wNi0wMlQxNTo1OTozOC44MDlaIiBSZWNpcGllbnQ9Imh0dHA6Ly9sb2NhbGhvc3Qvc2FtbC9jb25zdW1lIi8+PC9TdWJqZWN0Q29uZmlybWF0aW9uPjwvU3ViamVjdD48Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDYtMDJUMTQ6NTQ6MzguODA5WiIgTm90T25PckFmdGVyPSIyMDIwLTA2LTAyVDE1OjU5OjM4LjgwOVoiPjxBdWRpZW5jZVJlc3RyaWN0aW9uPjxBdWRpZW5jZT5zcG46M2VmZDlkOTctMjZlNC00ODM3LWJkNzUtOTFhMzRjMmRhNGVlPC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24+PC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2lkZW50aXR5L2NsYWltcy90ZW5hbnRpZCI+PEF0dHJpYnV0ZVZhbHVlPjM3NzBmNTg1LTFkOWItNDRkNC05MDE2LTI0MjhkMDA4MjE1NDwvQXR0cmlidXRlVmFsdWU+PC9BdHRyaWJ1dGU+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2lkZW50aXR5L2NsYWltcy9vYmplY3RpZGVudGlmaWVyIj48QXR0cmlidXRlVmFsdWU+N2E3NmIzNTAtMDAyMS00MDBhLTg3MmItNGRmNzU0MzNmNTEyPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3N1cm5hbWUiPjxBdHRyaWJ1dGVWYWx1ZT5mZDI1MWU5Zi1lZGUyLTQ4NjAtYmNiZi00Y2FiYzQxYTc0YjE8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvZ2l2ZW5uYW1lIj48QXR0cmlidXRlVmFsdWU+NjdlOGY0NjctNmQ2ZS00MWIyLTllZGYtODdhMjU4YWFmMjJiPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vaWRlbnRpdHkvY2xhaW1zL2Rpc3BsYXluYW1lIj48QXR0cmlidXRlVmFsdWU+NjdlOGY0NjctNmQ2ZS00MWIyLTllZGYtODdhMjU4YWFmMjJiIGZkMjUxZTlmLWVkZTItNDg2MC1iY2JmLTRjYWJjNDFhNzRiMTwvQXR0cmlidXRlVmFsdWU+PC9BdHRyaWJ1dGU+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiPjxBdHRyaWJ1dGVWYWx1ZT5taWNoYWVsLnV6cXVpYW5vQGNsb3VkY21zLmNvbTwvQXR0cmlidXRlVmFsdWU+PC9BdHRyaWJ1dGU+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL2lkZW50aXR5L2NsYWltcy9pZGVudGl0eXByb3ZpZGVyIj48QXR0cmlidXRlVmFsdWU+bGl2ZS5jb208L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjxBdHRyaWJ1dGUgTmFtZT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9jbGFpbXMvYXV0aG5tZXRob2RzcmVmZXJlbmNlcyI+PEF0dHJpYnV0ZVZhbHVlPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9BdHRyaWJ1dGVWYWx1ZT48QXR0cmlidXRlVmFsdWU+aHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2F1dGhlbnRpY2F0aW9ubWV0aG9kL3Vuc3BlY2lmaWVkPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48L0F0dHJpYnV0ZVN0YXRlbWVudD48QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDIwLTA2LTAyVDE0OjUwOjA2LjYzNFoiIFNlc3Npb25JbmRleD0iXzFjMTA0YTI5LTBiOWUtNDA3ZC05ZjRlLWJhNjExYTcwYzEwMCI+PEF1dGhuQ29udGV4dD48QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L0F1dGhuQ29udGV4dENsYXNzUmVmPjwvQXV0aG5Db250ZXh0PjwvQXV0aG5TdGF0ZW1lbnQ+PC9Bc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4=

Not very attractive. That's because it is Base64 encoded. You can use an online tool (such as https://www.base64decode.org/) to decode the Base64 string and you'll get something like this:

<samlp:Response ID="_8fc33f13-c24a-4704-be81-5de2eb48fdea" Version="2.0" IssueInstant="2020-06-02T14:59:38.997Z" Destination="http://mysubdomain.cloudcms.net/saml/consume" InResponseTo="_e21eb82a75f43487fdf2" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
    <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://sts.windows.net/3770f585-1d9b-44d4-9016-2428d0082154/</Issuer>
    <samlp:Status>
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </samlp:Status>
    <Assertion ID="_1c104a29-0b9e-407d-9f4e-ba611a70c100" IssueInstant="2020-06-02T14:59:38.982Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
        <Issuer>...</Issuer>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            ...
        </Signature>
        <Subject>
            <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">joe.smith@company.com</NameID>
            <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <SubjectConfirmationData InResponseTo="_e21eb82a75f43487fdf2" NotOnOrAfter="2020-06-02T15:59:38.809Z" Recipient="https://mysubdomain.cloudcms.net/saml/consume" />
            </SubjectConfirmation>
        </Subject>
        <Conditions NotBefore="2020-06-02T14:54:38.809Z" NotOnOrAfter="2020-06-02T15:59:38.809Z">
            <AudienceRestriction>
                <Audience>...</Audience>
            </AudienceRestriction>
        </Conditions>
        <AttributeStatement>
            <Attribute Name="http://schemas.microsoft.com/identity/claims/tenantid">
                <AttributeValue>0f583775-1d9b-44d4-9016-008212428d54</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.microsoft.com/identity/claims/objectidentifier">
                <AttributeValue>76b37a50-0021-400a-872b-75433f4df512</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname">
                <AttributeValue>Smith</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
                <AttributeValue>Joe</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.microsoft.com/identity/claims/displayname">
                <AttributeValue>Joe Smith</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
                <AttributeValue>joe.smith@company.com</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.microsoft.com/identity/claims/identityprovider">
                <AttributeValue>live.com</AttributeValue>
            </Attribute>
            <Attribute Name="http://schemas.microsoft.com/claims/authnmethodsreferences">
                <AttributeValue>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AttributeValue>
                <AttributeValue>http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/unspecified</AttributeValue>
            </Attribute>
        </AttributeStatement>
        <AuthnStatement AuthnInstant="2020-06-02T14:50:06.634Z" SessionIndex="_14a2c109-b09e-740d-9f4e-611a7ba0c100">
            <AuthnContext>
                <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
            </AuthnContext>
        </AuthnStatement>
    </Assertion>
</samlp:Response>

The attributes that you can use for user and group mappings are listed in the <AttributeStatement> section.

Note that Azure sends all of its attribute names in a rather lengthy domain-qualified manner. For example, instead of sending displayname, they send http://schemas.microsoft.com/identity/claims/displayname.

SAML 2.0 supports namespaces for attribute names but Microsoft has elected to go with the long format.

You can add new attributes within Azure AD by adding new claims. Click on Token Configuration to be taken to a screen where you can add new claims.

After you add claims, they will appear under the <AttributeStatement> section. Be sure to check your SAMLResponse after making changes to see the attribute names that come back.

In addition, note that the values you get back will match the entry for the principals as stored in Active Directory. Azure's SAML 2.0 support always sends the attributes listed above and, if it doesn't know a value, you may see a GUID. You'll need to work with Active Directory to make sure that your users have values assigned for:

  • surname
  • givenname
  • displayname

As well as another attributes that you map via SAML assertions.