Tuesday, June 23, 2015

stackoverflow : Tag badges

Finally got my ADFS 2.0 tag badge:


Nice to get some recognition for all those hours helping and mentoring people.

Goes nicely with my others:

Enjoy!


Tuesday, June 16, 2015

Musings : How to reset a password

I've been involved in discussions with a number of customers recently around this very topic and (without exception) everyone has given me a flowchart that is flawed in some way.

People, this problem has already been solved. Why do you want to re-invent the wheel?

The inestimable Troy Hunt blogged on this a while back - Everything you ever wanted to know about building a secure password reset feature .

There's a flowchart at the end of the post which gives a good overview of the type of system that you should be designing.

I used this flowchart as a discussion point against the various "models" I was presented with and in each case, we discussed what was missing and the risks associated with that.

Ultimately, the customer makes the choice - but it's always worth having the discussion so that they are as informed as possible.

And always write down your recommendations so when things turn to custard and the finger points at you, you have something tangible to fall back on.

BTW - you could use most of this as a "Change Password" guide.

Enjoy!

ADFS : Playing around with Postman

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

My server is in an Azure VM.

I been playing around with Postman and the new OpenID Connect / OAuth2 features in ADFS.

But could not get it to work.

I contacted the Postman people who were very helpful and they advised me that the issue was that the SSL certificate on my VM is self-signed and hence causes the problem. There's no trust. Other than that, there appears to be no issues.

As this is a test VM and is going to be superseded when TP3 comes along, I don't really see any point in paying for a CA certificate.

But it would be nice to see it working :-)

Enjoy!

Monday, June 15, 2015

SAML : Federating with Azure Active Directory using SAML

This is SAML-P (the protocol) as opposed to SAML (the token).

There are a number of questions around about how you do this with AAD.

The official documentation SAML Protocol Reference is not very helpful but then I came across this:

Set up a trust between Shibboleth and Azure AD

The trick seems to be some "missing" documentation i.e. in:

Set-MsolDomainAuthentication   ,,, -PreferredAuthenticationProtocol SAMLP

Applying Mr. Google to "PreferredAuthenticationProtocol" bought me to:

Office 365 Supports SAML 2     and

Office 365 - Switching the federation protocol to SAML from WS-Federation

Note: Office 365 runs on top of AAD.

No idea why the documentation lacks this key element?

Enjoy!

Wednesday, June 10, 2015

MSDeploy : Registry editing has been disabled by your administrator

Was installing a web site in IIS on Windows Server 2012 R2 and using msdeploy.

Came up with:

Registry editing has been disabled by your administrator

Mr Google etc. and there's pages about using gpedit.msc and navigating to "User Config / Admin Templates / System" and finding the entry "Prevent access to registry editing tools" and making it "Not configured" or "Disabled" etc.

Actually, all you need to do is add:

set MSDeployPath=C:\Program Files\IIS\Microsoft Web Deploy V3\

to the DOS settings!

Obviously, you need to install msdeploy beforehand.

Enjoy!

Tuesday, June 09, 2015

ADFS : OpenID Connect and ADAL


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

My server is in an Azure VM.

It also uses the Active Directory Authentication Library (ADAL).

This is based on AzureADSamples/WebApp-WebAPI-OpenIDConnect-DotNet.

This is for Azure AD and is a web application that requires authentication with AAD and then calls a secure Web API that uses the current JSON token. The Web API is the Graph API that you use to get attributes from AAD.

So let's translate this to ADFS which now supports OpenID Connect. Somewhat annoying that all the samples are for Azure and none for ADFS, ADFS needs to feel the love :-).

<add key="todo:TodoListResourceid" value="https://myPC/TodoListService" />
<add key="todo:TodoListBaseAddress" value="https://myPC" />
<add key="ida:ClientId" value="OpenIDConnect1234" />
<add key="ida:AppKey" value="[Enter app key as obtained from Azure Portal, e.g. dYfh0H8iRU7FIBnPcYIil/Af6SSAwkxVhB0mA8DbzdQ=]" />
<add key="ida:Tenant" value="adfs.local.cloudapp.net" />
<!--<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />-->
<add key="ida:AADInstance" value="https://adfs.local.cloudapp.net/adfs" />
<add key="ida:PostLogoutRedirectUri" value="https://myPC/TodoListWebApp" />

