Wednesday, July 19, 2017

Git : Publish causes problems

Git in VS 2015 is driving me insane!

When I publish the applications, I get a whole lot of build files etc. in the Git changes folder.

This is despite these files being in the .gitignore.

Undoing the changes does nothing. The changes still sit in the folder.

Then I figured out that:

git reset --hard

gets rid of everything.

Always check with:

git status 

before and after and always ensure you are on the correct branch.


Tuesday, July 18, 2017

Git : Using Beyond Compare with the Bash shell

This post shows how to set up Git Bash.

There is a diff in Git but it's the Unix style showing one side and then the other in text and it's hard to understand. I like visual indicators in a GUI which is where BC comes in.

So first we need to tell Bash where BC is:

$ PATH=$PATH:/c/'Program Files/Beyond Compare 4'

Test this by typing:


and you should get the BC GUI coming up.

Tell Git to use BC for diff.

$ git config --global diff.tool bc

Now compare a file on two branches.

$ git difftool branch1  branch1 -- somepath/Index.cshtml

Viewing (1/1): 'somepath/Index.cshtml'
Launch 'bc' [Y/n]? y

BC will now launch showing the text wizard differences for Index.cshtml between the two branches.

So much cleaner and more readable.

The Bash prompt will stay open until you close BC.


Monday, July 17, 2017

VS : Cannot pull because there are uncommitted changes

Using VS 2015.

The full error is:

"Cannot pull because there are uncommitted changes. Commit or undo your changes before pulling again. See the Output window for details."

So annoying. This happens a lot when trying to pull.

Install Git Bash.

In Windows Explorer, navigate to the directory where your project is and right click.

$ git status
On branch somebranch


Check you are on the right branch. You will get messages about the difference with master.


 $ git pull

Back to VS. Now you get a message about files being updated outside of VS.

Just "Reload all".

Job done!


Thursday, July 13, 2017

ADFS : A SQL operation in the ADFS configuration database failed

The full error is:

A SQL operation in the AD FS configuration database with connection string Data Source=np:\\.\pipe\microsoft##wid\tsql\query;Initial Catalog=AdfsConfigurationV3;Integrated Security=True failed.

Event ID: 352.

This was on Server 2016 with WID after I had done a Windows update.

The normal Google collection of mostly useless information when I searched.

The ADFS service was stopped. Restarting it just gave errors.

Then I came across a post where the user had restarted the "Windows Internal Database" service first.

I tried that - took a while - and then got an error message.

But when I looked at the status of the service it was running.

Restarting the ADFS service then worked - Hallelujah!

And the next thing I did was backup the database with the AD FS Rapid Restore Tool :-)


ADFS : Pop-ups on the HRD / login and change password screens

This was inspired by a post over on the forum.

This is for Server 2012 R2 and 2016.

Full customisation wrap here.

The code you add to onload.js would be something like:

if ( document.getElementById("hrdArea") ) {
        window.alert("Some message");

This would only pop-up on the HRD screen.

This is a JavaScript Alert.

A Confirm would let the user enter "OK" or "Cancel". (What would the point be?).

A Prompt would let the user enter a value but you wouldn't be able to use it other than maybe further customising the screen.

Note: Use at own risk.


Tuesday, July 11, 2017

ADFS : Adding default password rules to the Change and Update Password pages

Continuing the series on customising the ADFS HRD, login and password pages.

This is for Server 2012 R2 and 2016.

There's a full wrap here.

I see continual problems when users have to pick a new password because they are not sure of the actual validation rules detailed in the AD group password policy.

So it would be useful to give them some guidelines.

The change to onload.js is:

if ( document.getElementById("updatePasswordArea") ) {
    if ( document.getElementById("introduction") ) {
        document.getElementById("introduction").innerHTML = "<br/><b>Default Password Rules</b><br/><br/><ul><li>At least 12 characters</li><li>At least one capital & one lower case letter and at least one number (0-9) and / or symbol (e.g. !, $, #, %, @)</li></ul><br/><br/>"

This gives:


Monday, July 10, 2017

ADFS : Removing the copyright notice - the easy way

This is for Server 2012 R2 / 2016.

I've done a series of articles on customising the login, HRD and change password screens here.

The official article for removing the copyright notice is here.

This involves updating the .css file etc.

Following the procedure in my posts, there is an easier way.

Just update the onload.js.

if ( document.getElementById("copyright") ) {
    document.getElementById("copyright").innerHTML = "";


Thursday, June 29, 2017

AD : LDAP connections work remotely but not locally

I have a web API on a server which talks to a DC (via LDAP) on an Azure VM and I have been connecting to it remotely no problem.

Remotely, I connect to The DC shows as in ADUC.

Then I deployed a copy of the web API locally on the VM and the web API started throwing LDAP errors and telling me that there was an invalid user name / password when trying the LDAP connection. The  aforementioned user name / password work perfectly remotely.


So using "LDP" locally, we see:

and then we bind:

This gives:

0 = ldap_set_option(ld, LDAP_OPT_ENCRYPT, 1)
res = ldap_bind_s(ld, NULL, &NtAuthIdentity, NEGOTIATE (1158)); // v.3
    {NtAuthIdentity: User='xxxldap'; Pwd=unavailable; domain = 'dev.local'}
Error 49: ldap_bind_s() failed: Invalid Credentials.
Server error: 8009030C: LdapErr: DSID-0C090516, comment: AcceptSecurityContext error, data 52e, v3839
Error 0x8009030C The logon attempt failed

Now I can try any combination I like. I can leave out the domain name, just use "dev", ... Nothing works.

This works remotely i.e. use "" as the LDAP address and login as dev/userxxx.

So what if I try the "dev.local" address?

The connect works. Now for the Bind.

This works! But only if I leave the domain blank.

This gives:

0 = ldap_set_option(ld, LDAP_OPT_ENCRYPT, 1)
res = ldap_bind_s(ld, NULL, &NtAuthIdentity, NEGOTIATE (1158)); // v.3
    {NtAuthIdentity: User='xxxldap'; Pwd=; domain = ''}
Authenticated as: 'DEV\xxxLDAP'.

I'm not a DC / LDAP guru so can't explain this but hopefully this will help someone stuck on the same issue.


Thursday, June 22, 2017

ADFS : Moving the "Active Directory" IDP entry to the top of the list

This is for ADFS 4.0 (Server 2016).

This post shows how to rename the "Active Directory" IDP and at the bottom of the post is a comment around "move Active Directory to Top" and some script.

This script assumes a set number of IDP and looking at the script, it seems to move the names but not the URL i.e. if you had two IDP viz. IDP A and "Active Directory", it would swop the names around but clicking "Active Directory" would in fact navigate to IDP A.

Also, there are other considerations as mentioned by @Jorge in a reply:

"The list of IdPs in general is dynamic as you may add or remove an IdP. The code should take that into account.

In addition, per RP trust you can also specify a list of IdP that are allowed to use that RP trust and that list is a subset of the overall list of IdPs. Again, the code should take that into account.

It should not matter how long the IdP list is.

Is it possible to make this dynamic where the logic is something like:

* if more than 1 IdP is listed and the AD IdP is included, then move that IdP to the top."

I needed to move this entry to the top.

I would not have got this to work without having the ability to debug.

This is the script I came up with:

// This script moves the "Active Directory" entry (the local IDP) to the top of the list.

// Per RP trust you can specify a list of IdP that are allowed to use that RP trust and that list is a subset 
// of the overall list of IdPs. This list may not have an "Active Directory" entry.

// If there is only one entry, no point in re-ordering!

// The logic is:

// If more than one IDP is listed and the "Active Directory" IDP is included, then move that IdP to the top.


var idp = document.getElementsByClassName("idp");

var totElements = idp.length;

var listAllSpanForIdp = document.getElementsByClassName("idpDescription float");

var adElementPresent = false;

var inc;

for (inc = 0; inc < listAllSpanForIdp.length; inc++) {
  if (listAllSpanForIdp[inc].innerText == "Active Directory") {
    adElementPresent = true;

if ((totElements > 1) && (adElementPresent)) {
  var lastElement = totElements - 1;

  idp[lastElement].parentNode.insertBefore(idp[lastElement], idp[0]);

I move the elements around in the DOM as per this.

idp[lastElement].parentNode.insertBefore(idp[lastElement], idp[0]);

This inserts one node before the other.

You get the parent of the node. You then call the insertBefore method on the parent and you pass it the idp[lastElement] node (the "Active Directory" IDP  one) and tell it you want it inserted before idp[0] (the first one). This then swops their order.

This seems to work across all the requirements.


Wednesday, June 21, 2017

ADFS : Debugging onload.js when customising the Login, Update Password and Home Realm Discovery (HRD) screens

This is for ADFS 3.0 / 4.0 (Server 2012 R2 and 2016).

I've done a number of posts around the customisation but the hard part is that I could not debug the JavaScript in onload.js.

Then I came across JSFiddle.

This allows you to input your HTML, your css and your JavaScript and then run the script and see the result.

The screen is divided into four segments:

Top left = HTML
Bottom left = JavaScript
Top right = css
Bottom right = Result

I'm playing around with the order of the HRD IDP so this post concentrates on that functionality.

(The full post around the order is here).

In a browser, navigate to the HRD screen and then view the source.

Copy the entire:

form id="hrd" method="post" autocomplete="off" ...

section into the HTML.

Obviously, what HTML you select depends on what you are trying to achieve.

Then write your JavaScript in the JavaScript window.

Then click "Run".

The results will show in the Results section.

Perfect - so now I can play around and see the result. This is far easier than going through the whole theme / upload etc. procedure.

What would be the cherry on top is if I could step through and debug it. And like Bob the Builder - "Yes you can!".

 Run JSFiddle in Chrome.

You can add:

console.log (idp.length);

commands if you want.

To hook into the debugger, add this command at the point that you want to invoke it.


In Chrome, hit "F12" - the developer window.

Now hit "Run" in JSFiddle.

The debugger will be activated:

 Now you can step through using the commands top right and view Local etc.

 Any console output you are looking for is under the "Console" tab.

This is SO much easier.


ADFS : Adding extra text and links to the Login and Update Password screens

This is for ADFS 3.0 / 4.0. (Server 2012 R2 / 2016).

There are PowerShell commands to change text but there is nothing to add text, links etc.

We use the js inject mechanism described here.

Assume we want extra links on both the login and Update Password screens but we only want the text on the Update Password page.

And we want the text in blue.

The trick is to find an element on the Update Password HTML that is not on the login page.

"cancelButton" fits the bill.

// Add link and text

if ( document.getElementById("footerPlaceholder") ) {
   if ( document.getElementById("cancelButton") ) {    
     var str = "<p>Some extra text - lots of words - blah blah blah<p>";
     var result = str.fontcolor("blue");    
   } else {
     var result = "";
   document.getElementById("footerPlaceholder").innerHTML = result + "<br/><p>Help? <A href='https://company/help'>Get help</A></p><br/><p>Repost an Issue? <A href='https://company/issues'>Report an Issue</A></p>";

The screens are then:

Note that the "Cancel" button in the "Update Password" screen has been removed as per this.


ADFS : Remove the "Cancel" button from the Update Password screen

This is for ADFS 3.0 / 4.0 (Server 2012 R2 / 2016).

I don't actually get the "Cancel" button on this screen. It doesn't seem to do anything and just confuses people.

So why not remove it?

This is just another change to onload.js. (See this for how to make a new theme etc.).

// Remove Cancel button for "Update Password" screen

if ( document.getElementById("cancelButton") ) {
   document.getElementById("cancelButton").outerHTML = "";

Notice the user of "outerHTML" to remove the whole element.


Tuesday, June 20, 2017

ADFS : Continuing the Login and Home Realm Discovery (HRD) and Change Password customisation adventure

I've posted a number of times on this topic and during my research came across a number of useful articles so I thought I would wrap them all up as a reference.

This is for Server 2012 / Server 2016 - (ADFS 3.0 and 4.0).

I found that most articles I came across simply regurgitate the information in the official articles. I just wonder what the point is?

The official Microsoft reference is:

AD FS user sign-in customisation

Add sign-in page description

 Home Realm Discovery Customisation

Update Password

Some code to allow the ADFS Cancel button on the Update Password page (Expired Password) to redirect back to the original page.

ADFS 3.0 Cancel Button Redirection and Password Change Link


Just remove the button:

ADFS : Remove the "Cancel" button from the Update Password screen

Handling Expired Passwords in AD FS 2012 R2

What's interesting about this article is that the custom js is in a separate file, it's added via the additional file resources and then "injected" i.e.

Set-AdfsGlobalWebContent –SignInPageDescriptionText "&lt;script type=""text/javascript"" src=""/adfs/portal/script/custom.js"">&lt;/script>"

Most examples for text strings simply replace the text with other text but as this shows, you can replace the text with JavaScript. You could use this technique for the other screens as well.

You can add text and links:

ADFS : Adding extra text and links to the Login and Update Password screens

Adding some help text around password requirements:

ADFS : Adding default password rules to the Change and Update Password pages

Login page

Adding buttons instead of links:

Customize the ADFS authentication page with buttons!

Using sAMAccountName to login rather than User Principal Name (UPN) or using DOMAIN\username.

Using SAMAccountName to Login to ADFS in Windows Server 2012R2/2016


Accept SAM-account name as a login format on the ADFS form-based password update page

Don't like the screen - just redo it!

How to “TOTALLY” customize your Home Realm Discovery Page in Windows Server 2012 R2 ADFS

Hiding others customers when using Office 365.

Customizing the AD FS 3.0 Sign-in Page Logo

You can add text and links:

ADFS : Adding extra text and links to the Login and Update Password screens

Hiding some of the RP in the IDPInitiated scenario:

How to Hide a Relying Party from AD FS 3.0

Forcing a button click automatically on logout:

How do I customize the ADFS 3.0 logout page to force sign out?

Don't like the copyright notice?

ADFS : Removing the copyright notice - the easy way 

Slightly different technique to add a link:

Adding A Link To The SSPR Page In The ADFS FBA Page


Changing the "Active Directory" name in ADFS 4.0 to something more appropriate.

ADFS 2016 – Change the Active Directory claim provider display name in the Home Realm Discovery page

At the bottom of that article, there is some JavaScript to show how to move this entry (the local AD) to the top. Unfortunately, it is hard-coded for a certain number of IDP and needs to be more dynamic.

This is a more dynamic solution.

ADFS : Moving the "Active Directory" IDP entry to the top of the list

If you use the -OrganizationalAccountSuffix to associate a RP with an IDP, you get something like:

If you want to skip this, have a read of:

Customize the Home Realm Discovery page to ask for UPN right away

-OrganizationalAccountSuffix is out-of-the-box for ADFS 4.0.

To do this in ADFS 3.0:

Customizing the AD FS sign-in pages per relying party trust

If you want to change the thumbnail images:

Customizing the IDP images in the Home Realm Discovery page

You can add extra text to the pages:

ADFS : Adding messages to the ADFS login / HRD screens

If you are wondering about how to get the small text inside the "button", refer:

ADFS : Adding extra text to the HRD screen IDP description


The standard commands to change the default theme are in the official links at the top.

Note that you can change any collateral in the theme.

ADFS : You can change anything in the Theme structure


"Unless you have saved all the PowerShell cmdLet you typed to create your custom theme in your pre-production environment, it is quite challenging to recreate the exact same webtheme on your production servers. It is easy to export a configuration with the Export-WebTheme cmdLet. But it does not give you the ability to re-import what you just exported. I wrote the following script to help with that:"

How to export an ADFS custom web theme and import it to another serverrt-an-adfs-custom-webtheme-and-import-it-to-another-server/

And you can debug the onload.js. Bargain!

ADFS : Debugging onload.js when customising the Login, Update Password and Home Realm Discovery (HRD) screens

Want pop-ups with that?

ADFS : Pop-ups on the HRD / login and change password screens 

I'll keep adding to this page as new examples come to light.


Friday, June 16, 2017

ADFS : Adding messages to the ADFS login / HRD screens

This is for Server 2012 R2 / Server 2016.

There are a number of posts out there around this, mainly around changing the text of an element in onload.js e.g.

element.innerHTML = "Some text"

where that element already has a text element and you are simply overwriting it.

What I didn't realise is that you can do this with any element, even those that don't currently have text.

I needed to add some text to the HRD screen. The only text there is the copyright message at the bottom.

<div id="footerPlaceholder"></div>
<div id="footer">
<div id="footerLinks" class="floatReverse">
<div><span id="copyright">© 2016 Microsoft</span></div>

There's an element there called "footerPlaceholder". It has no text.


if ( document.getElementById("footerPlaceholder") ) {
   document.getElementById("footerPlaceholder").innerHTML = "<p>
If you need help, please look at <A href='https://company/help'>

gives us:

This then flows into the login page:

If you don't want that, you need to filter by:

//Check if we are in the HRD page
if ( document.getElementById("hrdArea") ) {


Monday, June 12, 2017

ADFS : Beware copying over claims rules when they contain groups

I copy claims rules over all the time as per my earlier post.

So I did this and it wouldn't work.

Much head scratching and then I realised that  one of the claims rules was a "Send Group Membership as a Claim".

If you look at this type of rule, it looks like:

c:[Type == "", Value == "S-1-5-21-965288371-...-1106", Issuer == "AD AUTHORITY"]
 => issue(Type = "", Value = "Admin", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, ValueType = c.ValueType);

Notice that it has a SID and (of course) this SID is relevant to that instance of AD only.

The same group name in a different AD will have a different SID value.

So, beware, you can't just copy these types of rules over!


Tuesday, June 06, 2017

ADFS : Adding extra text to the HRD screen IDP description

This is for Home Realm Discovery for Server 2012 R2 or 2016 (ADFS 3.0 or 4.0).

Building on from @Pierre's ADFS 2016 – Change the Active Directory claim provider display name in the Home Realm Discovery page.

As per that article, the IDP name is coded in the JavaScript as:

var strADCPName = "Corp Users" ;

What you can do is add a line of text beneath it:

var strADCPName = "Corp Users <br/><small>(Add some text here to suit)</small>" ;

This displays something like:

Because of the HTML there are only a limited amount of characters you can add but it is a neat way to add a better description.

If you are wondering how to change the icon (I just used a random one), refer:

ADFS : You can change anything in the Theme structure


Friday, June 02, 2017

ADFS : OAuth token timeout

This is for Server 2016 - ADFS 4.0.

These are the OpenID Connect / OAuth options that you have.
  • Native application
  • Server application
  • Web API
and combinations of the above.

But nowhere in the wizard can you set the token timeout.

AD FS Scenarios for Developers shows the following PowerShell commands:

Add native client                         Add-AdfsNativeClientApplication
Add server application as client  Add-AdfsServerApplication
Add Web API / resource             Add-AdfsWebApiApplication

Building on this we can do:


Name                       : AppA  - Native application
Identifier                 : b1...28
ApplicationGroupIdentifier : AppA
Description                :
Enabled                    : True
RedirectUri                : {ms-app://s-45...04/} 


ADUserPrincipalName                  :
ClientSecret                         : ********
JWTSigningCertificateRevocationCheck : None
JWTSigningKeys                       : {}
JWKSUri                              :
Name                                 : AppB - Server application
Identifier                           : 8e...44
ApplicationGroupIdentifier           : AppB
Description                          :
Enabled                              : True
RedirectUri                          : {https://localhost:1234/}


Name                                 : AppA  - Web API
Identifier                           : {https://localhost:44666/TodoListService}
AccessControlPolicyName              : Permit everyone
AccessControlPolicyParameters        :
AdditionalAuthenticationRules        :
AllowedAuthenticationClassReferences : {}
AllowedClientTypes                   : Public, Confidential
ApplicationGroupIdentifier           : AppA
ApplicationGroupId                   :
AlwaysRequireAuthentication          : False
ClaimsProviderName                   : {}
DelegationAuthorizationRules         :
Enabled                              : True
ImpersonationAuthorizationRules      :
IssuanceAuthorizationRules           :
IssueOAuthRefreshTokensTo            : AllDevices
IssuanceTransformRules               : @RuleTemplate = "LdapClaims"
                                                        ... Some claims ...

NotBeforeSkew                        : 0
Description                          :
PublishedThroughProxy                : False
RefreshTokenProtectionEnabled        : False
RequestMFAFromClaimsProviders        : False
ResultantPolicy                      : RequireFreshAuthentication:False
                                         Permit everyone
TokenLifetime                        : 480

And here we see a token lifetime!

But note the option only applies to web API.

To set this use:

set-AdfsWebApiApplication -TargetIdentifier "https://localhost:44666/TodoListService" -TokenLi
fetime 480


Friday, May 26, 2017

Misc : Top search result in Google

Now this I like :-)

Top search result - awesome.


Thursday, May 25, 2017

ADFS : The case of the slanting quote

I don't know the official name of this character but it's the double quote sign that slants to the right.

There was a question on the forum about a claims rule that threw "POLICY0002: Could not parse policy data" when the user tried to save it.

It looked like:

c:[Type == "", Issuer == "AD AUTHORITY"]
 => issue(type = "", Value = RegExReplace(c.Value,”'”,””);

The problem was the slanting quotes in the RegExReplace.

Using the double quote key (") on the keyboard and replacing these gives:

c:[Type == "", Issuer == "AD AUTHORITY"]
 => issue(type = "", Value = RegExReplace(c.Value,"'","");

which parses no problem.


Monday, May 15, 2017

ADFS : You can change anything in the Theme structure

The official document around onload.js and customising the ADFS login page for 2012 R2 and 2016 (ADFS 3.0 and 4.0) is here.

The Theme directory structure looks like:

Directory of C:\...\Theme\Custom
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM css
05/11/2017 09:26 PM illustration
05/11/2017 09:26 PM images
05/11/2017 09:26 PM script
0 File(s) 0 bytes

Directory of C:\...\Theme\Custom\css
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 8,144 style.css
05/11/2017 09:26 PM 8,146 style.rtl.css
2 File(s) 16,290 bytes

Directory of C:\...\Theme\Custom\illustration
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 09:26 PM 116,699 illustration.png
1 File(s) 116,699 bytes

Directory of C:\...\Theme\Custom\images
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/14/2017 08:45 PM idp
0 File(s) 0 bytes

Directory of C:\...\Theme\Custom\images\idp
05/14/2017 08:45 PM .
05/14/2017 08:45 PM ..
05/11/2017 09:26 PM 931 idp.png
05/11/2017 09:32 PM 1,727 localsts.png
05/11/2017 09:26 PM 1,977 otherorganizations.png
3 File(s) 4,635 bytes

Directory of C:\...\Theme\Custom\script
05/11/2017 09:26 PM .
05/11/2017 09:26 PM ..
05/11/2017 10:23 PM 5,925 onload.js
1 File(s) 5,925 bytes 

The document refers to onload.js but you can use the same commands for any of the above files.

@Pierre e.g. has blogged on changing the "thumbnails" here.

The command for this then looks like:

Set-AdfsWebTheme -TargetName MyTheme -AdditionalFileResource @{Uri="/adfs/portal/images/idp/idp.png";path="C:\MyTheme\images\idp\idp-adfs.png"} 


Friday, May 12, 2017

ADFS : Avoiding the Home Realm Discovery screen by using a link

I was asked if given the HRD screen is just "buttons" and there was a "link" behind that and users always picked the same one. could they not simply add the "link" to their web site and avoid the HRD screen altogether?

The basic problem with this is that the link is not static – it’s actually dynamic.

Take SAML-P as a example.

Assume the HRD screen has two entries; ADFS A and ADFS B.

The SAML request from the application to ADFS A gives:


And then selecting ADFS B via HRD gives:


The SAML request itself is different because as the packet goes through each ADFS, it’s signed with that ADFS’s signing key.

So the "link" would have to be updated every time a signing certificate changed.

In addition, a section of the SAML AuthnRequest contains:


The request contains the time the request was sent – which is dynamic.

There is an ADFS parameter for SAML called “clock skew”.

ADFS rejects the request when the skew between the server clock and the time in the request is more than 30 seconds (by default).

So nice try but no cigar!


ADFS : Augmenting the default JWT with additional attributes

This is for Server 2016 - ADFS 4.0.

The standard use case is for an ASP.NET application using OpenID Connect / OAuth via the NuGet OWIN packages taking to ADFS.

The standard way is to configure a server application as above.

This all works . The problem is that the ADFS wizard does not have any way to configure claims rules. As a result you get the standard set of claims in the JWT.

aud     8173...1501
iat     1494378378
exp     1494381978     1494378377
nonce     6362....NmVl     0JrA...H7k=     user1@dev.local     DEV\user1
c_hash     baOc...R8GA

The only user specific details are the "upn" and the "name".

But what if you want more?

You can use the "Web browser accessing a web application" profile as per this.

This profile achieves the result in a roundabout way:

"Behind the scenes, this template creates a native client and new app type called Web application, which is just a Web API with an Identifier (RPID) that matches the native client's client ID. This means the Web application is simultaneously client and resource, so you can assign issuance transform rules as you would with a Web API."

I configured some claims in the wizard:

Now when I authenticate, I get this:

aud     c906...3ab4
iat     1494379409
exp     1494383009     1494379408
nonce     6362...NTk2     PRUA...50U=     DEV\user1     user1@dev.local     User1     Test     User1 Test
apptype     Public
appid     c906...3ab4     urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
ver     1.0     openid
c_hash     -0ZX...Z81A

You will also notice that the apptype is "Public" i.e. a web site as opposed to "confidential" i.e. a web API.

I also answered a question in stackoverflow along these lines here.

As per the discussion from @Efrain:

"As indicated in @nzpcmad's answer, it appears that custom claims in the id_token using the default URL-parameter-encoded GET redirect is simply not supported. The reason for this may be that there is an URL length limit, but I find that quite questionable.

Anyway, apparently this restriction does not apply when the token is returned in a POST redirect. That's also why people describe it working just fine for MVC applications.

So I was able to work around the problem by redirecting the response to a back-end API endpoint (POST), which just redirects it to the front-end (SPA) again, but as a GET request with URL-encoded parameters:

public class LoginController : ApiController
    public HttpResponseMessage Login(FormDataCollection formData)
        var token = formData["id_token"];
        var state = formData["state"];
        var response = Request.CreateResponse(HttpStatusCode.Moved);
        var frontendUri = ConfigurationManager.AppSettings["ad:FrontendUri"];
        response.Headers.Location = new Uri($"{frontendUri}#id_token={token}
        return response;
Note that to change the response method from GET to POST, one simply has to add &response_mode=form_post to the OAuth request URL."


Monday, May 08, 2017

ADFS : Passing NameID across CP and RP

Imagine the following:

CP A --> federated with CP B --> RP

So a user goes to the RP and via HRD on CP B selects CP A and authenticates against AD.

The claims derived from CP A need to be passed across.

On CP A we have the standard LDAP rules since the user authenticated against that AD.

One of the claims we want to pass across is NameID.

You have to configure pass-through rules on CP B and the RP.

So the claims are configured in three places.

The problem is that NameID never makes it across.

There are a number of posts from people reporting the same thing but no solution.

The way I got around it was:

Assume that we want sAMAccountName to be NameID.

On CP A, have a LDAP rule:

sAMAccountName --> http://company/claims/sAMAccountName

Plus pass-through all the other claims.

On CP B, have a Transform rule:

http://company/claims/sAMAccountName --> NameID

Plus pass-through all the other claims. 

In the RP, pass-through all the claims including NameID.

Now imagine you have two RP, RP A and RP B.

RP A wants sAMAccountName to be NameID.

RP B wants UPN to be NameID.

Now we have a problem because they both share the same pipeline CP A and CP B. You can't have two different rules both passing NameID.

What you have to do on CP A is:

UPN --> http://company/claims/UPN

Here you pass both http://company/claims/sAMAccountName and http://company/claims/UPN through on CP B and then transform them at the RP level to the NameID; one for each.


Wednesday, April 12, 2017

Visual Studio : ReplacableToken_ApplicationServices on transform

Using VS 2015 and doing a transform on my web.config.

My connection string is something like:

connectionString="data source=.\SQLEXPRESS;Integrated Security ..."

but what appears in the transformed file is:

connectionString="$(ReplacableToken_ApplicationServices-Web.config Connection String_0) ..."


Much conversation with Mr. Google and eventually the solution is:


You need to add this to the appropriate .pubxml file under Properties\PublishProfiles


Thursday, April 06, 2017

C# : Sending email

I needed a quick and dirty C# module to test a SMTP server.

So I came up with this.

I used Windows Forms and a very simple button.

This uses the System.Net.Mail.MailMessage class.


ASP.NET : Comparing a VS project to a web site

We have a legacy project which has been untouched and unloved for many years.

Then we needed to make an urgent change.

The problem was that the people who had worked on the project were long gone and nobody knew if the source in the repository was up to date?

So we took a copy of the web site,

You can't just compare this to the VS project. In VS, you have an aspx file and a corresponding cs file. In the web site, the aspx files are there but the cs files are all rolled up into a dll.

You can't directly compare dll either. There's guids, dates etc. that keep changing every time you do a build.

The way to do this is to deploy your current project to IIS and then compare the two web sites.

You still have the problem of comparing the dll files in the \bin directory.

To do this, decompile the dll into a VS project using something like Reflector or dotPeek and then compare the source code.

Some of the names will be different e.g. "isComplete" vs. "flag1" but it will give you a pretty good idea of whether there have been code changes,


Thursday, March 30, 2017

WCF : Calling an async. method

Just for a change, I was asked to help out on a non-Identity project.

There was a legacy host that only understood SOAP and a modern back-end that only supported REST web API.

So we need a bridge between them and I had to remember everything I had ever forgotten about WCF.

I needed my WCF method to call:

string response = await my_api.CallREST(parameter);

The problem is that the compiler expects the method to be decorated with async.

Aync. WCF?

Turns out you can.

My method looks like:

public async Task<validateresponse> ValidateParameter (parameter)

... and similarly for the interface.

And it works!

The word "async" doesn't appear anywhere is the WSDL. It appears to be completely ignored.


Friday, March 24, 2017

ADFS : Copy claims rules over

Don't know how many times I've done this.

Deploy to Dev., get everything working, deploy to QAS, support QAS acceptance testing, deploy to Prod., smoke test.

When you have a lot of claims rules to copy over, I found a neat way to do it.

e.g. for a CP.

(Get-AdfsClaimsProviderTrust -Name "My CP").AcceptanceTransformRules | Out-File “C:\path\CPClaimsRules.txt”

And then import the rules to the new CP.

Set-AdfsClaimsProviderTrust -TargetName "My CP" -AcceptanceTransformRulesFile “C:\path\CPClaimsRules.txt” .

I've found that this also avoids the issue where you keep a copy of the rules in e.g. Word and then when you try and paste them into the ADFS wizard, you get all kinds of format errors.

Update: But beware copying groups over.


Wednesday, March 22, 2017

ADFS : Creating a custom attribute store

This is for ADFS 4.0 on Server 2016.

This is a good write-up.

Unfortunately, all the code is in a screen shot which sucks. Somewhat difficult to copy / paste :-).

Luckily, similar code can be found here.

Just standardise the names; one is "ToUpper"; the other is "toUpper".

However, my requirement was for getting claims from a back-end (details unimportant for the purposes of this post) where a user could have many claims of that type returned. Think of a property ID where one person owns a house but an investor owns several.

All the examples were for returning one attribute.

Essentially, you are returning a C# jagged array e.g. string[][] resultData.

My query string was :

c:[Type == ""]
 => issue(store = "CAS", types = ("HouseID"), query = "House", param = c.Value);

So I assumed that the multiple claims would be in the same row i.e. one row; many columns.

The search to the back-end returned 3 houses.

I then got:

Microsoft.IdentityServer.ClaimsPolicy.Language.PolicyEvaluationException: POLICY0019: Query 'House' to attribute store 'CAS' returned an unexpected number of fields: expected '1', got '3'.

If you look at the query, you'll see there is only one query parameter which will only return one result. A query which returns 3 results would be like:

query = "House", sn, mail

So what I need is 3 rows; one column.

I found some guidance around this here.

One of the things that stumped me for a while was the fact that the array had to be dynamic because there could be any number of houses. That's why you add to a list and then cast to an array.

 Another gotcha was the fact that while you can have a static rule like:

=> issue(type = "HouseID", value = "123456");

you cannot have that in a query string for the attribute store. You get:

System.ArgumentException: ID4216: The ClaimType 'HouseID' must be of format 'namespace'/'name'.

So it needs to be:

c:[Type == ""]
 => issue(store = "CAS", types = ("http://claim/HouseID"), query = "House", param = c.Value);

i.e. HouseID becomes http://claim/HouseID.

Once you have compiled a new .dll file, you have to copy it over in the ADFS directory on the server.

You will get "Access Denied" because ADFS is running. So you have to stop the ADFS service, copy over the .dll and then start up the service again.

You do not have to delete the custom attribute store in the wizard and reload it. When ADFS starts. it will load the latest .dll.

The gist with the code is here.

But if you want a sneak preview, the important part is:

    // Dummy values to illustrate the principle.

    List<string> claimValues = new List<string>();
    List<string[]> claimData = new List<string[]>();

    // Each claim value is added to its own string array 
    foreach (string claimVal in claimValues)
        claimData.Add(new string[1] { claimVal });

    // The claim value string arrays are added to the string [][] that is 
    // returned by the Custom Attribute Store EndExecuteQuery()
    string[][] resultData = claimData.ToArray();

    TypedAsyncResult<string[][]> asyncResult = new TypedAsyncResult<string[]
    []>(callback, state);
    asyncResult.Complete(resultData, true);
    return asyncResult;

catch (Exception ex)
    String innerMess = "";
    if (ex.InnerException != null)
        innerMess = ex.InnerException.ToString();
    throw new AttributeStoreQueryExecutionException("CAS exception : " +
       ex.Message + " " + innerMess);


Tuesday, March 21, 2017

NLOG : Logging for both web site and web API

I have a standard .NET 4.5 MVC web site with web API.

I added NLOG to the web API via the NuGet packages and log away quite happily.

Part of the NuGet are the files:
  • NLog.config
  • NLog.xsd
I then wanted to do logging in the web site. So add the NuGet to that. Added quite happily but no NLog files?

So I copied the two NLog files from the web API project and pasted them into the web site project.

I changed the logging file name. So I have web-site.log and web-api.log.

Works fine.


Monday, March 20, 2017

ADFS : WIF10201: No valid key mapping found

The error is:

WIF10201: No valid key mapping found for securityToken: 'System.IdentityModel.Tokens.X509SecurityToken' and issuer: 'http://MY-ADFS/adfs/services/trust'.

I have a simple WIF application circa VS 2012 that I use to display claims and ported it over to use on ADFS 4.0.

Then I got the above error.

The solution is as per Signing key rollover in Azure Active Directory.

Yes - it says AAD but the client-side code for ADFS is the same since it's all driven from the metadata.

Use the code from: "Web applications protecting resources and created with Visual Studio 2012".

When I compared the web.config changes, the error seemed to be because the server name is "MY-ADFS" (in caps) but I had written "my-adfs" (no caps) in the web.config.

The thumbprint was also in caps. (Although I've never had an issue with that).

It gives you a nice comment:

"Element below commented by: ValidatingIssuerNameRegistry.WriteToConfg on: '20/03/2017 1:00:16 a.m. (UTC)'. Differences were found in the Metadata from: ..."


AAD : Beware the difference in the V1.0 and V2.0 endpoints

The V2.0 endpoint is the endpoint that allows you to sign in with the converged Microsoft and Azure Active Directory accounts.

From an OpenID / OAuth perspective, the discovery documents can be found at:



Notice the extra "V2.0".

If we look at the keys, we see for V1.0:

And for V2.0 :

You will notice that V2.0 has four signing keys while v1.0 only has two.

V2.0 is the endpoint used by B2C.

Don't assume that tokens signed by Azure AD (V1.0) are also acceptable for B2C (V2.0) and vice versa.

This will be true when AAD and B2C are merged at some point in the future but right now it's a gotcha!


Wednesday, March 15, 2017

ADFS : Using the client_credentials flow with ADFS 4.0 returns 401

Been battling with this for ages. I'm using ADFS 4.0 on Server 2016.

My web site uses OpenID Connect and that uses the OWIN authorisation code grant.

When I use the authorisation code grant,  I can get the code, exchange the code for an access token and then call a web API with that access token no problem.

But that flow requires a user to authenticate and for some of my use cases there is no user. An example would be a forgotten password flow where the user cannot authenticate. To do that, I use the client_credentials flow.

Getting this to work was a non-trivial task since the documentation is (shall we say) sub optimal.

I also needed the ADAL Nuget package. Interesting that you can mix OWIN and ADAL

So under "Application Groups", I have the "Server application accessing a web API" scenario.

The server application has a client ID and the secret key.

So assume the general API URL is:


And the API's inside this are like:


The web API in the wizard has the RP identifier:


(This matches the ValidAudience below).

Access control policy is:

Permit everyone

I have one custom claim rule:

c:[] => issue(claim = c);

Now there is no way to pass these claims back in the JWT but I added this during my troubleshooting.

Client permissions is set to:

"All clients"

with scope of:

openid and user_impersonation.

The web API controller is decorated with  [Authorize].

The code is:

using Microsoft.IdentityModel.Clients.ActiveDirectory;

ClientCredential clientCredential = new ClientCredential(clientId, secretKey);

AuthenticationContext ac = new AuthenticationContext("https://my-adfs/adfs/", false);
AuthenticationResult result = await ac.AcquireTokenAsync("https://my-pc/WebService"

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,  
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

HttpContent content = new FormUrlEncodedContent(new[] { new KeyValuePair<string,  
string>("foo", "blah"), new KeyValuePair<string, string>("foo1", "blah1") });
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);

if (response.IsSuccessStatusCode)
    // etc

To call a second API, it's the same code with one change:

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://my-pc/WebService/api/my-api1/");

I also has to change to code for the web API App_Start, Startup.Auth.cs.

    new ActiveDirectoryFederationServicesBearerAuthenticationOptions
        TokenValidationParameters = new TokenValidationParameters()
            SaveSigninToken = true,
            ValidAudience = "https://my-pc/WebService"
        MetadataEndpoint = "https://my-adfs/FederationMetadata/2007-06/FederationMetadata.xml"


Monday, March 06, 2017

ADFS : Health Check

This is a question I've been asked a number of times.

Usually, you just ping the metadata endpoint or the IDPInitiatedSignOn endpoint.

Then I found: AD FS Diagnostics Module.

"The AD FS Diagnostics Module contains commandlets to gather configuration information of an AD FS server, as well as commandlets to perform health checks to detect configuration issues based on common root causes identified during support engagements such as duplicate SPN, cert".

There are some other useful links on the LHS.

On my list to try out :-)


Tuesday, February 14, 2017

AD : Using PowerShell to create users and groups

ADFS specifically targets authentication and authorisation.

It does not target provisioning users into AD or adding them to groups. You need an Identity Manager for that.

You can do this with PowerShell and there are many links to do this around on the Internet e.g. PowerShell: Bulk create AD Users from CSV file.

My version

csv file format is:



$Users = Import-Csv -Path "C:\blah\Users.csv"            
foreach ($User in $Users)            
    $Displayname = $User.'Firstname' + " " + $User.'Lastname'            
    $UserFirstname = $User.'Firstname'            
    $UserLastname = $User.'Lastname'            
    $OU = $User.'OU'            
    $SAM = $User.'SAM'            
    $UPN = $User.'Firstname' + "." + $User.'Lastname' + "@" + $User.'Maildomain'            
    $Password = $User.'Password'  
    $Email = $User.'Email'
    New-ADUser -Name "$Displayname" -DisplayName "$Displayname" -SamAccountName $SAM -UserPrincipalName $UPN -GivenName "$UserFirstname" -Surname "$UserLastname" -Description "$Description" -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) -EmailAddress "$Email" -Enabled $true -Path "$OU" -ChangePasswordAtLogon $false –PasswordNeverExpires $true -server mydomain.local 

    Write-Host –NoNewLine "Adding user:  "
    Write-Host $SAM
$Users = Import-Csv -Path "C:\blah\Users.csv"            
foreach ($User in $Users)            
    $SAM = $User.'SAM'            
    Add-ADGroupMember -Identity "My Group 1" -Member $SAM
    Add-ADGroupMember -Identity "My Group 2" -Member $SAM
    Write-Host –NoNewLine "Adding groups for user:  "
    Write-Host $SAM


Wednesday, February 08, 2017

ADFS : Useful PowerShell cmdlets

I seem to be running these on just about every installation I do these days so thought it would be worthwhile to note them.

Login with email address

This seems to be more and more common.

Set-AdfsClaimsProviderTrust -TargetIdentifier "AD AUTHORITY" -AlternateLoginID mail -LookupForests 

There are some gotchas with this especially if you are thinking of extending out to Azure (via AD Connect) at some point.

Configuring Alternate Login ID

Certificate revocation

Most Dev. instances don't have access to the extranet. The ADFS login will be slower because ADFS will try and check for certificate revocation.

So it makes sense to remove this functionality.

Set-AdfsRelyingPartyTrust -TargetIdentifier urn:xxx -SigningCertificateRevocationCheck None -EncryptionCertificateRevocationCheck None


Although the time across servers should be consistent, in a lot of cases it isn't. This means that if the ADFS server is ahead, the SAML token will be in the future and the SAML RP will reject it.

Some SAML RP that I have dealt with have the skew hard coded so it cannot be altered.

The best solution is to ensure that the server time is synchronised but if that is not possible, you can "back date" the time in the token. The cmdlet below sets the time 3 minutes backwards.

Set-AdfsRelyingPartyTrust -TargetIdentifier urn:xxx:de -NotBeforeSkew 3

ADFS Not Before Time Skew 


Wednesday, January 18, 2017

WebAPI : Using Swagger and Postman with multiple POST

I've been busy on a WebAPI project where I need multiple POST actions in a a WebAPI controller.

There's a ton of stuff around best practice for this - some say each controller should only have one POST; others disagree.

I finally decided to break the controllers up by category and then within each controller have multiple POST functions.

I also added Swagger via Swashbuckle and used Postman for some unit tests so I thought I would write this all up.

As an example, I have a Maths controller with Add and Multiply.

The controller:

using MathsWebAPI.Models;
using System.Web.Http;

namespace MathsWebAPI.Controllers
    public class MathsController : ApiController
        // POST: api/Maths
        public int Add([FromBody] Maths info)
            int number1 = info.number1;
            int number2 = info.number2;

            return (number1 + number2);

        // POST: api/Maths
        public int Multiply([FromBody] Maths info)
            int number1 = info.number1;
            int number2 = info.number2;

            return (number1 * number2);

Note that I am passing in a model object as I have multiple parameters.

The model object:

namespace MathsWebAPI.Models
    public class Maths
        public int number1 { get; set; }

        public int number2 { get; set; }

The WebAPIConfig.cs:

using System.Web.Http;

namespace MathsWebAPI
    public static class WebApiConfig
        public static void Register(HttpConfiguration config)
            // Web API configuration and services

            // Web API routes

                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }

                name: "ControllerAndAction",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new {id = RouteParameter.Optional}

In SwaggerConfig.cs, uncomment:


to avoid the error message.

Now if I navigate to:


where 44571 is my IIS Express port

I see:

Now if you click the yellow area, it will populate the "Info" box with the model object format and you just have to fill in the values.

Click "Try it out" and you see:

 Note the URL is:


Now if we add Postman, the commands are in this gist.

Running the script, we see: