Wednesday, August 24, 2016

Postman : Using Postman to get "Userinfo" on Azure AD

I got this idea from v2.0 Protocols - OAuth 2.0 Authorization Code Flow that has "Run in Postman" buttons that load Postman collections.

So I made my own which you can find in this gist.

This uses OpenID Connect / OAuth 2.0.

It has three steps.

The first you have to customise for your clientID etc. and then run in a browser.

Then copy / paste the code from the reply into the second, customise for your clientID etc. and click "Run".

This returns an access token, an ID token and a refresh token.

(You can see what's in them by copy / paste the access / ID token into jwt.io).

The code inside the collection automatically sets up the token for the third step so all you have to do is press "Run".

You will see the full "Userinfo".

There are full instructions in the collection.

Enjoy!

Thursday, August 18, 2016

OAuth2 - Testing OpenID Connect / OAuth 2.0

Utilities to help you develop using OpenID Connect and OAuth 2.0 are always useful so I've listed out the ones I know about.

Postman - Really useful to for API testing. Requires Chrome.

v2.0 Protocols - OAuth 2.0 Authorization Code Flow - Article that has links to Postman collection to try this out step by step. Azure AD centric.

Inspecting the JWT token - from Auth0.

Outlook OAuth Sandbox - for testing Outlook.Office.com API.

OpenID Connect Playground - from Auth0 - for developers to test and work with OpenID Connect calls.

Demo. instance of IdentityServer - has full stack OIDC / OAuth2 support.

Tying the two together - hooking the playground up with IdentityServer.

Enjoy!

Monday, August 15, 2016

OAuth2 : Verifying the Azure AD JWT signature

Was having a look at Azure AD and JWT tokens and was wondering how the signature was calculated?

I use this useful utility from Auth0 to decode the tokens.

So I paste either the access or identity token into the "Encoded" box and set the "Algorithm" drop down to "RS256" (as below in bold). Problem is the signature is invalid. So how do I verify it.?

Looking at a typical token header,

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "MnC_VZcATfM5pOYiJHMba9goEKY"
}


we see the algorithm and the reference to the public key.

In Azure AD, we can get the OAuth 2.0 details from the discovery endpoint viz.



and that gives us:

"authorization_endpoint": "https://login.microsoftonline.com/common/oauth2/
    authorize",
    "token_endpoint": "https://login.microsoftonline.com/common/oauth2/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_post",
        "private_key_jwt"
    ],
    "jwks_uri": "https://login.microsoftonline.com/common/discovery/keys",
    "response_modes_supported": [
        "query",
        "fragment",
        "form_post"
    ],
... 

Now if we go to the key endpoint (bold above), we see:

"keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "MnC_VZcATfM5pOYiJHMba9goEKY",
            "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY",
            "n": "vIqz-4-ER_vNWLON9yv8hIYV737...NOhfXgelixLUQ",
            "e": "AQAB",
            "x5c": [
                "MIIC4jCCAc...H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ=="
            ]
        },
... 

There is  more than one of these sets. You need the one whose "kid" matches the "kid" in the JWT header.

The "x5c" is the public key. Copy the entire string e.g. from "MIIC" to "JQ==" in the above to Notepad.

Now add:

"-----BEGIN CERTIFICATE-----"

at the beginning and add:

"-----END CERTIFICATE-----"

at the end.

You should have something like:

-----BEGIN CERTIFICATE-----
MIIC4jCCAc
...
H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==
-----END CERTIFICATE-----

Now paste that into the "Verify Signature" box and you'll see the "Signature Verified" message at the bottom in blue.

Enjoy!

Friday, August 12, 2016

ADFS : Getting the IIS logs and event logs for ADFS 3.0

This is for ADFS on Server 2012 R2 and above since ADFS in these versions no longer runs on IIS but runs directly on HTTP.SYS.

So there was a question over on the ADFS forum around looking at the IIS logs and @Pierre replied:

Every access generates logs as long as you enabled the audit. So the information is still there, just in a different format.

Just an example:
Get-WinEvent -FilterHashtable @{LogName="Security";ID=403} | 
%{ $_.Properties.Value -join " " }

And here is the example of output:

00000000-0000-0000-9758-0080000000b3 2016-08-11 15:32:58 10.0.0.7 
  GET /adfs/Proxy/GetConfiguration - 443 10.0.0.6 - 0 - - - False -