Changes for the application web.config as above.

I changed StartUp.Auth.cs to the simpler:

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
     new OpenIdConnectAuthenticationOptions
     {
         ClientId = clientId,
         Authority = Authority,
         PostLogoutRedirectUri = postLogoutRedirectUri,
     });

The ADFS client OAuth configuration is:



 OK - so let's run up this puppy.

We get this error:

MSIS9221: Received invalid OAuth authorization request. The 'redirect_uri' parameter is missing or found empty. Public clients must send the redirect_uri parameter with valid redirect URI in the OAuth authorization request.

Looked through the OWIN code and couldn't see anywhere where this is passed.

So I manually added it to the URL at the end in the browser:

 &redirect_uri=https%3A%2F%2FmyPC1%2FTodoListWebApp

Then off to ADFS, authenticate and back we come.

Clearly, still some work to do with ADAL and TP2!

Enjoy!

Thursday, June 04, 2015

ADFS : Protecting Web API with OAuth2

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

It also uses the Active Directory Authentication Library (ADAL).

Vittorio has a good overview describing Server 2012 behaviour over at Securing a Web API with ADFS on WS2012 R2 Got Even Easier.

How does that look on Server 2016?

The VS solution is pretty much the same. I added a Windows Forms project to the solution which is my native client and the code for that (behind the button) is pretty much the same as  the above post. This calls a web API project in the same solution.

private async void button1_Click(object sender, EventArgs e)
{
            string authority = "https://ADFS_local.cloudapp.net/adfs";
            string resourceURI = "https://myPC/WinServTP2ADALWebApplication/";
            string clientID = "API1234";
            string clientReturnURI = "https://myPC/WinServTP2ADALWebApplication/";

            AuthenticationContext ac = new AuthenticationContext(authority, false);
            AuthenticationResult ar = ac.AcquireToken(resourceURI, clientID, new Uri(clientReturnURI));

            string authHeader = ar.CreateAuthorizationHeader();
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://myPC/WinServTP2ADALWebApplication/api/Values");
            request.Headers.TryAddWithoutValidation("Authorization", authHeader);
            HttpResponseMessage response = await client.SendAsync(request);
            string responseString = await response.Content.ReadAsStringAsync();
            MessageBox.Show(responseString);
}

ADFS_local is a Windows Server 2016 server running as an Azure VM.

Over to ADFS.

The OAuth client now has wizard support as opposed to the PowerShell in the post. It looks like:



You do this under "Clients".

We add the RP manually and notice the new OAuth option.


Notice also the extra menu items on the LHS for OAuth.


You can click on the "All Clients" to add more permissions (scopes). At the moment, it is just for "openid". You could also click on "Add" and select a specific client.

I added "email" and "profile".

(BTW, there is a separate "Scope Descriptions" item in the main menu).

Then choose your Access Policy on the next screen - in this case "Permit everyone". Review and you are done.

So looking at our RP, we have an identifier:


and some OAuth permissions - notice the new tab. That's pretty much it.




OK - so now we have the client and the web API RP all configured and ready to go.

Time to run up the client:


Click the button which calls the API. This is protected as per the configuration and so we get the ADFS logon screen. Authenticate with ADFS and:


We are done!

Enjoy!

Tuesday, June 02, 2015

ADFS : Authorisation code grant

This is continuing the series with Active Directory Federation Services / "AD FS" / ADFS with Windows Server 2016 (currently Technical Preview 2) and OAuth2.

Refer previous blog entry : ADFS : OpenID Connect.

As usual, we need a client. One of Thinktecture's products is Authorization Server.

(Note: AuthorizationServer is not really maintained anymore - read here for details).

I have all the code and I've played around with it previously so it made sense for me to use it. Also, there are some really nice clients which take you through the protocol step-by-step. (in samples/Flows/Clients).

