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
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.