Category Archives: Identity Management

ADFS Username Behavior

Problem

ADFS 4.0 on Windows Server 2016 tells users to log in with their full email address “someone@example.com.”  This generates many support requests, and complaints about too much typing.

Additionally, some extranet users may have email addresses not on the domain, and it’s unclear which email address they should supply.

This affects both the ADFS log in page, and the ADFS password change page.

Solution Methodology

ADFS Server 4.0 has PowerShell cmdlets to manage the content delivered to users during authentication requests: https://technet.microsoft.com/windows-server-docs/identity/ad-fs/operations/ad-fs-user-sign-in-customization

We’ll focus on the following

Get-AdfsWebTheme

and

Set-AdfsWebTheme

Of particular interest here is that we’re able to modify the JavaScript that runs on these pages.

Steps

Use PowerShell to manage custom ADFS Themes

  1. Export the Default ADFS Theme using this snippet:
     Export-ADFSWebTheme -Name "Default" -DirectoryPath c:\test
  2. Use your  favorite editor to open c:\test\script\onload.js
  3. Add the snippets from below (as desired) into onload.js
  4. Create a New ADFS Theme
     New-AdfsWebTheme -Name BetterDefault -SourceName c:\test 
    1. Set your new theme as the default (best for testing)
       Set-ADFSWebConfig -ActiveThemeName BetterDefault 
  5. Alternatively, you may update an existing theme with your code changes
    Set-AdfsWebTheme -TargetName "Default" -AdditionalFileResource @{Uri=“/adfs/portal/script/onload.js”;Path=“C:\theme\script\onload.js"}

Placeholder Text Solution

To update the “someone@example.com” placeholder on both the login and the password change ADFS pages, paste this code into your onload.js, and update your ADFS theme.

function UpdatePlaceholders() {
    var userName;
    if (typeof Login != 'undefined'){
        userName = document.getElementById(Login.userNameInput) 
    }
    if (typeof UpdatePassword != 'undefined'){
        userName = document.getElementById(UpdatePassword.userNameInput);
    }
    if (typeof userName != 'undefined'){
        userName.setAttribute("placeholder","Username");
    }
}

document.addEventListener("DOMContentLoaded", function(){
  // Handler when the DOM is fully loaded
  UpdatePlaceholders()
});

 

Formatting of the Username field

For single-domain organizations, it may be less than desirable to force users to enter the domain name as part of their username. To “fix” this requirement of entering usernames in a format of “domain\username” or “username@domain.com”, paste the following code into your onload.js.  Make sure to update your domain where appropriate.

Logon Username Format Solution

 


if (typeof Login != 'undefined'){
    Login.submitLoginRequest = function () { 
    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 = 'example.org\\' + 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;
    }
    document.forms['loginForm'].submit();
    return false;
};
}

Password Change Username Formatting Solution


if (typeof UpdatePassword != 'undefined'){
    UpdatePassword.submitPasswordChange = function () { 
    var u = new InputUtil();
    var e = new UpdErrors();

    var userName = document.getElementById(UpdatePassword.userNameInput);
    var oldPassword = document.getElementById(UpdatePassword.oldPasswordInput);
    var newPassword = document.getElementById(UpdatePassword.newPasswordInput);
    var confirmNewPassword = document.getElementById(UpdatePassword.confirmNewPasswordInput);

    if (userName.value && !userName.value.match('[@\\\\]')) 
    {
        var userNameValue = 'example.org\\' + userName.value;
        document.forms['updatePasswordForm'].UserName.value = userNameValue;
    }

    if (!userName.value) {
       u.setError(userName, e.userNameFormatError);
       return false;
    }

    if (!oldPassword.value) {
        u.setError(oldPassword, e.oldPasswordEmpty);
        return false;
    }

    if (oldPassword.value.length > maxPasswordLength) {
        u.setError(oldPassword, e.oldPasswordTooLong);
        return false;
    }

    if (!newPassword.value) {
        u.setError(newPassword, e.newPasswordEmpty);
        return false;
    }

    if (!confirmNewPassword.value) {
        u.setError(confirmNewPassword, e.confirmNewPasswordEmpty);
        return false;
    }

    if (newPassword.value.length > maxPasswordLength) {
        u.setError(newPassword, e.newPasswordTooLong);
        return false;
    }

    if (newPassword.value !== confirmNewPassword.value) {
        u.setError(confirmNewPassword, e.mismatchError);
        return false;
    }

    return true;
};
}

Thanks for reading!  If you have any questions, feel free to send me a tweet @crossan007.

FIM Portal No Access for FIM Admin Account

Today’s adventure with Forefront Identity Manager started when I was unable to log into the FIM portal.  Some digging revealed that the accountName attribute for my admin user had been set to null (probably from too much tinkering with sync rules).

I realized that the accountName was probably the issue by two indicators: there was no account name attribute for the FIM Admin object in the FIM Synchronization Service Manager application, and because the query below referencing the ObjectValueString table lacked some attributes. The change-fimadmin.ps1 script helped me determine these SQL sanity check queries.

I had already eliminated the usual suspects for not being able to access the portal (ObjectSID, MPRs, etc), so this stumped me for a little while

Anyway, I needed a way to get back in the portal (and I didn’t want to re-install), so I came up with this script that uses the FIM PowerShell modules to set the accountName attribute of the FIM Admin user (identified by the well-known admin user GUID).

I used the script on How to Use PowerShell to Set the Required Attributes for the FIM Portal Access as a starting point, modifying it to set only the accountName attribute.

$adminAccountName=”accountNameHere”