So we are looking at the "Code Flow" sample.

Dominick always puts the constants in the Constants.cs class (part of the bigger project) so you need to change them as appropriate.

public static class AS
{
            public const string OAuth2TokenEndpoint = "https://adfs40.local/adfs/oauth2/token";
            public const string OAuth2AuthorizeEndpoint = "https://adfs40.local/adfs/oauth2/authorize";

            public const string IssuerName = "AS";
            public const string SigningKey = "1fTiS2clmPTUlNcpwYzd5i4AEFJ2DEsd8TcUsllmaKQ=";
}
 
 
Also notice the client_id:

public const string CodeClient = "codeclient";

Config the ADFS client as per:


Note the Client_id matches.

In CallbackController.cs:


public async Task<ActionResult> Postback()
{
            var client = new OAuth2Client(
                new Uri(Constants.AS.OAuth2TokenEndpoint),
                Constants.Clients.CodeClient,
                Constants.Clients.CodeClientSecret,
                OAuth2Client.ClientAuthenticationStyle.PostValues);

            var code = Request.QueryString["code"];

            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("client_id", Constants.Clients.CodeClient);

            var response = await client.RequestAuthorizationCodeAsync(
                code,
                Constants.Clients.CodeClientRedirectUrl,
                dict);

            return View("Postback", response);
}

I've had to modify the code somewhat. The original code used BasicAuthentication in the ClientAuthenticationStyle. That doesn't work and throws the ADFS error:

MSIS9265: Received invalid Client credentials. Found 'client_secret' query parameter in the request but the Client 'codeclient' is not configured to authenticate using client secret post.

No idea what this means. There is no way via the wizard to configure the secret. Via PowerShell, we see:

PS C:\> Get-AdfsClient -Name "Code Flow Client"

RedirectUri                          : {https://localhost:44303/callback}
Name                                 : Code Flow Client
Description                          :
ClientId                             : codeclient
BuiltIn                              : False
Enabled                              : True
ClientType                           : Public
ADUserPrincipalName                  :
ClientSecret                         :
JWTSigningCertificateRevocationCheck : None
JWTSigningKeys                       : {}
JWKSUri                              :


Notice the ClientSecret as a parameter. The problem is that the Add and Set do not accept this as a paramaeter?

So I changed the Style to PostValues. What this means via The OAuth 2.0 Authorization Framework is that:

client_id
         REQUIRED, if the client is not authenticating with the
         authorization server as described in Section 3.2.1.

we need to add "client_id" as a parameter. Hence, the "additional "Directory dict" code.

Security : Creating self-signed certificates

When you play around a lot with IIS and ADFS and SSL you need certificates and these will normally be self-signed.

You can create them through IIS but that's a pain (they are only valid for a year) and from Server 2012 R2 onwards, ADFS does not run on IIS so you are installing IIS simply for certificate creation?

I used to use the magic SelfSSL7 but that requires .NET 3.5 which isn't there by default on Server 2012 and upwards. (You could always install it as a role / feature).

So now I use "makecert".

Brock has blogged on this: makecert and creating ssl or signing certificates and also
Generating and using a certificate to authorise Azure Automation.

On Windows 8, you'll find makecert inside "Visual Studio Tools" / "Developer Command Prompt for VS2013". Remember to run as Admin.

We see:

C:\>makecert -?

Usage: MakeCert [ basic|extended options] [outputCertificateFile]
Basic Options
 -sk        Subject's key container name; To be created if not present
 -pe                 Mark generated private key as exportable
 -ss          Subject's certificate store name that stores the output
                     certificate
 -sr       Subject's certificate store location.
                        .  Default to 'CurrentUser'
 -#          Serial Number from 1 to 2^31-1.  Default to be unique
 -$       The signing authority of the certificate
                       
 -n        Certificate subject X500 name (eg: CN=Fred Dews)
 -?                  Return a list of basic options
 -!                  Return a list of extended options

C:\>makecert -!

Usage: MakeCert [ basic|extended options] [outputCertificateFile]
Extended Options
 -tbs          Certificate or CRL file to be signed
 -sc           Subject's certificate file
 -sv        Subject's PVK file; To be created if not present
 -ic           Issuer's certificate file
 -ik        Issuer's key container name
 -iv        Issuer's PVK file
 -is          Issuer's certificate store name.
 -ir       Issuer's certificate store location
                        .  Default to 'CurrentUser'
 -in           Issuer's certificate common name.(eg: Fred Dews)
 -a       The signature's digest algorithm.
                        .  Default to 'sha1'
 -ip       Issuer's CryptoAPI provider's name
 -iy           Issuer's CryptoAPI provider's type
 -sp       Subject's CryptoAPI provider's name
 -sy           Subject's CryptoAPI provider's type
 -iky       Issuer key type
                        >.
 -sky       Subject key type
                        >.
 -l            Link to the policy information (such as a URL)
 -cy       Certificate types
                       
 -b      Start of the validity period; default to now.
 -m          The number of months for the cert validity period
 -e      End of validity period; defaults to 2039
 -h          Max height of the tree below this cert
 -len        Generated Key Length (Bits)
                        Default to '2048' for 'RSA' and '512' for 'DSS'
 -r                  Create a self signed certificate
 -nscp               Include Netscape client auth extension
 -crl                Generate a CRL instead of a certificate
 -eku ]>  Comma separated enhanced key usage OIDs
 -?                  Return a list of basic options
 -!                  Return a list of extended options
