Wednesday, October 18, 2017

IdentityServer : WS-Fed metadata imported into ADFS

I've been looking at Identity server 4 (idsrv4) just to have a play with it.

This runs on .NET Core which I something else I need to get up to speed on.

There is also a WS-Fed plug-in which I got working and tried to hook up to ADFS as an exercise.

The metadata endpoint is:

http://localhost:5000/wsfederation

and when I tried to import this into ADFS, I got the normal:

"Metadata contains some features not supported by ADFS" warning.

Now this could be because the metadata contains a SAML profile that ADFS doesn't support - PAOS being an example.

But 999 out of 1000 times, it's because the endpoints are "http" not "https".

Looking at the metadata, this is indeed the case.

This means that although the entry is added to ADFS, it has no endpoints so it will never work.

You can't just edit the metadata because it's signed and you'll get a signing error when you try and import the updated file.

You can delete the whole "Signature" section in the XML if you want. Do this at your own risk - normal best practice security applies :-).

The other way is to update the metadata when it's generated. There is no metadata file - it's dynamically generated every time.


You can do this in the "Properties".

Select the "Enable SSL" check box. IIS Express generates a new endpoint as above so now you have to replace all the instances of:

http://localhost:5000

with the https endpoint as above.

This also means that you need to change this address in any of the client samples.

The new metadata imports without issues.

Enjoy!

Wednesday, October 11, 2017

ADFS : PowerShell cmdlet - parameter PolicyMetadata

Another question on the forum around the format of PowerShell parameters.

This one was around PolicyMetadata.

Get-AdfsAccessControlPolicy -Name "Demo"

Name           : Demo
Identifier     : Demo
IsBuiltIn      : False
RpUsageCount   : 0
LastUpdateTime : 10/10/2017 7:22:00 PM
Description    :
PolicyMetadata : RequireFreshAuthentication:False
                 IssuanceAuthorizationRules:
                 {
                   Permit everyone
                 }
AssignedTo     : {} 


Now if you copy / paste the metadata into a file and then run:

New-AdfsAccessControlPolicy -Name "DemoOne" -PolicyMetadataFile c:\Filename

you get all kinds of errors.

Looking at the errors e.g. "Root error" made me think that the format wasn't JSON, rather XML.

Which means that it is almost impossible to guess the element names etc.

So Mr. Google to the rescue and a long time later, I came across:

(Get-AdfsAccessControlPolicy -Name "Permit everyone").PolicyMetadata | fl *

which displays:

IsParameterized : False
Serialized      : <PolicyMetadata xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns="http://schemas.datacontract.org/2012/04/ADFS">
                    <RequireFreshAuthentication>false</RequireFreshAuthentication>
                    <IssuanceAuthorizationRules>
                      <Rule>
                        <Conditions>
                          <Condition i:type="AlwaysCondition">
                            <Operator>IsPresent</Operator>
                            <Values />
                          </Condition>
                        </Conditions>
                      </Rule>
                    </IssuanceAuthorizationRules>
                  </PolicyMetadata>
Summary         : RequireFreshAuthentication:False
                  IssuanceAuthorizationRules:
                  {
                    Permit everyone
                  }
ExtensionData   : System.Runtime.Serialization.ExtensionDataObject


Putting that into a file e.g.

<?xml version="1.0" encoding="UTF-8"?>
<PolicyMetadata xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://schemas.datacontract.org/2012/04/ADFS">
    <RequireFreshAuthentication>false</RequireFreshAuthentication>
        <IssuanceAuthorizationRules>
            <Rule>
                <Conditions>
                    <Condition i:type="AlwaysCondition">
                        <Operator>IsPresent</Operator>
                        <Values />
                    </Condition>
                </Conditions>
            </Rule>
        </IssuanceAuthorizationRules>
</PolicyMetadata>


and then running the command works!

I suggest running:

Get-AdfsAccessControlPolicy  

which displays them all and then look at the XML formats to get some hints as to the XML format.

Enjoy!

Saturday, October 07, 2017

ADFS : PowerShell cmdlet - parameter is an array

Over on the forum, there was a question around a parameter in the cmdlet that accepts multiple options as an array i.e.

"-RedirectUriSpecifies an array of redirection URIs for the OAuth 2.0 client to register with AD FS".

The question was around the format of the array since that is not specified.

Looking at another example:

Set-AdfsRelyingPartyTrust -TargetName claimapp -ClaimsProviderName @("Fabrikam","Active Directory")

the array is of the form:

@("Fabrikam","Active Directory")

Enjoy!

Thursday, October 05, 2017

OAuth2 : Displaying JWT tokens

The standard way that I've been looking at and debugging with JWT tokens is via:

https://jwt.io

Now, Microsoft have come to the party with:

https://jwt.ms

It starts off with


Then you copy / paste the ID token and it will display the details.

You can see the standard attributes:


or the same thing as claims rules (if this is a more familiar format for you).



And it gives you a bit of extra context.

Enjoy!

Friday, September 29, 2017

Auth0 : The connection was disabled

I was trying to set up an enterprise connection in Auth0 to my ADFS instance.

When I tried the "Test" button on the enterprise connection, I got:

"description": "the connection was disabled"

WTF?

Eventually I realised that this is because I do not have a client that is configured under "Connections" to use that enterprise connection.

Assigned this to a client and viola! all is well.

That error message needs improvement!

Enjoy!

Monday, September 25, 2017

ADFS : Pre-populating the user on the login screen

This question often comes up and I came across a site that does this.

Note this is with ADFS 4.0 (Server 2016).

The URL is:

GET https://my-adfs.cloudapp.net/adfs/ls/?login_hint=myuser%40dev.local
&wctx=Yue...BfN-A7v
&wtrealm=some_realm
&wa=wsignin1.0
...
etc

The ADFS login screen then looks like:


Enjoy!

Wednesday, September 13, 2017

ADFS : RP default token lifetime

This question keeps coming up.

The default value for TokenLifetime on a RP trust is 0. But what value is 0?

As usual, a heap of garbage via Google.

60 minutes, 300 minutes, 600 minutes, 10 hours ...

Using ADFS 4.0 and looking at a SAML RP, we get:


Conditions        NotBefore="2017-09-12T19:24:01.817Z"
                   NotOnOrAfter="2017-09-12T20:24:01.817Z"


So the correct answer is 1 hour = 60 minutes.

Note: Don't confuse this with the ADFS wide WebSSOLifetime. This is a server wide timeout parameter.

The default value for that = 8 hours = 480 minutes.

Enjoy!

Friday, September 01, 2017

Azure B2C : Adding Azure Active Directory (AAD) via custom policies

As I write these are in preview.

The documentation is here.

The AAD guide is here.

And the obligatory warning:

"Custom policies are designed primarily for identity pros who need to address complex scenarios. For most scenarios, we recommend that you use Azure Active Directory B2C built-in policies. Built-in policies are easier to set up for your configuration. You can use built-in and custom policies in the same Azure Active Directory B2C tenant. To learn more, see the overview of custom policies."

and again:

"Custom policy editing is not for everyone. The learning curve is demanding, the startup time is longer, and future changes to custom policies will require similar expertise to maintain. Built-in policies should be carefully considered first for your scenario before using custom policies."

I don't necessarily agree with this and am somewhat puzzled as to why they push this so hard.

The aim should be to encourage people to have a crack at it and learn something rather than scare them away.

I would spend some time reading through the getting started guide and get an overview of how the XML files work, how to upload them etc.

The big drawback about all of these guides is that they publish snippets of XML and it's always hard to figure out the context i.e. where they go in the document and how they relate to the other sections.

So I decided to publish all five files as gists (suitably redacted!).

I have three add-ons:
  • Facebook - from the default policy 
  • ADFS - added but doesn't work because of the self-signed certificate
  • AAD - which works
Note that this was for a PoC where I was just looking at authentication. I haven't looked at the claims passed etc.

Also I did not have an actual application. I just tested using the "Run Now" button.

My B2C page looks like:


Also note that I added Application Insights which I strongly recommend for debugging (in SignUpOrSigninWithAAD.xml).

Enjoy!

Azure B2C : Using B2C as an Identity router

B2C has been touted as the successor to ACS but I've always struggled to understand this.

With the advent of custom policies, this is now doable.

Essentially, forget about using B2C as it's supposed to be used i.e. external customer registration and self service password reset.

Just use it as a hub / identity router.

You can configure a policy e.g. sign up / sign in to handle any number of IDP as long as they support OpenID Connect or SAML 2.0. Each of these is configured via XML in the custom policies. Each has a login button on the landing page.

You could almost think of B2C as acting as a pseudo Home Realm Discovery page.

B2C can be branded so it could have the same look and feel as the rest of the corporate pages.


e.g. this is my PoC page.

It allows you to sign up with Facebook, ADFS or Azure AD.

Downstream B2C only allows OpenID Connect so the path would be e.g.

Application --> OIDC --> B2C --> OIDC  --> Facebook
                                                  --> SAML --> ADFS
                                                  --> OIDC  --> AAD

Or if you wanted lots of social providers, you could go OIDC to something like Auth0 and then use their large array of social providers.

So it's pretty much ACS++!

WS-Fed support is on the way.

Enjoy!

Thursday, August 31, 2017

Azure B2C : Tracking errors

I've posted before on how crucial Application Insights is to troubleshooting B2C custom policies.

Once you had got it setup, you need to wait about 5 minutes and then run something like:

traces

This displays all the data. You can sort by clicking on the "timestamp column".


 Expand the "message" section.


Expand the "FatalException" section.


and you'll see the error.

If you want to filter the errors, try something like:

traces
| where severityLevel > 0 and message contains "Exception"


Enjoy!

Wednesday, August 30, 2017

Azure B2C : Custom policies with ADFS

Azure AD B2C has custom policies in preview that enable you to add extra IDP / social to B2C via an "Identity Framework" that is a collection of XML files that document standards, orchestrations, user journeys etc.

Using this you can add providers that use either SAML or OpenID Connect.

So ADFS 4.0 was a good candidate for OIDC.

As per my SO question:

"I have ADFS 4.0 on an Azure VM and am trying to add ADFS as a provider to my Azure AD B2C tenant.

I have set up all the custom policies.

I am using OpenID Connect as the protocol.

My ADFS SSL certificate is self-signed and I have certificate rollover for the encryption and signing certificates.

The error I get in Application Insights is: 

Exception {"Kind":"Handled","HResult":"80131501",
"Message":"The remote certificate is invalid according to the validation procedure.","Data":{}} Kind Handled HResult 80131501 
Message The remote certificate is invalid according to the validation procedure.

I battled for hours trying to get this to work before asking the question.

Turns out:

"Your ADFS needs to have a valid SSL cert signed by the standard Certificate Authorities in order for Azure AD B2C to communicate with it".

So no self-signed. As this was a proof on concept, I'm not intending to go out and buy a certificate. This is further complicated by the fact that you can't buy a certificate for xxx.cloudapp.net!

Tip - to debug the custom policies you need Application Insights. Without that, your chances of solving the issues are effectively zero.

Enjoy!

Tuesday, August 29, 2017

ADFS : Issue with updating the SSL certificate

Using ADFS 4.0 and updating the SSL certificate.

This is on an Azure VM and I was accessing it remotely.

Ran the normal commands:

Set-AdfsCertificate -CertificateType Service-Communications -Thumbprint thumbprint

Set-AdfsSslCertificate -Thumbprint thumbprint

Error :

Set-AdfsSslCertificate -Thumbprint 24f...b35

Set-AdfsSslCertificate : PS0319: Validation task 'Test-_InternalAdfsSslCertificate' on AD FS server 'localhost' failed with error 'Connecting to remote server localhost failed with the following error message : The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. Consult the logs and documentation for the WS-Management service running on the destination, most commonly IIS or WinRM. If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: "winrm quickconfig".

As per the message, running:

winrm qc

and then re-running the command fixed the problem.

Enjoy!

Monday, August 21, 2017

ADFS : SAML SLO

SLO = Single log out.

The way this is supposed to work is described in the SAML specification.


For one customer, they had 6 RP and one of then didn't do SLO properly and didn't return a logout response.

This stopped all the others getting called, clearing cookies etc. so it was a "half a logout" solution.

Eventually, I simply removed the SLO endpoint for the RP via the ADFS wizard.

Everything then worked correctly.

The RP with issues was still logged in and if you knew the URL you could still continue but at least the bigger picture worked.

I should point out that this RP was the only one that did not use an industry standard SAML stack and had instead tried to roll their own. You may draw your own conclusions :-)

Enjoy!

Friday, August 04, 2017

Identity : Breached passwords

Troy Hunt has an interesting feature over on Introducing 306 million freely downloadable pwned passwords.

All the passwords that have been in a breach are searchable.

If there is a hit, it's either out there or someone else make the same password selection as you did (decreasing security).

But there's also a section on how to utilise this for Identity Management.

When you ask the user to select a password, check it against this list and reject if there is a hit.

Azure AD uses a similar approach where they reject all "common" passwords.

