Single sign-on with AD FS
When you finish, your AD FS farm issues SAML assertions that CCTVplanner accepts, and people at your verified domains sign in with their Active Directory credentials. The work is one DNS record, one relying party trust, two claim rules, one metadata URL pasted back into the CCTVplanner console, and one test you run from the console before the connection carries real sign-ins.
Before you start
- An organization on the Enterprise plan in CCTVplanner, and owner access to the organization console
- AD FS on Windows Server 2016 or later, published over TLS, directly or through a Web Application Proxy. The labels differ on Windows Server 2012 R2, where the menu item is Edit Claim Rules and the wizard shows Choose Issuance Authorization Rules instead of an access control policy page
- Membership in Administrators, or equivalent, on the federation server
- Your domain verified in the CCTVplanner console with the DNS TXT record
- Outbound HTTPS from the federation server to auth.cctvplanner.io, if you import our metadata by URL
- The mail attribute populated in Active Directory for everyone who will sign in, including the owner who runs the test
- A free seat for each person, if just-in-time provisioning is on
Values to paste
Copy these from your organization console rather than from this page. The console is the authority; the shapes below are only so you recognise the right field.
https://auth.cctvplanner.io/auth/v1/sso/saml/metadatahttps://auth.cctvplanner.io/auth/v1/sso/saml/acshttps://auth.cctvplanner.io/auth/v1/sso/saml/metadata?download=trueurn:oasis:names:tc:SAML:1.1:nameid-format:emailAddressSteps
- 1
Verify your domain first
Open cctvplanner.io/organization and add the domain your users sign in with.
The console generates a token and shows the DNS TXT record to publish.
Publish it, then re-check in the console. Nothing else in this guide works until the domain is verified. A connection is bound to your verified domains, and an assertion carrying an address outside them is refused with SSO_EMAIL_OUTSIDE_DOMAIN, at sign-in and again at every token refresh.
TXT record name_cctvplanner-challenge.<your-domain>TXT record valuecctvplanner-verify=<token the console generates>Leave the TXT record published after setup. The console lists your claimed and verified domains, so check there if a domain stops working.
- 2
Copy the service provider values from the console
The organization console shows every value AD FS needs, each with a copy button. The console is authoritative. Copy from it, do not type the values out of this page.
The shapes below are here so you recognise what you are looking at.
Entity ID / Identifierhttps://auth.cctvplanner.io/auth/v1/sso/saml/metadataAssertion Consumer Service (ACS) URLhttps://auth.cctvplanner.io/auth/v1/sso/saml/acsSP metadata for importhttps://auth.cctvplanner.io/auth/v1/sso/saml/metadata?download=trueNameID formaturn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - 3
Add the relying party trust from our metadata URL
In Server Manager, click Tools, then select AD FS Management. In the console tree, under AD FS, click Relying Party Trusts. Under Actions, click Add Relying Party Trust.
On the Welcome page, choose Claims aware and click Start.
On the Select Data Source page, choose Import data about the relying party published online or on a local network, and type the SP metadata URL into Federation metadata address (host name or URL). Click Next.
Importing beats the manual path. The document carries the entity ID, the assertion consumer endpoint, its binding and its index in one place, so an ACS URL typo cannot happen. A wrong ACS URL fails at the last hop of the sign-in, which is the most expensive place to debug.
Federation metadata address (host name or URL)https://auth.cctvplanner.io/auth/v1/sso/saml/metadata?download=trueThe federation server itself performs the fetch, not your workstation. If your servers reach the internet through a proxy, AD FS uses the WinHTTP proxy configuration of the service account, not your browser settings. Test with Invoke-WebRequest on the federation server before you blame the wizard.
- 4
Name the trust and choose an access control policy
On the Specify Display Name page, type a name such as CCTVplanner, and put the ticket or owner in Notes.
On the Choose Access Control Policy page, pick the policy you want. Permit everyone is the usual starting point, and you can narrow it to a group later.
On the Ready to Add Trust page, review the settings and click Next. On the Finish page, click Close. Microsoft's wording for what happens next: this action automatically displays the Edit Claim Rules dialog box.
Access control here is a second gate. If you restrict the trust to a group, everyone outside that group is stopped by AD FS and never reaches us, so no CCTVplanner error appears at all. Look in the AD FS admin event log, not in our console.
- 5
Check what the import actually created
Open the trust's Properties. On the Identifiers tab you should see our entity ID. On the Endpoints tab you should see a SAML assertion consumer endpoint pointing at our ACS URL with the POST binding.
If either is empty, the fetch returned something other than metadata, usually an error page from a proxy. Fix the fetch and re-import rather than typing the values by hand.
- 6
Set the secure hash algorithm to SHA-256
Still in the trust's Properties, open the Advanced tab and set the secure hash algorithm to SHA-256.
New trusts default to SHA-256. A trust imported from metadata that declares SHA-1, or carried through an upgrade from an older farm, can still be on SHA-1. Check the setting rather than assuming it.
Secure hash algorithmSHA-256In PowerShell the same setting is -SignatureAlgorithm on Set-AdfsRelyingPartyTrust, and its value is the XML signature identifier, not the string SHA-256. Microsoft Learn lists the accepted values with an https scheme, while the XML signature identifier itself is an http URI. Confirm what your farm stored with Get-AdfsRelyingPartyTrust -Name "CCTVplanner" before you script against the value.
- 7
Send the LDAP attributes as claims
In the console tree, under AD FS, click Relying Party Trusts. Right-click the trust and click Edit Claim Issuance Policy.
In the Edit Claim Issuance Policy dialog box, under Issuance Transform Rules, click Add Rule.
On the Select Rule Template page, under Claim rule template, select Send LDAP Attributes as Claims and click Next.
On the Configure Rule page, type a Claim rule name, select the Attribute store, and fill in Mapping of LDAP attributes to outgoing claim types from the table below. Click Finish, then click OK.
Attribute storeActive DirectoryLDAP Attribute: E-Mail-AddressesOutgoing Claim Type: E-Mail AddressLDAP Attribute: Given-NameOutgoing Claim Type: Given NameLDAP Attribute: SurnameOutgoing Claim Type: SurnameE-Mail-Addresses reads the mail attribute, not the user principal name. If your users have a UPN at the verified domain but an empty or stale mail attribute, sign-in fails later with SSO_ACS_FAILED or SSO_EMAIL_OUTSIDE_DOMAIN. Fix the directory, or change the rule to issue User-Principal-Name as the e-mail claim if the UPN suffix is the verified domain.
- 8
Transform the e-mail address into the Name ID
Click Add Rule again. On the Select Rule Template page, choose Transform an Incoming Claim and click Next.
Set Incoming claim type to E-Mail Address, Outgoing claim type to Name ID, and Outgoing name ID format to Email. Select Pass through all claim values. Click Finish, then click OK.
This rule consumes the claim the previous rule issued, so it must sit below it in the Issuance Transform Rules list. Reversed, it has nothing to transform and the assertion arrives without a Name ID.
We try the e-mail attribute names first, in the order we document, and fall back to the NameID only when the assertion carries no e-mail attribute at all. So a missing or wrong NameID on its own does not break a connection whose assertion already carries the e-mail claim. Set it anyway: it is what covers you if the LDAP rule ever stops issuing.
- 9
Or script the whole trust in PowerShell
Put the two rules in a text file, for example C:\adfs\cctvplanner-rules.txt, then create the trust in one call. Run this in an elevated PowerShell session on a federation server.
The rules file content is exactly what the wizard would have generated. First rule:
@RuleTemplate = "LdapClaims" @RuleName = "CCTVplanner attributes" c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"] => issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"), query = ";mail,givenName,sn;{0}", param = c.Value);
Second rule, in the same file, below the first:
@RuleTemplate = "MapClaims" @RuleName = "E-Mail Address to Name ID" c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
Then create the trust:
Add-AdfsRelyingPartyTrust -Name "CCTVplanner" -MetadataUrl "https://auth.cctvplanner.io/auth/v1/sso/saml/metadata?download=true" -MonitoringEnabled $true -AutoUpdateEnabled $true -AccessControlPolicyName "Permit everyone" -SignatureAlgorithm "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" -IssuanceTransformRulesFile "C:\adfs\cctvplanner-rules.txt" -Enabled $true
AutoUpdateEnabled set to true makes every field the metadata can supply read only in the console, and lets our metadata refresh overwrite them. That is what you want, and it will confuse you the first time a field is greyed out. It works in one direction only: AD FS re-reads our metadata, we do not re-read yours. Confirm the built-in policy name on your farm with Get-AdfsAccessControlPolicy before you reuse the line above.
- 10
Give CCTVplanner your federation metadata, then choose the policy
Back in the organization console, create the connection and give it your AD FS federation metadata. The console takes either a metadata URL or pasted metadata XML. Use the URL: re-saving the connection re-reads it, while a pasted document has to be replaced by hand every time your certificate changes. AD FS publishes the metadata at a fixed path.
Choose the sign-in mode, optional or required, and decide whether just-in-time provisioning is on and at which role. With it off, people must exist in CCTVplanner before they can sign in, added by hand or by SCIM.
A connection is a draft until you test it, and it cannot be switched on until the test passes. Run the test in the next section before you tell anyone to sign in.
IdP metadata URLhttps://<adfs-host>/FederationMetadata/2007-06/FederationMetadata.xmlRequired sign-in stops password sign-in for everyone at the verified domains. One exception exists: the owner may keep a password if the owner has a verified second factor, so a broken federation server cannot lock you out of your own tenant. Enrol that factor before you switch the mode to required.
What the assertion should carry
E-Mail Addresshttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, issued from the mail attribute. This is what we read first.Given Namehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname, issued from givenName. Optional.Surnamehttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname, issued from sn. Optional.Name IDhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier, format urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress. We fall back to it only when no e-mail attribute arrives.Group membership, optionalType http://schemas.microsoft.com/ws/2008/06/identity/claims/groups into Outgoing Claim Type yourself. The AD FS drop-down entry named Group uses a different URI that we do not read. We read the groups claim but do not use it for roles yet, so this changes no one's role.Prove it works
- Leave the connection where it is. It is not active yet, and ordinary sign-ins through it are refused with SSO_CONNECTION_TESTING until the test passes and you switch it on.
- In the organization console, open the connection and click the test button. The console opens your identity provider in a new browser tab.
- Sign in at AD FS as yourself, the owner, with a directory account at one of your verified domains and the mail attribute filled in.
- That sign-in creates a twin account: a second, separate CCTVplanner account at your own address, held by the identity provider. It occupies no seat, because an SSO identity is never linked to an existing password account.
- The test tab confirms the result, signs the twin account out again and hands the outcome back to the original console tab, which restores your own session.
- If the test fails, fix the claim rules or the metadata, then run it again. Only a passing test lets you switch the connection on.
- Switch the connection on, then sign in once through cctvplanner.io as somebody from your directory, and confirm the person lands in your organization and not as a personal account.
- If that person already had a password account at the same address, confirm the choice screen appears and that declining leaves both accounts intact.
When it does not work
| Symptom | Cause | Fix |
|---|---|---|
| SSO_ACS_FAILED right after the AD FS sign-in page | The assertion carried no e-mail at all: no e-mail claim, and no NameID to fall back to. Usually the mail attribute is empty in Active Directory. | Run Get-ADUser <user> -Properties mail and fill the attribute in, or reissue User-Principal-Name as the e-mail claim when the UPN suffix is the verified domain. Check that the LDAP rule sits above the transform rule in Issuance Transform Rules. |
| SSO_ACS_FAILED with a signature complaint, weeks after a working setup | AD FS rolled its token-signing certificate over. AutoCertificateRollover generates the new certificate ahead of time and promotes it on its own schedule. | Open the connection in the CCTVplanner console and save it again, which re-reads your metadata URL. If you pasted metadata XML instead, paste the new document. We do not refresh your metadata for you. Saving new metadata puts the connection back into testing, so run the test again and switch it on. |
| SSO_EMAIL_OUTSIDE_DOMAIN | The mail attribute holds an address at a domain you never verified with us, often an internal .local suffix or an old company name after a merger. The check runs at sign-in and again at every token refresh. | Verify that domain too, or correct the mail attribute. Both domains can be verified against the same organization. |
| SSO_PROVIDER_UNKNOWN | The e-mail domain in the assertion is not registered with any organization. | Confirm the domain appears as verified in the console and that its TXT record is still published. |
| SSO_CONNECTION_TESTING | The connection is in the testing state. A new connection sits there until the test passes, and editing an active connection's metadata puts it back there. | Run the test in the organization console, then switch the connection on. |
| SSO_PROVIDER_DISABLED | The connection was switched off. This is a different state from testing, with a different message. | Switch the connection back on in the organization console. |
| SSO_JIT_DISABLED | Just-in-time provisioning is off and the person was never added to the organization. | Add the person by hand or through SCIM, or turn just-in-time provisioning on and pick the role it should assign. |
| SSO_NO_SEATS | Just-in-time provisioning is on, but every seat, or every editor seat, is taken. | Free a seat or buy one. Deactivating someone through SCIM suspends the account but keeps the seat occupied, so deactivation alone will not clear this. |
| SSO_ACCOUNT_SUSPENDED | The account exists but is suspended, for example after a SCIM deactivation. | Reactivate the person in the organization console, or reactivate them in your directory and let SCIM push the change. |
| SSO_SESSION_EXPIRED | The session reached its maximum age. | Sign in again. Shorten or lengthen the cap for your organization in the console if the default does not suit you. |
| SSO_SESSION_IDLE | The session ended after a period without activity, which is a separate limit from the maximum age. | Sign in again. |
| SSO_REQUIRED on the password form | Sign-in mode is required for your verified domains, so the password form is closed to that address. | Sign in through AD FS instead. |
| SSO_OWNER_MFA_REQUIRED | Sign-in mode is required and the owner has no verified second factor, so the owner's password exception does not apply. | Enrol the owner's second factor before setting the mode to required. If the federation server is already broken and the owner has no factor, there is no self-service way back in. Write to us from the owner address. |
| SSO_OWNER_MFA_INCOMPLETE | The owner started enrolling a second factor and did not finish it, so there is no verified factor yet. | Finish the enrolment. This is a different state from SSO_OWNER_MFA_REQUIRED, where no factor was started at all. |
| SSO_SUBSCRIPTION_INACTIVE, nothing changed on the AD FS side | The organization's subscription is not active, so single sign-on stops even though the trust is healthy. | Settle the subscription. There is nothing to fix in AD FS. |
| AD FS shows MSIS7007, the requested relying party trust is unspecified or unsupported | The AuthnRequest issuer does not match any Identifier on the trust, usually because the metadata import failed and someone typed the entity ID with a trailing slash or the wrong host. | Compare the Identifiers tab with the Entity ID in the CCTVplanner console, character for character. Re-import the metadata instead of correcting it by hand. |
| Sign-out looks broken: a user signs out and is straight back in | The AD FS single sign-on cookie is still valid, so the next request re-authenticates without a prompt. Our session ended correctly. | Expected behaviour. Shorten the AD FS single sign-on lifetime if you need a real prompt, and shorten the CCTVplanner session cap for the organization if the browser session itself is the concern. |
Worth knowing
- Access tokens live fifteen minutes. Sessions end after twenty-four hours by default, and your organization can shorten that. Sessions also end after a period of inactivity, before the cap is reached.
- A daily job reads your signing certificate's expiry out of the metadata and e-mails the organization owner before it lapses. Acting on that e-mail is the same work as the certificate row in the table above: open the connection and save it again, or paste the new XML.
- An SSO identity is never linked to an existing password account at the same address, because the two are separate accounts as far as the identity provider is concerned. After the first SSO sign-in the person is shown exactly what would move, projects, offers, price lists, portal links and files, and chooses. Accepting blocks the old sign-in. Billing history and any personal subscription stay with the old account.
- Identity-provider-initiated sign-in is accepted and cannot be switched off, so the AD FS IdpInitiatedSignOn page works if your farm has it enabled. The property is EnableIdPInitiatedSignonPage on Set-AdfsProperties. The redirect target has to be on our allow list.
- The organization console holds the rest of what you need: the service provider values with copy buttons, your claimed and verified domains with the DNS TXT record, the connection with its display name, provider template, entity id and certificate expiry, the sign-in policy, the provisioning tokens, and an audit list you can export.
- AD FS does not speak SCIM. With AD FS alone, use just-in-time provisioning or add people by hand. We have no OpenID Connect, no IP allow list, no SIEM streaming, no group to role mapping, no uptime SLA, no SOC 2, no ISO 27001 and no third-party penetration test report.
- Microsoft's own pages for the AD FS half of this guide: learn.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-rule-to-send-ldap-attributes-as-claims, .../create-a-rule-to-transform-an-incoming-claim, learn.microsoft.com/en-us/powershell/module/adfs/add-adfsrelyingpartytrust, learn.microsoft.com/en-us/powershell/module/adfs/set-adfsproperties, and learn.microsoft.com/en-us/windows-server/identity/ad-fs/troubleshooting/ad-fs-tshoot-endpoints for the federation metadata path. Microsoft's create-a-relying-party-trust page is archived under previous-versions and documents the older wizard, so we do not cite it for the click path above.
Stuck on something this page does not cover? Write to [email protected] and quote the error text on screen. Enterprise is the plan that carries single sign-on.
Screens and field names belong to the identity provider and change without telling us. Where this page and their own documentation disagree, theirs is right, and we would like to hear about it.