ASP.NET OpenID/OAuth Login With ASP.NET 4.5 – Part 4

With ASP.NET 4.5 it is very easy to enable users to login to your site with their accounts from Facebook, Google, LinkedIn, Twitter, Yahoo, and Windows Live. In this 7 part series I’ll show you how for each of the identity providers.

Note: Out-of-the-box this only works with WebForms and MVC4. MVC3 is not supported by default.

Part 4: Logging in with LinkedIn

Like with Facebook as covered in Part 3, you will need to register your application with LinkedIn before users can log in using their LinkedIn account.

  1. Go to http://developer.linkedin.com.
  2. In the menu select Support->API Keys, login when prompted.
  3. Click Add New Application.
  4. Next you need to fill out a form with quite some information in it. Fortunately most is straight forward. The image below shows the Company Info and the Application Info. Most important is the Application Name (in my case Michiel’s Security Demo), the Website URL (in my case http://michiel-security-demo.localtest.me), and Live Status. The latter indicating whether this is a development application or a live (production) application.

  5. Next up is contact information, which I’ll skip here, followed by the OAuth User Agreement. This is where you add information on what the user sees when logging in through linked in and where the user should be sent (in your application) when the user logs in (or cancels). In this case I’ll use the root URL of the application, because ASP.NET handles all that for me. Also notice I’ve set the Agreement Language to English.

  6. Agree to the terms of service and click Add Application.
  7. Next you’ll see a screen like the one below. You need the information shown to configure ASP.NET. You can get back to this information later by repeating steps 1 and 2, and then selecting the application you created. You will need the API Key and the Secret Key. The other two are only useful if your application uses the LinkedIn API (not covered here).

  8. Open Visual Studio (if you don’t have already).
  9. Open the project created in Part 1 (or quickly create a project in the same manner).

Now comes the tricky part. For starters, ASP.NET WebForms doesn’t have a neat method AddLinkedIn. There’s a fairly easy way around that using the generic Add method, but you’ll then find out what MVC users have already when they used RegisterLinkInClient: it doesn’t work! After logging in with LinkedIn your application will show one of the following messages:

  • MVC: Login Failure. Unsuccessful login with service.
  • WebForms: External login LinkedIn failed.

The problem is that the internal LinkedInClient class used by both MVC and WebForms isn’t quite correct, as Mr. Roa explains in this blogpost. Now, Mr. Roa’s solution works fine of you’re using ASP.NET MVC, but if you’re using WebForms, you’ll run into a new exception. For some reason verification of the authentication response sent by LinkedIn can’t be processed correctly. I’ve already spent quite some time digging through the underlying mechanics, but so far I haven’t been able to track down the problem. So if you’re using WebForm you’re out of luck. For MVC users just follow the steps Mr. Roa discusses and you’ll be fine.

Book review: Inside Microsoft Dynamics AX 2012


The information in Inside Microsoft Dynamics AX 2012 (The Microsoft Dynamics AX Team, Margaret Sherman; Microsoft Press) in itself might be useful for Dynamics AX 2012 developers, but it is poorly structured. It is also not clear what the target audience is. Chapter 1 is supposed to be an overview, but it contains way too much detail. By the time you reach the end of the chapter, you will have forgotten most of the details, because it doesn’t make sense to you if you’re just beginning. If you’re already experienced, it may make sense, but then it is superfluous information. This style of writing is maintained throughout the book. What use is it to cover MorphX and X++ in great detail if readers haven’t done anything with it yet? As I said, the information might be good, but it should be structured around scenarios. For instance, when discussing Design Role Centers (chapter 5), take it all the way down and go from why, to what, to how (including some X++ development). As it is, you are stuck with some rules of thumb, which you can’t use, because there is no follow through until much later in the book. By then, you need to go back and reread what was said originally. Basically the book reads like a technical manual, which isn’t too surprising if you consider that it was mainly written by the Dynamics AX team itself.

Bottom line: For experienced AX developers this might be a good reference and brush up from previous versions. For other types of readers this book doesn’t work.

ASP.NET OpenID/OAuth Login With ASP.NET 4.5 – Part 3

With ASP.NET 4.5 it is very easy to enable users to login to your site with their accounts from Facebook, Google, LinkedIn, Twitter, Yahoo, and Windows Live. In this 7 part series I’ll show you how for each of the identity providers.

Note: Out-of-the-box this only works with WebForms and MVC4. MVC3 is not supported by default.

Part 3: Logging in with Facebook

  1. Login to Facebook.
  2. Go to https://developers.facebook.com/apps.
  3. Click the Create New App button.
  4. Go through the Captcha (if shown).
  5. In the dialog enter the App Name, in my case Michiel’s Security Demo, and a unique namespace. Then click Next (Doorgaan in Dutch).
  6. Next you need to fill out some information about your application. If all is well the Display Name, Namespace, and Contact Email are already prefilled. The only other thing really needed is a Site URL, which appears when you click the top check-icon.
  7. Save the settings to finish up on Facebook.
  8. Open Visual Studio (if you don’t have already).
  9. Open the project created in Part 1 (or quickly create a project in the same manner).
  10. Find the App_Start folder and open AuthConfig.cs.
  11. Register the identity provider:
    1. In MVC go to the bottom of the RegisterAuth method and uncomment the following line of code:
      OAuthWebSecurity.RegisterFacebookClient(
        appId: "",
        appSecret: "");
    2. In WebForms go to the bottom of the RegisterOpenAuth method and uncomment the following line of code:
      OpenAuth.AuthenticationClients.AddFacebook(
        appId: "your Facebook app id",
        appSecret: "your Facebook app secret");
  12. Set the appId and appSecret values in the code with the App ID and App Secret shown on the settings page of your application in Facebook (see image under step 6).
  13. Save the file.
  14. Run the project.
  15. Click the Log in link. You will notice Facebook has automatically been added next to Google (from Part 2) under Use another service to log in.
  16. Clicking the Facebook button will send you to Facebook to log in.
  17. Login with a Facebook account. You will then see a page like the one below. Notice that it is telling the user which application wants you to log in with your Facebook account. Facebook recognizes this from the App ID, and provides the user with the information you entered earlier.
  18. If you click Go To App (Naar app gaan) you’re sent to your website again. This also registers the App with the user, so he/she will not see the second screen again. The App will show up in the user’s account under My Apps, where the user can remove the App if he/she no longer wants to allow logging in through Facebook. This has to do with the fact that by accepting the App, the user also accepts that the App can read some information.
  19. If this is the first login with Facebook, you can create a registration for the Facebook account. Note: if you’re already logged in through the local database or another identity provider, you will be asked to add the Facebook account to your existing account.

ASP.NET OpenID/OAuth Login With ASP.NET 4.5 – Part 2

With ASP.NET 4.5 it is very easy to enable users to login to your site with their accounts from Facebook, Google, LinkedIn, Twitter, Yahoo, and Windows Live. In this 7 part series I’ll show you how for each of the identity providers.

Note: Out-of-the-box this only works with WebForms and MVC4. MVC3 is not supported by default.

Part 2: Logging in with Google

Enabling your site for users to login with Google is extremely easy. It just requires you to uncomment a single line of code. There is no need for registration with Google or setting up some share secret, as is the case with the other identity providers.

  1. Open Visual Studio (if you don’t have already).
  2. Open the project created in Part 1 (or quickly create a project in the same manner).
  3. Find the App_Start folder and open AuthConfig.cs.
  4. Register the identity provider:
    1. In MVC go to the bottom of the RegisterAuth method and uncomment the following line of code:
      OAuthWebSecurity.RegisterGoogleClient();
    2. In WebForms go to the bottom of the RegisterOpenAuth method and uncomment the following line of code:
      OpenAuth.AuthenticationClients.AddGoogle();
  5. Save the file.
  6. Run the project.
  7. Click the Log in link. You will notice Google has automatically been added under Use another service to log in.
  8. Clicking the Google button will send you to Google to log in, which will show a page like the one below. Notice that it is telling the user which application (gained from URL) wants you to log in with your Google account.
  9. On the next page the user gets to see which information the application will get, in this case only the email address (and in Dutch in my case). The user can also indicate if this permission should be remembered next time.
  10. After clicking the approval button, you’re sent back to the application, where you can create a registration for the Google account. Note: if you’re already logged in through the local database or another identity provider, you will be asked to add the Google account to your existing account.

ASP.NET OpenID/OAuth Login With ASP.NET 4.5 – Part 1

With ASP.NET 4.5 it is very easy to enable users to login to your site with their accounts from Facebook, Google, LinkedIn, Twitter, Yahoo, and Windows Live. In this 7 part series I’ll show you how for each of the identity providers.

Note: Out-of-the-box this only works with WebForms and MVC4. MVC3 is not supported by default.

Part 1: Setting up your development environment

Create a website project in Visual Studio

You’re going to need a website project for any of this to work, and you may have to configure some of it later. So first, let’s create a project.

  1. Fire up Visual Studio (Express will work just fine).
  2. From the menu select FILE > New Project…
  3. Select one of the Web Application projects. In this example I’ll use ASP.NET MVC 4 Web Application.
  4. Click OK.
  5. Select Internet Application. This is the only template that includes the OpenID/OAuth stuff by default.
  6. Click OK.
  7. The project is ready. You may want to run it once just to make sure everything works. Be sure to click Register and then register for an account to see if the database is working.

Setting up your development environment

In most cases you need to register you website with the identity provider to be able to authenticate users with that identity provider. The provider will ask for several pieces of information, most notably a URL. The problem is that this can’t be an IP-address, and can’t be localhost. Most identity providers also don’t allow a port number, so you will have to ensure that your development website can be reached at port 80 (or port 443) and on a valid URL.

Getting a valid URL

Getting a valid URL is mainly about using a domain that an identity provider will recognize as valid and public. You can get one setup for your application in three ways:

  1. Modify your hosts-file to contain the desired URL.
  2. Open notepad as Administrator.
  3. Click File > Open and navigate to the C:\Windows\System32\drivers\etc folder.
  4. Select All Files (*.*) as filter so the files with no extension show up, as shown below.
  5. Open the hosts file.
  6. Add a line for IP-address 127.0.0.1 with the domain you want to use with your application, as highlighted below.
  7. Save the file.
  8. Use a public domain of your own and modify add a DNS entry for your application. How this works depends on your hosting provider, so I won’t elaborate in this.
  9. Use the localtest.me domain. This is a very neat trick by some clever guys. They’ve setup a DNS that will route any localtest.me request to 127.0.0.1, your local computer. That includes the root domain itself, but any subdomain or sub-sub domain (or sub-sub-sub domain… you get the idea), will work. The only domain that doesn’t work is http://readme.localtest.me, because that’s where you can find the documentation and background. The great thing is that this works without registering or anything. For my demo’s I will use http://michiel-security-demo.localtest.me.

Setting up IIS

Besides having a public domain, your application needs to run on port 80. Although this is possible with IIS Express, it is much easier using IIS (if you want to tinker with IIS Express, check Scott Hanselman’s post Working with SSL at Development Time is easier with IISExpress). The one proviso is that IIS can’t work with LocalDB, so you will have to use SQL Server (Express) instead. For the description below I will assume you have SQL Server Express installed. The best way to go about this is creating a new Web Site in IIS with a custom host header. The steps are easy:

  1. Start the IIS Manager.
  2. In the Connections pane navigate the tree to the Sites folder.
  3. Right click Sites and select Add Website…
  4. Set the Site name and under Physical path browse for the application folder created by Visual Studio.
  5. Under Host name set the host header, in my case michiel-security-demo.localtest.me.
  6. Click OK.

Now you need to point Visual Studio to use IIS, instead of IIS Express when you debug.

  1. Open your project in Visual Studio (if not already open).
  2. Right click the project and select Properties.
  3. Select the Web tab, and scroll down to where it says Use Local IIS Web server.
  4. Uncheck Use IIS Express.
  5. You will be warned that you can’t use LocalDB with IIS with the dialog below. Click Yes.
  6. Enter the Project Url, in my case http://michiel-security-demo.localtest.me/. The settings now should look like the image below.
  7. Click Save.
  8. Start the project in debug mode to verify that everything is working under the specified URL.

Although IIS points to the right folder, it needs permissions to read the folder. If that is not the case, you will get a HTTP 500.19 error when you run the application. You can solve this as follows:

  1. Open Windows Explorer.
  2. Navigate to the application folder.
  3. Right click on the application folder and select Properties.
  4. Go to the Security tab and ensure IIS_IUSRS has read permissions. If so, skip the next steps.
  5. Click Edit…
  6. Click Add…
  7. Enter IIS_IUSRS and click OK.
  8. Click OK twice.

Book Review: Async in C# 5.0

Async in C# 5.0 (Alex Davies, O’Reilly) is good read of you plan to do parallel or async programming, although a bit theoretical. The book shows you why asynchronous programming is a little more than just using the new async and await keywords. These keywords make asynchronous programming syntactically simple, but as this book shows through meticulous explanation there is much more than meets the eye. The book starts of with just showing you how async and await works, and then shifts gear to show you how asynchronous programming worked before we had these keywords. This makes you appreciate all the heavy lifting done for you, but it also makes you understand why asynchronous programming is something you need to do with care. Then there’s several chapter about what’s going on and which pitfalls you can run into (and how to avoid them). The strength of this book is the excellent theoretical handling of the topic. The weakness is the lack of practical examples and a high-level view on when and how to use asynchronous constructs.

Intercepting a 404 in IIS 7 and up

Lately I’ve been working on a system that needs to serve flat files, which is what IIS is very good at. However, when a file does not exist (i.e. a 404 error occurs) there are several options:

  1. The file never existed: propagate the 404.
  2. The file did exist, but has been permanently removed: return a 410 (Gone).
  3. The file did exist, but a newer version is available. In our system a different version always gets a new identifier and as such a new URL, so this is a common scenario for us: redirect the user to the new version.

So the question is: How do we intercept the 404 error for any file?

The answer: With an IIS HttpModule, which is pretty much the same as an ASP.NET HttpModule, as the code below demonstrates.

using System;
using System.Net;
using System.Web;

namespace IisModule
{
    public class DemoModule : System.Web.IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(System.Web.HttpApplication context)
        {
            context.EndRequest += new EventHandler(context_EndRequest);
        }

        void context_EndRequest(object sender, EventArgs e)
        {
            var context = (HttpApplication)sender;
            if(context.Response.StatusCode == (int)HttpStatusCode.NotFound)
            {
                context.Response.Redirect(“http://michiel.vanotegem.nl/”);
            }
        }
    }
}

The only place where you can check for a 404 error is at EndRequest. Other events get bypassed, including the Error event. However, as you can see in the code, getting the response code is easy. And after checking it you can do a redirect or change the response and response code (don’t forget to clear the response stream). You need to add this code to a Class Library project in Visual Studio, to which you will have to add a reference to the System.Web assembly. Also, this does not work with .NET 4.0 and up (except in Windows Server 2012), so you’ll need to configure the project to target the .NET 2.0 runtime, which is the case for .NET 2.0 through 3.5, as is shown below.

Configuring IIS

First you have to create a /bin folder in the website you want the module to work in, and copy the DLL to the bin folder. If you want this on the Default Web Site, you need to add this to C:\inetpub\wwwroot (assumming C is your OS drive), as shown below.

Second, you need to fire up IIS Manager to add the Managed Module to the Web Site. Select the Website (in my case Default Web Site) and select Modules, as highlighted below.

Now click Add Managed Module… as highlighted below.

Finally, give the new module a name, so you can recognize it in the list of modules active for the website, and select the module in the dropdown, as shown below, and click OK.

That’s it! Now every 404 will redirect the user to the root of my blog.

Configuring IIS for all sites

If you don’t want to have a /bin folder in your site, and all sites hosted on the system require the same behavior, you can also sign the assemly, place it in the GAC, and configure the Module at server level.

Book Review: Programming Microsoft SQL Server 2012

Programming Microsoft SQL Server 2012 (Leonard Lobel and Andrew Burst; Microsoft Press) is an excellent book if you want to learn the ins and outs of SQL Server. The book is meticulous when it comes to explaining SQL Server features. Much of this is because of the elaborate examples and the detailed explanation of what’s going on. This attention to detail is however both its strength and its weakness. If you already know about database development, you already know much of what is being explained. For instance, you already know what transactions are for and how they work. Explaining the subject as if you’ve never seen a database feels a little overdone. This is also where the book is a little ambiguous. The chapter that deals with T-SQL, only covers the newer T-SQL statements, so you are expected to have prior knowledge. Of course finding the right balance is hard, and this is a good book regardless. It just means that if you already know the topic, you can just skip to where the content is new. It also means that the book is an excellent reference. In fact, reading this book from beginning to end might not be the best use. Explore it for new features, and then keep it around.

Book review: Building Hypermedia APIs with HTML5 and Node

Building Hypermedia APIs with HTML5 and Node (Mike Amundsen; O’Reilly Media) is an interesting little book. It covers designing an online system that operates well in a browser, but works equally well when talking to
from another type of application, which also has positive impact on automated testing. The strength of this book is absolutely the design theory, which is explained very well. The theory applies to pretty much any web development platform, which is where the weakness of the book comes in. If you are not an HTML5 or Node developer, and many of us aren’t, reading through the practical part of the book is somewhat tedious. That said, the book is only 244 pages, and the theory is an easy read. That means that if you are only interested in that you can get through pretty quickly. The examples in itself are simple, but already add up to quite a bit of code you need to wade through. Understanding all that’s going on takes a while.

Dealing with Token Timeout in Windows Identity Foundation

When a Security Token Service (STS) creates a token, that token has an absolute expiration. Usually this is about 60 minutes, after which the Relying Party (RP) has to send the user back to the STS to acquire a new token. When using Windows Identity Foundation (WIF) in ASP.NET (so for passive federation), this is the default behavior, because the SessionAuthenticationModule stores the token in the FedAuth cookie and checks that token on each request.

What’s the problem?

I hear you thinking “Cool, WIF takes care of all that for me”, and that is cool. But there’s also a nasty side effect, one which all web developers have encountered in a different context: sessions. If a user logs in, starts filling out some form, and then gets interrupted by a phone call, chances are that the session expires. This can lead to problems when the user submits the form, because the user has to log in again. The original posted data gets lost in the process, much to the frustration of the user. Because sessions use sliding expiration the problem is minor. But with absolute expiration, expiration can wreak havoc even if the user posts a form just a minute after the form was presented to the user.

Solving the problem

There are several ways to solve the problem outlined above:

  1. Don’t use the SessionAuthenticationModule.
  2. Modify the stored token to implement sliding expiration.
  3. Force reacquiring a token in the background, so you can control when this happens.

Ditching the SessionAuthenticationModule

Not using the SessionAuthenticationModule appears to be simple. Just remove it from web.config and you’re done. However, if you remove it, you have to ensure a user stays logged in after receiving the token, and you need to keep track of the received claims. This means you need to use a cookie or session data to keep track of the user. Basically you would be recreating what the SessionAuthenticationModule does for you for free.

Making token expiration sliding

This is actually easier than ditching the SessionAuthenticationModule. All you have to do is handle the SessionSecurityTokenReceived event and modify the ValidTo property of the token, as shown in this MSDN Forums post.
Tip: You can hookup the event in the Application_Start event in global.asx using FederatedAuthentication.SessionAuthenticationModule to point to the module.

Force reacquiring a token

The above methods work fine, but have some drawbacks. One of these is the fact that Single Sign-On (SSO) no longer works properly if a user spends too long in a single application. This is because the login session with the Identity Provider will expire at some point. Another issue arises if you want to use delegation or impersonation when you call a web service. WS-Trust 1.4 supports delegation (ActAs) and impersonation (OnBehalfOf). Even though WIF officially implements WS-Trust 1.3, WIF does support these constructs. When calling web services from a web application, using delegation is a recommended practice, because it greatly improves the security. The reason is that in order for the web application to make the web service call on your behalf, it needs to acquire a token from the STS, based on the original token. This means a malicious user would need that token in order to make the web service call. Breaking into the web application is not enough.

So, how can you ensure you get a new token once in a while? Although the implementation is somewhat more difficult, the principle is simple: logout of the RP. That triggers the RP to re-authenticate the user by redirecting to the STS. As long as the user is still known in the STS, a new token is transparently given out. You can do this in roughly two ways: in an invisible iframe that does this process under the covers or in the main request stream. The latter is more visible to the user and requires you to bring the user back to where he was before the logout was forced, which is slightly more complicated than a simple redirect. The iframe solution is more elegant, because it does not interrupt the main working process. Basically all you have to do is point the iframe to a handler that logs the user out if necessary, triggering the re-authentication process when necessary and coming back to the handler to produce an empty HTML page. The handler to do this is shown in the code below.

using System;
using System.Configuration;
using System.Globalization;
using System.IdentityModel.Tokens;
using System.Threading;
using System.Web;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.Web;
namespace WebApp
{
    /// 
    /// HTTP Handler triggering token renewal when this is necessary.
    /// 
    public class TokenRenewalHandler : IHttpHandler
    {
        /// 
        /// The default number of minutes before the token expires when renewal should be triggerd.
        /// 
        private const int DefaultTokenRenewalWindow = 20;

        /// 
        /// Value container for the TokenRenewalWindow
        /// 
        private int m_TokenRenewalWindow = 0;

        /// 
        /// Number of minutes before the token expires when renewal should be triggerd.
        /// 
        /// Should be less or equal to the session timeout.
        protected int TokenRenewalWindow
        {
            get
            {
                if (m_TokenRenewalWindow == 0)
                {
                    int securityTokenRenewalWindow;
                    if (!int.TryParse(
                           ConfigurationManager.AppSettings["SecurityTokenRenewalWindow"],
                           NumberStyles.Integer, CultureInfo.InvariantCulture,
                           out securityTokenRenewalWindow))
                    {
                        securityTokenRenewalWindow = DefaultTokenRenewalWindow;
                    }
                    m_TokenRenewalWindow = securityTokenRenewalWindow;
                }
                return m_TokenRenewalWindow;
            }
        }

        /// 
        /// Handles the HTTP reqyest and forces a signout to renew the token if necessary.
        /// 
        public void ProcessRequest(HttpContext context)
        {
            SecurityToken token = null;
            IClaimsPrincipal principal = Thread.CurrentPrincipal as IClaimsPrincipal;
            if (principal != null && principal.Identities.Count > 0)
            {
                token = principal.Identities[0].BootstrapToken;
                if (token != null)
                {
                    DateTime tokenExpirationTime = DateTime.Now.ToUniversalTime().AddMinutes(TokenRenewalWindow);
                    if (token.ValidTo.ToUniversalTime().CompareTo(tokenExpirationTime) < 0)
                    {
                        if (FederatedAuthentication.WSFederationAuthenticationModule != null)
                        {
                            // Force sign-out.
                            FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
                        }
                        // Redirect to this handler to force a new sign-in request to the STS
                        context.Response.Redirect(context.Request.RawUrl, true);
                    }
                }
            }
            // Return an empty HTML page
            context.Response.ContentType = “text/html”;
            context.Response.Write(“”);
        }

        public bool IsReusable
        {
            get { return false; } }
    }
}