Windows Server 2012 R2 comes with IIS 8.5, and in this release an issue has been found in relation to the Dynamic Compression module. The module sets the “Vary” header which is used to specify caching properties that the browser uses to determine whether the response should be cached or not.

In IIS 8.0 and earlier, the Dynamic Compression module was overwriting the Vary header with the value “Accept-Encoding”, and as it happens this is the correct value to ensure that dynamic content is correctly cached – but – according to IIS it should be appending this value to the existing value and not overwriting it.

As it happens, this was supposed to be fixed in IIS 8.5 but the fix appears to be broken. In IIS 8.5 (which ships with Windows Server 2012 R2) the Vary header is being set to “*” and the “Accept-Encoding” from the Dynamic Compression module is not appended. The result of this is that no dynamic content is being cached by the browser.

Workaround

Thankfully there is an easy workaround in IIS 8.5 for this:

1. Select an IIS site, and go to ConfigurationEditor.

2. Select system.web/caching/outputCache section, then set the omitVaryStar property to true

Setting this value results in the Vary header being returned with a value of “Accept-Encoding” and the browser then caches the dynamic content.

Hello there. I’ve been spending a lot of time of late trying to develop a solution to a very obscure problem scenario. The entire problem itself is outside the scope of this article – and to be honest, probably wouldn’t be terribly relevant to many – however, I felt there was value in articulating my recent experiences with Cross-Origin Resource Sharing, or CORS for short.

So what is CORS?

“Cross-origin resource sharing (CORS) is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated.[1] In particular, JavaScript’s AJAX calls can use the XMLHttpRequest mechanism.

Such “cross-domain” requests would otherwise be forbidden by web browsers, per the same-origin security policy. CORS defines a way in which the browser and the server can interact to determine whether or not to allow the cross-origin request.[2] It is more useful than only allowing same-origin requests, but it is more secure than simply allowing all such cross-origin requests.”

Now that we’ve cleared that up… my take – a web site makes use of resources which are hosted on another site outside of its domain. This is an important distinction owing primarily due to cross-site scripting (XSS) and cross-site request forgery (CSRF) vulnerabilities.

What does something like CORS address exactly?

Well, the concept is fairly straightforward. What CORS aims to do is have two websites (sites, pages, APIs etc.) agree on what kind of resources and types of requests one website will provide to another. Both must agree exactly on what is being shared and how.

How is this accomplished?

There’s a few parties who need to participate to enable CORS – the two parties involved, of course, and the user’s browser. Both sites need to request and respond to each other in an expected manner, and browsers need to be aware of, and in some cases make special requests to ensure CORS works correctly.

In essence, what happens is that both websites agree on how resources will be shared. The requesting site must be known as an “allowed origin” by the site providing the resources. The response also must contain headers which contain scope for acceptable resource sharing, e.g. naming allowable methods (e.g. GET, PUT) and whether credentials are supported. Browsers themselves are the last key – they must respect the restrictions established by the requesting site and the resource site.

What is a “pre-flight request”?

In some cases, a browser might make a special type of request known as an OPTIONS request, which is sort of like an initial handshake before performing the actual request specified (e.g. a GET request).

In essence, an OPTIONS request attempts to determine what supported methods and other information is available from a resource sharing server. In browser terms, this is known as a “pre-flight” request and is often attempted automatically by the browser.

The first time a cross-site request might fail (and in subsequent attempts) the browser’s JavaScript console might log something similar to the following error:

XMLHttpRequest cannot load https://<TARGETSERVER>/p?ReadViewEntries&outputformat=json. The request was redirected to ‘https://<ANOTHERSERVER>’, which is disallowed for cross-origin requests that require preflight.

Here’s an example of a browser (Chrome) attempting an OPTIONS pre-flight request and failing:

The most important thing in this response isn’t probably what you’d expect it to be. It’s actually the HTTP STATUS CODE (i.e. 200). For CORS pre-flight to work, the resource target must respond with a Status Code of 200 to a HTTP OPTIONS request – and it must do so for unauthenticated requests!

Why must the pre-flight/OPTIONS requests be unauthenticated?

“Otherwise, make a preflight request. Fetch the request URL from origin source origin with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints:

Therefore, a response status code of 302 (Found – Redirect, usually to authenticate) or 401 (unauthorised) will clearly fail pre-flight. Note that the resource (target) server doesn’t have to honour all OPTIONS requests, you could lock down the server’s security to only respond (Status 200) to requests on certain paths, for example.

In the example screenshot earlier, the pre-flight was failing because the resource server had authentication enabled for the OPTIONS request, and it was redirecting to a security token service (STS) for authentication.

Now that that’s all cleared up, how about some sample code?

Here’s a pretty straightforward request which sets the appropriate header values for cross-domain and with credentials for a GET request, which is expecting a JSON response:

In many cases though, this is not enough. Some browsers, particularly older versions for example Internet Explorer 8 and 9 simply don’t support CORS well enough. A more version sensitive approach might be something like this:

Some notes about setting headers

CORS header values in a request must be supported on the resource server for a given request to succeed, i.e. for a request which specifies Access-Control-Allow-Credentials, the target server must respond with Access-Control-Allow-Credentials listed in the Access-Control-Allow-Headers.

The same applies to methods, e.g. a GET method must be listed in the response Access-Control-Allow-Methods

When you set the Access-Control-Allow-Origin value, it should be the name of the origin server (the origin of the request) it really shouldn’t be asterisk (*) which is often used in sample code. The target resource server also must recognise/honour the origin.

Introduction

In Part 1, we had a brief look at OWIN/OAuth concepts, and then prepared a clean ASP.NET web forms project for integration with NuGet packages essential to supporting a lightweight integration for OAuth handling.