If(@(get-pssnapin | where-object {$_.Name -eq “FIMAutomation”} ).count -eq 0) {add-pssnapin FIMAutomation}

Function SetAttribute
{
PARAM($CurObject, $AttributeName, $AttributeValue)
END
{
$ImportChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange
$ImportChange.Operation = 1
$ImportChange.AttributeName = $AttributeName
$ImportChange.AttributeValue = $AttributeValue
$ImportChange.FullyResolved = 1
$ImportChange.Locale = “Invariant”
If ($CurObject.Changes -eq $null) {$CurObject.Changes = (,$ImportChange)}
Else {$CurObject.Changes += $ImportChange}
}
}
$curObject= export-fimconfig -uri $URI –onlyBaseResources -customconfig (“/Person[ObjectID='{7fb2b853-24f0-4498-9534-4e10589723c4}’]”)

$ImportObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject

$ImportObject.ObjectType = $curObject.ResourceManagementObject.ObjectType
$ImportObject.TargetObjectIdentifier = $CurObject.ResourceManagementObject.ObjectIdentifier
$ImportObject.SourceObjectIdentifier = $CurObject.ResourceManagementObject.ObjectIdentifier
$ImportObject.State = 1

SetAttribute -CurObject $ImportObject -AttributeName “AccountName” -AttributeValue $adminAccountName
$ImportObject | Import-FIMConfig -uri $URI -ErrorVariable Err -ErrorAction SilentlyContinue

 

After running this script, you should be able to log into the FIM portal again.

Helpful places to look also include the FIMService database.  Particularly the ObjectValueString and UserSecurityIdentifiers Tables.

 

The following query represents the values for the FIM Admin User, and should yield 7 rows(Attribute Keys 1,66,68,70,117,125,132)

SELECT TOP 1000 [AttributeID]
,[ObjectKey]
,[ObjectTypeKey]
,[AttributeKey]
,[SequenceID]
,[LocaleKey]
,[ValueString]
,[Multivalued]
FROM [FIMService].[fim].[ObjectValueString]

where ObjectKey =2340

The following query represents the SID, in HEX form, of the FIM Admin User, and should yield 1 row:

SELECT TOP 1000 [UserObjectKey]
,[SecurityIdentifier]
FROM [FIMService].[fim].[UserSecurityIdentifiers]
where UserObjectKey =2340

 

 

 

ForeFront Identity Manager (2010 R2) Synchronization Service Becomes Disabled

I had just installed FIM 2010 R2 in a lab environment – All roles on one server, and noticed that the “Forefront Identity Manager Synchronization Service”  Kept stopping, and being set to “Disabled.”

I had installed all of the latest patches for both FIM (4.1.3613.0) and SharePoint (14.0.7140.5000). I saw nothing in the Windows Event log to indicate there was a problem.  The “Synchronization Service Manager on FIM” application would launch fine while the service was running.  All configured management agents would synchronize no problem.  It’s just that – Every so often, the  “Forefront Identity Manager Synchronization Service” would just up and STOP!

After some research, I found this TechNet Blog Post which suggests that SharePoint is the culprit!

Since SharePoint is installed on this server only for the purpose of providing the FIM Portal, I had no need to set up (or otherwise use) the User Profile Service.   Consequently, SharePoint isn’t expecting the Synchronization Service to be in a running state! So, as part of the “Health Analysis Job (Hourly, Microsoft SharePoint Foundation Timer, All Servers)” SharePoint notices the service is running, and disables it!

I verified this by returning the service to the normal Automatic – Running state, and manually kicking off that Timer Job.

Sure enough, the job was stopped! ULS Viewer shows me this also:

See that? – “The SharePoint Health Analyzer found and fixed the following problem: One or more services have started or stopped unexpectedly..”  No details about which service was “started unexpectedly,” but I presume that the “Forefront Identity Manager Synchronization Service” was the culprit.

To prevent this from happening again, I nagivated to “Review Job Definitions” under “Monitoring” in Central Administration, located the  “Health Analysis Job (Hourly, Microsoft SharePoint Foundation Timer, All Servers)” and disabled it.

All seems well at this point in time…

My set of links for FIM installation tips, tricks, procedures, etc:

  • https://technet.microsoft.com/en-us/library/hh332711(v=ws.10).aspx
  • http://www.harbar.net/articles/fimportal.aspx
  • http://www.toddklindt.com/blog/Lists/Posts/Post.aspx?ID=224
  • http://social.technet.microsoft.com/wiki/contents/articles/2229.fim-2010-build-overview.aspx
  • http://www.fimspecialist.com/fim-portal/installing-fim-2010-r2-sp1-portal-on-sharepoint-foundation-2013/
  • https://social.technet.microsoft.com/Forums/en-US/76bd6012-f619-4636-8401-74cac8436f1f/fim-sync-service-keeps-disabling?forum=ilm2
  • http://www.fimspecialist.com/category/fim-hotixes-service-packs-updates/
  • https://technet.microsoft.com/en-us/library/jj200258(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh322920(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh322863(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/jj134316(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh322877(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh332711(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh332707(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh332708(v=ws.10).aspx
  • https://technet.microsoft.com/en-us/library/hh332710(v=ws.10).aspx
  • https://fim2010techie.wordpress.com/2012/12/10/synchronizing-active-directory-users/
  • http://blogs.msdn.com/b/connector_space/archive/2014/12/30/understanding-the-fim-service-management-agent.aspx
  • http://social.technet.microsoft.com/wiki/contents/articles/3610.fim-2010-wiki-articles.aspx