00000000-0000-0000-662e-0080000000e1 2016-08-11 15:32:36 10.0.0.7 
  GET /adfs/Proxy/webapplicationproxy/store ?api-version=1 443 10.0.0.6 - 0 - - -  
  False - 00000000-0000-0000-652e-0080000000e1 2016-08-11 15:32:06 10.0.0.7 
  GET /adfs/Proxy/webapplication 

but that got me wondering about "Get-WinEvent". What else can you do with it?

What logs are there?

PS C:\>
PS C:\> Get-WinEvent -ListLog *

LogMode   MaximumSizeInBytes RecordCount LogName
-------   ------------------ ----------- -------
Circular             1052672         180 Active Directory Web Services
Circular            20971520       20503 Application
Circular            15532032         147 DFS Replication
Circular             1052672        1825 Directory Service
Circular           104857600         237 DNS Server
Circular            20971520           0 HardwareEvents
Circular             1052672           0 Internet Explorer
Circular            20971520           0 Key Management Service
Circular           134217728      198680 Security
Circular            20971520       30088 System
Circular             1052672           0 Windows Azure
Circular            15728640       15575 Windows PowerShell
Circular            52428800        1830 AD FS/Admin
Circular            52428800           0 DRS/Admin

... 

Hang ten! There's an ADFS log!

PS C:\> Get-WinEvent -LogName "AD FS/Admin"

   ProviderName: AD FS

TimeCreated                     Id LevelDisplayName Message
-----------                     -- ---------------- -------
8/11/2016 10:45:47 PM          415 Warning          The SSL certificate does not contain all UPN suffix values that ...
8/11/2016 10:13:19 PM          364 Error            Encountered error during federation passive request. ...
8/11/2016 10:11:57 PM          364 Error            Encountered error during federation passive request. ...
8/11/2016 10:09:22 PM          364 Error            Encountered error during federation passive request. ...
8/11/2016 9:53:13 PM           364 Error            Encountered error during federation passive request. ...


It goes on forever and most of the time I am only interested in the "Message" column (say the top 10) and I don't want it truncated. This leads to:

PS C:\> Get-WinEvent -LogName "AD FS/Admin" | Select Message -First 10 | out-string -Width 600

Message

-------

The SSL certificate does not contain all UPN suffix values that exist in the enterprise.  Users with UPN suffix values not represented in the certificate will not be able to Workplace-Join their devices.  For more information, see http:// go.microsoft.com/fwlink/?LinkId=311954.

Encountered error during federation passive request. ...

Encountered error during federation passive request. ...


...

This is the same information that you get in the Event log in ADFS.

Enjoy!

Thursday, August 11, 2016

ADFS : Installing the on-premises MFA adapter

I've been doing a PoC of this for a customer and finally got this to work.

It's only certified for 2012 R2 so no joy yet for Server 2016.

Firstly you need an Azure subscription so you can download the multi-factor authentication (MFA) module from the Azure AD section.

The subscription itself is free but you have to pay for MFA - either a block amount per user per month or per authentication.

This is a very good blog series on MFA.

Note that I used MFA server version 7.0.2.1 and some of the screen shots in the article no longer apply.

And the blog series is continued with an explanation of methods.

One question I was asked was why would you install this given that Azure AD already has MFA?

One reason is that ADFS has SaaS applications federated directly with it so the MFA needs to be on the ADFS server.

Once you've installed it, you need to add the ADFS adaptor under the ADFS icon.

During the installation it asks you to confirm that KB 2919355 has been installed. This dates from April 2014 and I regularly do "Windows Update" so was pretty sure that it was installed but didn't know how to check.

Quick chat with Mr. Google and via PowerShell:

get-hotfix | where HotfixID -eq 'KB2919355' 

Source     Description   HotFixID      InstalledBy          InstalledOn
------        -----------      --------             -----------             -----------
ADFS     Update          KB2919355    ADFS\admin       2/13/2015 12:00:00 AM


Once it's all installed, I still couldn't see the check box in the ADFS MFA view.

Another quick chat with Mr. Google and via PowerShell:

PS C:\Program Files\Multi-Factor Authentication Server> .\Register-MultiFactorAuthenticationAdfsAdapter.ps1
WARNING: PS0114: The authentication provider was successfully registered with the policy store.  To enable this provider, you must restart the AD FS Windows Service on each server in the farm.


and all was well.

This was described here.

If you want to install the user portal, note that this requires IIS. The installation wizard is pretty good. If something is missing, it tells you and you can then re-run after it has been installed.

Remember to bind https on IIS if you want SSL access.

Before you start, I recommend you read the articles above to give you clear direction and things will be a lot easier.