In Part 2 we established the information required to authenticate users against the Live Connect OAuth provider, and also established a debugging environment; it’s time for the meat of the article.

Finally, this article will demonstrate the code necessary to handle authenticated user identities. I must stress that this is incredibly lightweight, and hopefully it will be clear what you need to consider if taking this approach to a new or existing project.

Note: This article was originally published at Sanders Technology, check here for updates to this article and related technical articles.

The Concept Applied

Let’s quickly take a look at what you’ll be implementing from a conceptual point of view. What you’ll be doing is effectively redirecting your users to a third party. Once they have successfully authenticated, they’ll return to your site (or app) with some sort of token or claim (query string based).

Your site or app will then exchange this information with the third party to validate it is correct and valid. If this is successful, the third party will share a bit of information about this user with you, which you could use to tie to a local identity if you preferred.

Here’s the concept in diagram form:

How the OAuth process works

Armed with this knowledge, there are a few design decisions you’ll need to make. The most obvious one is planning how you’ll handle those redirects back to your site or application, once a user has authenticated with their OAuth provider (in this article, Microsoft Live Connect/Live Accounts).

In Part 2, I briefly discussed the importance of the Redirect Domain when configuring the Live Application. This is, in fact, the target URL that your users will be given, once they’ve authenticated. This can be a basic URI or a specific page. In any case, the target has to be able to handle the authentication tokens.

Enough theory, show me some code

The code to handle authentication is ridiculously simple and lightweight. In fact, I’ve encapsulated the logic into a single class. In my example, I’ve hardcoded the implementation to the Microsoft provider, but I’m positive you could easily write a more generic implementation which accepts multiple OAuth providers.

There is an additional class required to make use of the OpenAuthSecurityManager class – a concrete class which implements the IOpenAuthDataProvider interface. Luckily, you don’t have much to implement for this to work successfully:

As you can see, I don’t even really do anything here, I’m just returning the provider-supplied value. You could do much more with this, including your own application-specific formatting.

Class view of the OAuthDataProvider class

Taking a look at the runtime process

To illustrate how this all works, I’ve updated the “clean” example solution from Part 1. I must stress, this is a very, very simple implementation, and should in no way be used in a Production website. It’s simply to illustrate how lightweight the implementation can be.

The sample site has a master page with a simple user control on it. This control reflects whether a user has authenticated or not, and provides a mechanism for initializing the authentication process (in short: a “login button).

I’ll now show you what the process looks like from a series of screenshots of the working solution. In this example, I’ve made the modifications mentioned in Part 2, as I have aliased “atempuri.org” to my localhost (for debugging purposes).

I’ve also created a Live Application at the Live Connect site. Remember – you need the Client ID and Secret ID plus have set the correct Redirect URL for this to work.

The Process

To begin, I load the default page on the site. Notice in the top left hand corner the fairly boring “[ Authenticate ]” button. When I click the button, it redirects me to the Live Connect page to sign in with a Microsoft (formerly Live, formerly Passport) Account.

The default page / Authenticate to Microsoft

Once I have authenticated, the Microsoft site asks me to allow my application to access basic account data.

The user is prompted to authorise the application

Notice here that I have not supplied an application logo – doesn’t it look ugly and unprofessional? Once the user clicks the “Yes” button, the Microsoft site redirects the user’s browser to the URI set in the Live Application’s settings – which must match the address specified in the code in your site or application.

In this case, the user’s browser is directed to http://atempuri.org/Login (with additional data in the query string). My login.aspx page handles the request and checks that the user is not already authenticated.

The Login.aspx event hander code

This page forces the “VerifyAuthentication” call, and checks again OnPreRender to see if validation worked or not. If it is successful, the user is redirected to the default page.

Below is an example of some of the information retrieved with a successful authentication:

Successful authentication provides very basic information

The ProviderUserId can be used to uniquely identify the authenticated user, and also provides a “friendly name” which you can use for display purposes.

We’ve achieved the Zen like state of “authenticated”

The default page now displays a more friendly user experience, and provides a mechanism for “logging off”.

More on the “TODO” elements of this approach

Again, I can’t stress strongly enough that this is only a simple proof of concept. I’ve specifically cut corners here to demonstrate how little is needed to implement OAuth support for authentication.

If you look at the sample solution, there are three values you need to supply:

At a minimum you should consider doing the following with your own website or application, up-front:

Determine your complete security and identity requirements

Decide if you want to have user profiles “local” to your application, and whether you’ll map these identities to their Provider ID

Consider an approach to storing or looking up the authentication data

Decide how your site or application should handle:

Unauthenticated users

Authenticated users

The location of your “Redirect URL”

Decide if you want to integrate this into the ASP.NET Role Provider

Consider using HTTPS/SSL for some or all of your site/application

That’s just a start, this barely scratches the surface.

Summary and Download

Well, I think I’ve managed to cover off all the basics of a lightweight implementation of OAuth (OWIN) using ASP.NET. To aid you, I’ve attached a copy of the “clean” ASP.NET web forms solution, with the authentication classes and the user control included. I’ve personally tested the solution with real Application keys and it is working as depicted here.

It’s not a bad start if you are just looking for a very lightweight way to integrate OAuth as a mechanism for identifying users, and could obviously be extended or used to integrate into your existing authentication/authorisation mechanisms.

Post navigation

Stuff to cover the hosting costs

View By Category

Disclaimer

Privacy: Ads are generated on this site. Google may collect cookies about your interests to make ads more relevant. To Opt out or find more information, see this blog or also see the Google Privacy Center