Active Directory Federation Services

PointClickCare – Single Sign On (SSO) with Active Directory Federation Services (ADFS)

Below is a step by step walk through on how to configure PointClickCare SSO with ADFS. PointClickCare will provide you with metadata XML files in which you will use to build the relying party trust. These files are unable to be imported into ADFS and we must pull the information from them manually. You will create one relying party and claim rules for each Single Sign On Endpoint (Web App, POC, eMar, Mobile App)

Gather Relying Party details from XML file

EntityID – Within the md:EntityDescriptor element will be entityID. Record the value within the quotes (Ex:″

Assertion Consumer URL – Within the md:AssertionConsumerService element will be Location. Record the value within the quotes (Ex:

Signature Certificate – Find <ds:X509Data><ds:X509Certificate>. Copy the value between those two XML elements and</ds:X509Data></ds:X509Certificate>. Paste the value into notepad and save it as a .cer file. Make a note of this expiration date as the certificate will need to be updated on the relying party configuration when PointClickCare renews it.

Create the Relying Party Trust

Open Active Directory Federation Services, go to Relying Party Trusts and click Add Relying Party Trust

Choose Claims aware

Choose Enter data about the relying party manually

Enter a name for the relying party trust. Here are some recommended names depending on the relying party trust.
PointClickCare – Web App
PointClickCare – POC App
PointClickCare – eMar App
PointClickCare – Mobile App

Don’t add a token encryption certificate, just click Next.

Check the box Enable support for SAML 2.0 WebSSO protocol and enter the Assertion Consumer URL you recorded from the XML file.

Add the EntityID that you recorded from the XML file and add it to the list of Relying party trust identifiers

If you plan to use an access policy, select the appropriate one or choose Permit everyone

Click Next on the review page

Uncheck Configure claims issuance policy for this application and click Close. We will do this later

Right click on the new Relying Party Trust and click Properties

Go to the Signature tab and click Add. Browser to the .cer certificate file you created in the beginning and then click Apply

Go to the Endpoints tab and click on the SAML Assertion Consumer Endpoint and click Edit. Check the box Set the trusted URL as default and click OK.

As of this writing, PointClickCare doesn’t offer Single Log Out with ADFS so we will configure it within ADFS. Click Add SAML on the Endpoints tab.

Change Endpoint type to SAML Logout and Binding to Redirect.
For the Trusted URL enter your ADFS signout URL (Ex:
Leave Response URL blank and click OK

Click OK on the Relying Party properties page.

Create the claims

Right click the Relying Party Trust again and choose Edit Claim Issuance Policy

We are going to add a total of 6 claims. Pay careful attention to the following directions

Click Add Rule and choose Send Claims using a Custom Rule

Name the claim rule SSO Key. Enter the custom rule

=> issue(Type = "ssoKey", Value = "YOURSSOKEY_FROM_PCC");

Click Finish/OK and click Add Rule and choose Send Claims using a Custom Rule

Name the claim rule Org Code. Enter the custom rule

=> issue(Type = "orgCode", Value = "YOUR_PCC_ORG_CODE");

Click Finish/OK and click Add Rule and choose Send Claims using a Custom Rule

Name the claim rule Module and enter the custom rule

=> issue(Type = "destmodule", Value = "MODULE");

PointClickCare Web App = PCC_WEB
PointClickCare Point Of Care (POC) = POC
PointClickCare eMar = EMAR

(Note: This claim is not used if you’re setting up the Mobile relying party)

Click Finish/OK and click Add Rule and choose claim rule Send LDAP Attributes as Claims.

Name the claim rule Username and select the attribute store Active Directory

IMPORTANT: The LDAP field that you’re about to select must be a one to one match to the PointClickCare Login Name field for each user. Most probably use the sAMAccountName, which means they log into a computer on your domain with the same username as they log into PointClickCare. If this is a new PointClickCare environment or you’re leveraging UAP you could also use a custom attribute in LDAP. Please note, this LDAP attribute has nothing to do with what the end user logins into the computer with (although it could be one in the same), it is only used to tell PointClickCare who the user is.

Select the LDAP Attribute that contains the unique identifier between Active Directory and PointClickCare. On the Outgoing Claim Type enter username all lowercase with no spaces.

Click Finish/OK and click Add Rule and choose claim rule Send Claims using a Custom Rule

(Note: This claim is not used if you’re setting up the Mobile relying party)

Name the rule Get Username for Subject and enter the following claim below. If you are not using sAMAccountName, change the two references below to the LDAP attribute you’re using.

c:[Type == "", Issuer == "AD AUTHORITY"]
=> add(store = "Active Directory", types = ("temp:sAMAccountName), query = ";sAMAccountName;{0}", param = c.Value);

Click Finish/OK and click Add Rule and choose claim rule Send Claims using a Custom Rule

Name the rule Create Subject with Username and enter the following claim below. Again, if you’re using a different LDAP attribute, change the name of the temp value to match. Relpace YOUR_ORG_CODE with your PointClickCare organization code. Be sure to keep the period at the end of the org code.

c:[Type == "temp:sAMAccountName"]
=> issue(Type = "", Value = "YOUR_ORG_CODE." + c.Value);

Click Finish/OK

The next claim is only used for the Mobile relying party.

Click Add Rule and choose claim rule Send Claims using a Custom Rule. Name the claim rule Org ID and enter the following rule below. Replace the value with the Org ID provided by PointClickCare for Mobile

=> issue(Type = "orgId", Value = "XXXXXXXX");

Click Finish/OK. You’ve finished the setup for the relying parties.

Validating ADFS Configuration

PointClickCare’s SSO works with an IDP Iniated setup which require changing/verifying two settings on the ADFS servers. On your ADFS server, open Powershell and run the following command. If either of them say false, you’ll need to enable them. You will need to restart the ADFS service for these to take effect.

To Verify
Get-AdfsProperties | Select EnableIdpInitiatedSignonPage, RelayStateForIdpInitiatedSignOnEnabled
To Set
Set-AdfsProperties -EnableRelayStateForIdpInitiatedSignOn $true
Set-AdfsProperties -EnableIdPInitiatedSignonPage $true

Creating the shortcut to use SSO

We will build the URL’s necessary for your users to access PointClickCare using SSO. In the previous step we enabled relay state, which allows the ADFS server to identify the relying party within the URL and provide a streamlined experience for your users.

Obtain your Idp initiated signon URL. It should be You can browse to this page and see your relying parties and test the SSO with PointClickCare.

Obtain the relying party identifier for the shortcut you’d like to make. For example, if creating a shortcut for Point Click Care Web App the identifier should be

Finally the last url you need is the target and that is

Head over to and enter the information you just gathered and general the URL. You can now visit that URL and once authenticated it will log you directly into PointClickCare.

PointClickCare – Single Sign On (SSO) with Active Directory Federation Services (ADFS) Read More »

Active Directory Federation Services (ADFS) Authentication Adapter

Microsoft has provided source code to allow a Username and Password to be used as a second factor of authentication in ADFS. You may ask, why would you want to have the usernam and password as a second factor?

  1. Some automated hacking attempts will can lock out your accounts and/or identify a username and password and obtain access through ADFS or another non-authenticated ADFS system. You can setup a different primary authentication like DUO and then username/password seconds.
  2. You may want to allow Windows Integrated Authentication (WIA) for internal connections but require certain relying parties to still require a password for access. Think payroll/financial applications.

The second reason is precisely why I initially was interested, however once it was compiled and deployed to ADFS, clicking the sign in button would not work but hitting the enter key would. After some diagnosing I determined that I was getting Javascript errors on the page referencing InputUtil. I remembered that years ago I had adjusted the onload.js Javascript for the login page of ADFS so that it did not require the domain name within the username text box. This Javascript function was still being called during the second factor authentication and causing the Javascript in the authentication adapter to not run.

To fix this, there are two things that need to be adjusted. Open the file StringResources.resx within the Visual Studio project and go to the key AuthPage. Add the following Javascript below, above function Login(), compile and redeploy to ADFS.

function LoginErrors(){
this.userNameFormatError = 'Enter your user ID in the format 
 or \u0026quot;user@domain\u0026quot;.'; 
this.passwordEmpty = 'Enter your password.'; 
this.passwordTooLong = 'Password must be shorter than 128 characters.';
var maxPasswordLength = 128;

function InputUtil(errTextElementID, errDisplayElementID) {
if (!errTextElementID)  errTextElementID = 'errorText'; 
if (!errDisplayElementID)  errDisplayElementID = 'error'; 

this.hasFocus = false;
this.errLabel = document.getElementById(errTextElementID);
this.errDisplay = document.getElementById(errDisplayElementID);
 InputUtil.prototype.canDisplayError = function () {
     return this.errLabel && this.errDisplay;
 InputUtil.prototype.checkError = function () {
     if (!this.canDisplayError){
         throw new Error ('Error element not present');
     if (this.errLabel && this.errLabel.innerHTML) { = '';        
         var cause = this.errLabel.getAttribute('for');
         if (cause) {
             var causeNode = document.getElementById(cause);
             if (causeNode && causeNode.value) {
                 this.hasFocus = true;
     else { = 'none';
 InputUtil.prototype.setInitialFocus = function (input) {
     if (this.hasFocus) return;
     var node = document.getElementById(input);
     if (node) {
         if ((/^\s*$/).test(node.value)) {
             this.hasFocus = true;
 InputUtil.prototype.setError = function (input, errorMsg) {
     if (!this.canDisplayError) {
         throw new Error('Error element not present');
if (errorMsg) {
    this.errLabel.innerHTML = errorMsg;
this.errLabel.setAttribute('for',; = '';
 InputUtil.makePlaceholder = function (input) {
     var ua = navigator.userAgent;
if (ua != null && 
    (ua.match(/MSIE 9.0/) != null || 
     ua.match(/MSIE 8.0/) != null ||
     ua.match(/MSIE 7.0/) != null)) {
    var node = document.getElementById(input);
    if (node) {
        var placeholder = node.getAttribute("placeholder");
        if (placeholder != null && placeholder != '') {
            var label = document.createElement('input');
            label.type = "text";
            label.value = placeholder;
            label.readOnly = true;
   = 'absolute';
   = 'transparent';
            label.className = node.className + ' hint';
            label.tabIndex = -1;
            label.onfocus = function () { this.nextSibling.focus(); };

   = 'relative';
   = 'relative';
            node.parentNode.insertBefore(label, node);
            node.onkeyup = function () { InputUtil.showHint(this); };
            node.onblur = function () { InputUtil.showHint(this); };
   = 'transparent';

            node.setAttribute("placeholder", "");
 InputUtil.focus = function (inputField) {
     var node = document.getElementById(inputField);
     if (node) node.focus();
 InputUtil.hasClass = function(node, clsName) {
     return node.className.match(new RegExp('(\s|^)' + clsName + '(\s|$)'));
 InputUtil.addClass = function(node, clsName) {
     if (!this.hasClass(node, clsName)) node.className += " " + clsName;
 InputUtil.removeClass = function(node, clsName) {
     if (this.hasClass(node, clsName)) {
         var reg = new RegExp('(\s|^)' + clsName + '(\s|$)');
         node.className = node.className.replace(reg, ' ');
 InputUtil.showHint = function (node, gotFocus) {
     if (node.value && node.value != '') { = 'none';
     else { = '';

You’ll also need to adjust the onload.js Javascript you imported into your ADFS theme. Change function if(Login) with the following.

if(Login) {
     Login.submitLoginRequest = function () { 
    //New Line - This checks to see if the user name text box is visible or not
     if(document.getElementById(Login.userNameInput) != null){
         var u = new InputUtil();
         var e = new LoginErrors();
         var userName = document.getElementById(Login.userNameInput);
         var password = document.getElementById(Login.passwordInput);
         if (userName.value && !userName.value.match('[@\\]')) 
             var userNameValue = 'lorettosystem\' + userName.value;
             document.forms['loginForm'].UserName.value = userNameValue;
  if (!userName.value) {
       u.setError(userName, e.userNameFormatError);
       return false;

    if (!password.value) 
        u.setError(password, e.passwordEmpty);
        return false;
return false;
    return false;

Active Directory Federation Services (ADFS) Authentication Adapter Read More »