so Brock's:

makecert -r -pe -n "CN=%1" -b 01/01/2015 -e 01/01/2020 -eku 1.3.6.1.5.5.7.3.1 
-sky exchange -a sha256 -len 2048 -ss my -sr localMachine
-r = Self-signed
-pe =  Mark generated private key as exportable
-n =  Certificate subject X500 name
-b =  Start of the validity period
-e =  End of validity period
-eku = Comma separated enhanced key usage OID
-sky = Subject key type
-a = The signature's digest algorithm
-len = Generated Key Length (Bits)
-ss  = Subject's certificate store name that stores the output certificate
-sr =  Subject's certificate store location

I've never used the -eku option,

As per the other link:

makecert.exe -sky exchange -r -n "CN=your certificate's name" -pe -a sha256 
-len 2048 -ss My "your certificate's name.cer"

Enjoy!

ADFS : Change / update password

I'm a regular contributor to the forum for

And there was this post:

SharePoint 2010 ADFS Password Expired


which contained an interesting snippet about changing your password via an ADFS screen.

This was for Server 2012 R2 and the functionality is enabled via a hotfix - kb/3035025.

It is in Server 2016 (Technical Preview 2) by default.

As per the article - under "Endpoints" - enable the "/adfs/portal/updatepassword/" endpoint under "Other" and reset the ADFS service via Services.

Then navigate to:

https://adfs .../adfs/portal/updatepassword

and you'll see:


Neat!

Enjoy!

Wednesday, May 27, 2015

ADFS : OpenID Connect

This is for ADFS vNext or ADFS 4.0 running on Windows Server 2016 (Technical Preview at the moment). (aka Active Directory Federation Services or "AD FS").

One of the new features is that support for OpenID Connect has been enabled.

I tried a number of clients (including Postman) and couldn't get any of them to work so I had to write my own.

Except that Dominick has already done most of the work - refer Writing an OpenID Connect Web Client from Scratch.

Fantastic - except that Github link doesn't work.

The revised one in the comments does but it is to the whole sample and it's not obvious which one to use. In fact, it's this:

Clients/MvcFormPostClient

Download all the samples because that's what the VS .sln file references.

It compiled no problem with VS 2013 on my PC.

Now if you've used Dominick's samples before, you'll know that they reference Dominick's environment so you have to ferret out all the references and change them,

But before we do that we have to find out what they are.

I run up the Technical Preview 2 in an Azure VM using my MSDN subscription. I then made it a forest of one by promoting the server to a DC so now I have AD, checked I have a certificate, created a service account and installed ADFS as a service exactly like Server 2012 R2.

(Note - after creating the Azure VM, you have to add endpoints for ports 80 and 443!).

In ADFS, click the Client tab and then "Add new OAuth Client". You need to set the settings as below because that's what the sample uses. "Name" can be anything.


Also of interest is the "Scope Descriptions":


These are the only scopes that are recognised by default.

You'll see the sample uses scopes "openid" and "email".

Back to VS,

In the Constants,cs file (part of the larger project), you need to edit for your endpoints:

public static class Constants
    {
        //public const string BaseAddress = "https://localhost:44333/core";
        public const string BaseAddress = "https://ADFS.local/adfs";
        
        //public const string AuthorizeEndpoint = BaseAddress + "/connect/authorize";
        public const string AuthorizeEndpoint = BaseAddress + "/oauth2/authorize";
        public const string LogoutEndpoint = BaseAddress + "/connect/endsession";
        //public const string TokenEndpoint = BaseAddress + "/connect/token";
        public const string TokenEndpoint = BaseAddress + "/oauth2/token";
        public const string UserInfoEndpoint = BaseAddress + "/connect/userinfo";
        public const string IdentityTokenValidationEndpoint = BaseAddress + "/connect/identitytokenvalidation";
        public const string TokenRevocationEndpoint = BaseAddress + "/connect/revocation";

        public const string AspNetWebApiSampleApi = "http://localhost:2727/";
    }

Note that the ADFS OAuth2 endpoint is .../adfs/oauth2/authorize and similarly for token as above.

Houston - we have a problem with the ValidateIdentityTokenAsync method in the AccountController class.

The Github code does not match the code in the above link.

There was a later post - refer Using Discovery and Katana Middleware to write an OpenID Connect Web Client - and this matches the Github code.

In that code, you'll see:

var certString = "MIIC4jCCA..."

OK - see where does my certificate info. come from?

As per that post, it comes from:

https://ADFS.local/adfs/.well-known/openid-configuration

which returns JSON like:

{

    "issuer": "https://ADFS.local/adfs",
    "authorization_endpoint": "https://ADFS.local/adfs/oauth2/authorize/",
    "token_endpoint": "https://ADFS.local/adfs/oauth2/token/",
    "jwks_uri": "https://ADFS.local/adfs/discovery/keys",
    "token_endpoint_auth_methods_supported": 

[

    "client_secret_post",
    "client_secret_basic",
    "private_key_jwt",
    "windows_client_authentication"

],
"response_types_supported": 
[

    "code",
    "id_token",
    "code id_token",
    "token id_token"

],
"response_modes_supported": 
[

    "query",
    "fragment",
    "form_post"

],
"grant_types_supported": 
[

    "authorization_code",
    "refresh_token",
    "client_credentials",
    "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "implicit",
    "password"

],

and more - this is truncated. But nowhere in the JSON is there any certificate information.

The key is this line as above.

"jwks_uri": "https://ADFS.local/adfs/discovery/keys"

Browse to that URL and you'll see a "x5c" section:

x5c": [ 
"MIIC4j... 
]

That Base64 string is what you paste into your code.for "var certString =".

And then you should be good to go.

Run the application, it will take you to the normal ADFS logon screen where you authenticate and then you will see:


If you use some sort of trace like "SAML Tracer" or Fiddler, you'll see a line like:


Now if you copy and paste this into a JSON viewer like:

http://jwt.io/

you'll see the same thing.

Enjoy!

Monday, May 25, 2015

Misc : Citrix Receiver on Firefox

I do a lot of work for customers and to do that I need remote access to their servers.

The VPN tool of choice these days seems to be Citrix Receiver.

If you are experiencing problems with FireFox i.e. you can login and get the portal but when you click on one of the icons e.g. desktop or remote desktop and you see the little wheel spin for a few moments and then stop and then nothing happens, try this:

Tools / Options / Applications


Ensure that "Connection Manager" is selected and not "Receiver".

Enjoy!