Enjoy!

Friday, July 28, 2017

ASP.NET : Misused header name. Make sure request headers are used

The full error is:

Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

This is when I use HttpClient with .NET 4.5 and try something like:

client.DefaultRequestHeaders.Add("Contact-Type", ...);

And so began a long and painful journey to figure out to to fix this because the external web API wouldn't work without "Content-Type" as a header.

There is so much garbage out there :-(

After some research, Content-Type is part of Content - the name pretty much implies that - so use HttpContent.

using (var client = new HttpClient())
{
  // Adding contentType to client as header gives "Misused header name. Make   sure request headers are used 
with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent 
  // objects."

  client.DefaultRequestHeaders.Add("Authorization", "Bearer abc...123");

  HttpResponseMessage response;
  
  // Construct an HttpContent from the data
  HttpContent hc = new StringContent(data);
  hc.Headers.ContentType = new MediaTypeHeaderValue("application/json");
  response = client.PostAsync(baseAddress, hc).Result;

  response.EnsureSuccessStatusCode();
               
  var result = response.Content.ReadAsStringAsync().Result;
}


Enjoy!

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.

Enjoy!

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:

bcompare 

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.

Enjoy!

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.

Then:

 $ git pull

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

Just "Reload all".

Job done!

Enjoy!

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 :-)

Enjoy!


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.

Enjoy!

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:



Enjoy!

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 = "";
}

Enjoy!

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 xxx.cloudapp.net. The DC shows as xxx.dev.local 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.

WTF?

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 "xxx.cloudapp.net" 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.

Enjoy!

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.

//debugger;

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.

Enjoy!

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.

debugger;

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.

Enjoy!

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.

Enjoy!

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.

Enjoy!

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

or

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

or:

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

HRD

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

Another example here.

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

Theme

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

Bonus

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

Enjoy!

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>
<div id="footerPlaceholder"></div>
</div>
<div id="footer">
<div id="footerLinks" class="floatReverse">
<div><span id="copyright">© 2016 Microsoft</span></div>
</div>

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

So:

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

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") ) {

Enjoy!

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 == "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "S-1-5-21-965288371-...-1106", Issuer == "AD AUTHORITY"]
 => issue(Type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", 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!

Enjoy!

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

Enjoy!

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:

get-AdfsNativeClientApplication

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


get-AdfsServerApplication

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


get-AdfsWebApiApplication

Name                                 : AppA  - Web API
Identifier                           : {https://localhost:44666/TodoListService}
AccessControlPolicyName              : Permit everyone
AccessControlPolicyParameters        :
AdditionalAuthenticationRules        :
AllowedAuthenticationClassReferences : {}
AllowedClientTypes                   : Public, Confidential
ApplicationGroupIdentifier           : AppA
ApplicationGroupId                   : 0e...cd
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
                                       IssuanceAuthorizationRules:
                                       {
                                         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

Enjoy!

Friday, May 26, 2017

Misc : Top search result in Google

Now this I like :-)


Top search result - awesome.

Enjoy!

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 == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
 => issue(type = "http://adatum.com/data1", 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 == "http://adatum.com/data1holder", Issuer == "AD AUTHORITY"]
 => issue(type = "http://adatum.com/data1", Value = RegExReplace(c.Value,"'","");

which parses no problem.

Enjoy!

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"} 

Enjoy!

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:

https://adfs-a/adfs/ls/?SAMLRequest=

And then selecting ADFS B via HRD gives:

https://adfs-b/adfs/ls/?SAMLRequest=...

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:

IssueInstant="2017-05-11T01:27:25.56Z"

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!

Enjoy!

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
iss     https://xxx.cloudapp.net/adfs
iat     1494378378
exp     1494381978
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant     1494378377
nonce     6362....NmVl
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier     0JrA...H7k=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn     user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name     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
iss     https://xxx.cloudapp.net/adfs
iat     1494379409
exp     1494383009
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant     1494379408
nonce     6362...NTk2
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier     PRUA...50U=
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name     DEV\user1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn     user1@dev.local
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname     User1
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname     Test
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress     user1@company.com
http://schemas.xmlsoap.org/claims/CommonName     User1 Test
apptype     Public
appid     c906...3ab4
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod     urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
ver     1.0
http://schemas.microsoft.com/identity/claims/scope     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
{
    [HttpPost]
    [Route("login")]
    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}
                                    &state={state}");
        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."

Enjoy!