Have to say that the MFA server is a lot more complex than I envisaged. It probably has more  configuration options than ADFS! One of the reasons for this is that it handles far more than ADFS (or any other adaptor that you want to add into the mix). It also handles IWA, RADIUS, LDAP, applications directly on IIS etc.

In terms of the generated claims rules, you can see them via:

"Get-AdfsRelyingPartyTrust -Name "RP Name" 

under "AdditionalAuthenticationRules" as described here.

I configured the RP for MFA for both Intranet and Extranet and the claims rules were:

c:[Type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork",
Value == "false"]
 => issue(Type =
"http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",
Value = "http://schemas.microsoft.com/claims/multipleauthn");

c:[Type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork",
Value == "true"]
 => issue(Type =
"http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",
Value = "http://schemas.microsoft.com/claims/multipleauthn");


These claims are described here (Part 1) and here (Part 2).

Enjoy!

Tuesday, August 09, 2016

ADFS : Equality in claims rules

There have been a number of posts over on the ADFS forum around comparing claims values.

You can easily compare that a claim has a certain value via:

 c:[type == "http://company.com/department", value == "marketing"]
 =>issue(Type = “http://someclaim”, Value = "somevalue");

but what if you have two claims and you want to test that they both have the same value (i.e. are equal)?

There is no OOTB claims rules for this.

When I answered a post with this, @Pierre answered that there are some clever workarounds and gave some examples so I'm writing this up.

Imagine the following:

During on-boarding of users into AD, some people have had the same number entered into their phone and mobile attributes. Our application wants to catch this and tell the user to contact the help desk and get this fixed. 

The rule for telephone number and mobile is:

c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
 => issue(store = "Active Directory", types = ("http://claim/phone", "http://claim/mobile"), query = ";telephoneNumber,mobile;{0}", param = c.Value);

So now we need to check that the values of "http://claim/phone" and "http://claim/mobile" are equal.

The clever trick is to do a regex replace across both i.e. a custom rule:

c1:[Type == "http://claim/phone"]
 && c2:[Type == "http://claim/mobile"]
 => issue(Type = "http://claim/ContactNumber", Value = RegExReplace(c1.Value, c2.Value, "Error"));

This rule will try and match the pattern in the first value with the pattern in the second value. If they match (i.e. are equal), it will issue a new claim type "http://claim/ContactNumber" with a value of "Error".

The application can then check the claim for the value of "Error" and then inform the user that the contact details need to be updated.


Here the values are equal and "ContactNumber" has the value of "Error".


Here the values are not equal so "ContactNumber" takes the value of the first claim.

This trick is not perfect e.g.


Here the one number is a subset of the other so the rest of the string is appended after "Error". However, this is easy to get around by checking that the claim contains the value "Error" rather than is equal to it. If that's the case, then the numbers are not equal.

Enjoy!

Thursday, July 21, 2016

ADFS : Authentication with .NET Core

This is for Active Directory Federation Services (ADFS / "AD FS") on Server 2016 Technical Preview 5.

The client is built on .NET Core along the lines of AAD : Authentication with .NET Core.

So I built the .NET Core sample as per the post above and then changed the code as per this gist.

I configured ADFS by adding an application group and picking "Standalone Application" and "Server application or Website".

Then:


The Client ID needs to be cut and pasted into appsettings.json as does the secret key that you generate as part of the process.

Note that you have to add "/signin-oidc" to the Redirect URL. I have no idea where this comes from. It must be added somewhere as part of the middleware.

Then I got this error:

Microsoft.IdentityServer.Web.Protocols.OAuth.Exceptions.OAuthClientCredentialAuthenticationException: MSIS9267: No Client credentials found in the request. Client '2c...b7' is configured as a confidential client.

Zero documentation on this and much frustration but then in desperation I added the ClientSecret in the code and suddenly it worked!

I did not do this when I did the other examples e.g. ADFS - Web App and Web API on Server 2016 TP4 ADFS 4.0 and they worked so completely baffled!

Enjoy!

AAD : Authentication with .NET Core

This is for Azure Active Directory (AD) with .NET Core 1.0 and VS 2015 Update 3.

Download the .NET Core files for Windows from Core#windows.

We want to write a web application using .NET Core that authenticates with Azure AD.


Then we want to select a web application and we also "Change Authentication" to "Work and School Accounts" and "Cloud - Single Organization".

Note there is no "on-premises" option using ADFS.


You have to enter your Azure tenant and authenticate and then the project is created together with  an application in Azure AD.

The project structure looks much the same as the normal ASP.NET project created this way wrt. the Controller and Views.

Also the authentication is via OpenID Connect as you would expect.

One difference is that the configurable options are no longer in web.config but rather in appsettings.json.

Also, the tried and tested way we have used for years i.e.

ViewBag.ClaimsIdentity = Thread.CurrentPrincipal.Identity;

no longer compiles. There's tons of discussion around this but you can find the code I used in this gist.

The claims then display in the "Contact" tab in the normal way.

Enjoy!

Monday, July 18, 2016

ADFS : Capacity Planning

This is for Active Directory Federation Services (ADFS / AD FS).

There used to be a useful capacity planning spreadsheet available which disappeared for a while.

It's now back and there's one for Server 2016 (ADFS 4.0) as well .

AD FS Capacity Planning Spreadsheet

AD FS Windows Server 2016 Capacity Planning

Enjoy!

Monday, July 11, 2016

Speaking : Getting organised

I been doing a few presentations lately and doing some co-presenting. Some of this with people with very little speaking experience.

Two tips:

Have a work sheet

Write down all the screens and utilities that you will use and use this to check that all are open and logged in before the presentation starts.

e.g.
  • I'm using Azure so ensure I'm logged into the classic and new portal.
  • I'm using Zoomit so ensure it's opened
  • I'm using VS so ensure it's opened and the correct project is loaded
  • I'm using Notepad++ so ensure it's opened
The audience doesn't want to watch you logging into Azure or trying to find that icon.

Write down what you will demo.

When you are standing up there you will be nervous and you will forget to demo. some stuff.

So write down what you will demo., the order, any pertinent points etc.

Print this out in larger type so you can read it from some distance. Have blank lines between the points.

Good luck.

Enjoy!

Friday, July 08, 2016

AAD : Migrating JWT to claims.

Both AAD and ADFS 4.0 (Server 2016) have support for JWT - normally from OpenID Connect / OAuth but also from WS-Fed (via ADFS).

On the RP side, you get a claims principal that contains some of the information that's in the JWT but it's hard to figure out the relationship.

You can see some of it via Get-ADFSClaimDescription with PowerShell.

e.g.

ClaimType  : http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
IsAccepted : True
IsOffered  : True
IsRequired : False
Name       : Name ID
ShortName  : sub
Notes      : The SAML name identifier of the user


You can see more info. here.

So "sub" in the JWT contains the NameID and this then is transformed to a NameID claim by the middleware.

Enjoy!

AAD : Easy Auth

This is for Azure Active Directory (AD).

"Easy Auth" is what the Authentication / Authorization feature in App Services is called.

The idea behind this is that you can have an Azure website and using this feature you can add authentication to it with a few clicks,

You do not have to use NuGet OWIN packages or anything similar. No code is involved.

Essentially it uses an "agent" feature as per Architecture of Azure App Service Authentication / Authorization. There's a native IIS module that sits outside of your application and all requests are directed through this. If not authenticated then you get redirected to the Azure AD login screen. (Recall that Azure web sites actually run on IIS).

You don't not have to add an application to Azure AD. It's all done for you.

So how does it work?

I used VS 2013.

New Project / ASP.NET Web Application. Ensure MVC is checked.

Click the "Change Authentication" button and select the "No Authentication" radio button.

So now we have a bare-bones project with no built-in authentication.

Now deploy to Azure (there are tons of posts around this so I'll skip the details).

At this point you have a web site - e.g. https://easyauth.azurewebsites.net/.

There is no entry for this in Azure AD.

In the new portal, click on "App Services", then the app service (the web site) and then "Settings".


Select "Authentication / Authorization" as above.


Turn on Authentication. choose "Login with Azure Active Directory" from the drop-down and then click the "Azure Active Directory" authentication provider.



Then select the "Express" setting.

Save out.

Now if you go to the old (classic) portal under "Azure Active Directory" you'll see that you have a new application.

You should be able to navigate to your website and the first thing it should do is ask you to authenticate.

Except - it doesn't!

Back to Mr. Google and I found this: How to configure your App Service application to use Azure Active Directory login.

As per the section: "(Alternative method) Manually configure Azure Active Directory with advanced settings", you need to click the Advanced section (not the Express) and add:

/.auth/login/aad/callback

to the "Reply URL"  in Azure Active Directory. Don't worry about all the stuff about actually creating the application - that's already been done.

And then, lo and behold, it worked!

Enjoy!