Wednesday, August 31, 2016

Misc : Microsoft Open Specifications

Microsoft publishes a list of open specifications that enable inter-operability over here.

These include a number of specifications that describe ADFS and WAP (Web Application Proxy).

These include:
The MS-ADFSPIP document describes the interface between ADFS and WAP.

There are tons of questions on the forum around "Do I have to use WAP as the proxy. Can I use any reverse proxy or a load balancer or F5 or Netscaler etc. ?"

You can as long as the proxy you want to use implements the standards.

Good luck with getting your vendor to confirm that they do and to demonstrate this fact.

In addition, your vendor may also need to comply with the rules around

[MS-OFBA]: Office Forms Based Authentication Protocol

if e.g. the request is 
  • from a Microsoft Office application that relies on the Office Forms Based Authentication (OFBA) Protocol
  • from non-Microsoft-Office clients accessing services that implement the OFBA protocol [MS-OFBA] that rely on ADFS for authentication
It also needs to ensure that the correct claims are set e.g.

http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork

which is "True" is accessing ADFS directly or "False" if accessing via WAP.

My advice : Just use the WAP - much less stressful - :-).

Enjoy! 

Postman : Azure AD and Implicit Flow

I've been playing around with this and thought it would be worthwhile to document  the journey.

I have a web application in Azure AD and this application calls a web API.

The web application has permission to call the web API.

I want to use the implicit grant.

This is not supported OOTB :

AADSTS70005: response_type 'token' is not supported for the application

You need to update the manifest as per ADAL JS - response_type=“token” is not supported.

After sorting that out, my first call was:

https://login.microsoftonline.com/[tenant id]/oauth2/authorize?client_id=[client id]&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F

This resulted in:

http://localhost/myapp/#error=invalid_resource&error_description=AADSTS50001%3a+Resource+identifier+is+not+provided...

OK - so I need a "resource" parameter - let's make it the same as the response type.

https://login.microsoftonline.com/[tenant id]/oauth2/authorize?client_id=[client id]&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&resource=http%3A%2F%2Flocalhost%2Fmyapp%2F 

This resulted in:

http://localhost/myapp/#error=invalid_resource&error_description=AADSTS50001%3a+The+application+named+http%3a%2f%2flocalhost%2fmyapp%2f+was+not+found+in+the+tenant+named+[tenant id]/++This+can+happen+if+the+application+has+not+been+installed+by+the+administrator+of+the
+tenant+or+consented+to+by+any+user+in+the+tenant.
++You+might+have+sent+your+authentication+request+to+the+wrong+tenant....

So maybe I need the "APP ID URI" of the web application?

https://login.microsoftonline.com/[tenant id]/oauth2/authorize?client_id=[client id]&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&resource=https://www.getpostman.com/oauth2/callback

This resulted in:

http://localhost/myapp/#error=invalid_request&error_description=AADSTS90027%3a+The+client+%[client id]%27+and+resource+%27https%3a%2f%2fwww.getpostman.com%2foauth2%2fcallback%27+identify+the+same+application...

Aha - so maybe I need the "APP ID URI" of the web API?

https://login.microsoftonline.com/[tenant id]/oauth2/authorize?client_id=[client id]&response_type=token&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&resource=https://xxx.onmicrosoft.com/xxxService 

This resulted in:

http://localhost/myapp/#access_token=eyJ...Cpw&token_type=Bearer&expires_in=3599&session_state=6e7...203 

Bingo!

I've obviously over-egged this a bit but you can see how you can work through the issues and figure out what's wrong based on the error messages.

The errors you get back from Azure AD  are an order of magnitude better than they used to be :-)

Note that the implicit grant does not return a refresh token because the browser has no means of keeping it private.

Enjoy!

Friday, August 26, 2016

Misc : Creating self-signed certificates

I normally recommend SelfSSL7 but I was looking at Let's Encrypt which generates free CA SSL certificates.

This is in the context of Windows, specifically IIS SSL or securing ADFS.

To do this you need a client and one of the browser clients on the list was ZeroSSL.

If you click the Tools button, you'll see a "Self-Signed Certificate Generator".


There's a private key and a certificate.

Save the two files somewhere. If you save the certificate as a .cer file, you can double click on it and get the certificate wizard. You'll note that it has no private key which makes it useless for SSL. The certificate is valid for a year.

Now use OpenSSL to run:

openssl pkcs12 -export -out c:\xxx\adfs.pfx -inkey c:\xxx\zerossl.prv -in c:\xxx\zerossl.cer
Loading 'screen' into random state - done
Enter Export Password:
Verifying - Enter Export Password:

You'll need to pick a password.

Now if you double click on the .pfx file, the Import wizard will guide you as to installing it into the certificate store. You'll need to type in the password.

If you look at the "Enhanced Key Usage", you'll see it covers a lot of cases which is cool.


Enjoy!

Postman : Using cURL to send OpenID Connect / OAuth to Azure AD / ADFS

"cURL is a computer software project providing a library and command-line tool for transferring data using various protocols".

I discovered that Postman allows you to generate these commands.

There are Postman collections for Azure AD / ADFS in gists as per Postman : Using Postman to get "Userinfo" on ADFS and the link inside the post.

So if you load the collection into Postman, click on "Generate Code" (top right) and then in the dropdown, select "cURL".

e.g.

curl -k -X POST -H "Content-Type: application/x-
www-form-urlencoded" -H "Cache-Control: no-cache" -H "Postman-Token: 0303470d-5a
3b-bdea-9da3-9ab3e12f66ce" -d "client_id=37f...0b0e&scope=openid&redirect_uri=https://localhost:1234&grant_type=authorization_code&client_secret=Rel...G9&code=Nj...WWg" "https://my adfs/adfs/oauth2/token"


{"access_token":"eyJ...GFQ",

"token_type":"bearer","expires_in":3600,
"resource":"urn:microsoft:userinfo","
refresh_token":"TT9...u21w",
"refresh_token_expires_in":25768,
"scope":"openid","id_token":"eyJ...Ywg"}


If you use self-signed certificates you need to use the "-k" option.

Enjoy!

Thursday, August 25, 2016

Postman : Using Postman to get "Userinfo" on ADFS

This follows on from Postman : Using Postman to get "Userinfo" on Azure AD.

There's a ton of stuff on Azure AD but very little on ADFS.

The gist is here.

Same instructions as the Azure AD article.

In terms of configuring ADFS, have a look at ADFS - Web App and Web API on Server 2016 TP4 ADFS 4.0 . You only need to do the Web App. part.

Enjoy!

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!