Prompt for Sign-In on 401 – Unauthorized Errors in MVC App with Azure Active Directory Using WS Federation

Following my post on Partial Authentication with AAD I am going to elaborate on how to prompt the user to sign into the application instead of throwing a 401 – unauthorized error.

First step is to add HTTP error 401 redirect to IIS configuration in Web.config

<system.webServer>
    <httpErrors existingResponse="Replace" 
			defaultResponseMode="Redirect" errorMode="Custom">
      <remove statusCode="401"/>
      <error statusCode="401" responseMode="ExecuteURL" path="/Account/SignInRedirect"/>
    </httpErrors>
	...
<system.webServer>

Note that the responseMode is ExecuteURL (and not Redirect). This allows the requested URL to be passed through to the SignInRedirect method.

Now all we need is to add proper redirect conditions to our /Account/SignInRedirect method

public ActionResult SignInRedirect()
{
	if (Request.IsAuthenticated)
	{
		// The user is signed in 
		// but does not have permissions to access a specific URL
		return RedirectToAction("InsufficientPermissions", "Account");
	}

	// Redirect to home page after signing in.	
	WsFederationConfiguration config = 
		FederatedAuthentication.FederationConfiguration.WsFederationConfiguration;
		
	string callbackUrl = Url.Action("Index", "Home", 
			routeValues: null, protocol: Request.Url.Scheme);
	
	//The URL the user originally requested is passed in Request.RawUrl
	if (Request.RawUrl != null)
	{
		callbackUrl = Request.RawUrl;
	}

	SignInRequestMessage signInRequest = 
		FederatedAuthentication.WSFederationAuthenticationModule
		.CreateSignInRequest(
			uniqueId: String.Empty,
			returnUrl: callbackUrl,
			rememberMeSet: false);

	signInRequest.SetParameter("wtrealm", IdentityConfig.Realm ?? config.Realm);
	return new RedirectResult(signInRequest.RequestUrl.ToString());
}

 

There are 2 main use cases this method aims to cover:

A non-authenticated user gets tries to access a URL that requires authorization

When a non-authenticated user tries to access a URL that is behind an [Authorize] attribute she will get a 401 – Unauthorized error. This error will be redirected by IIS to the path specified in httpErrors configuration, in our case /Account/SignInRedirect. The original URL will be passed on as well in Request.RawUrl. The sign in method will then attempt to sign the user in, and if the sign in is successful redirect her to the originally requested URL.

An authenticated user tries to access a URL he does not have access to

In this case the first if clause of the sign in method will be activated, and the user will be redirected to a custom error page. You can inform the user that she does not have permissions to view the page, or craft any other custom error message. You could also choose to redirect the user back to home page. The one thing you should _not_ do is redirecting the user back to the Request.RawUrl, as this will result in an infinite redirect loop.
Another possible behavior in this case would be to prompt the user to sign in with an account that does have access to the requested URL. This behavior is, however, not typical for AD scenarios, where it is unlikely that a single user has access to multiple AD accounts.

Why am I separating the /Account/SignInRedirect from the regular /Account/SignIn method?
The reason is the case when the user tries to sign in via /Account/SignIn, and her credentials are already stored by the browser. In this case the user should be redirected to home, rather than hitting the insufficient permissions error page. Please view the original post for the /Account/SignIn method implementation.

Generic Type-Safe Method to Retrieve Values from App Settings

Following my post from yesterday, I had to publish this little method that helps you retrieve values from your appSettings in a type-safe manner.

I simply love things like that – just a few lines of code make sure you won’t have to worry about type casting in the rest of your application!

using System;
using System.Configuration;

namespace MyApp.Domain.Helpers
{
	public static class ConfigHelper
	{
		public static T GetAppSetting<T>(string key)
		{
			var setting = ConfigurationManager.AppSettings[key];
			if (null == setting) return default(T);
			return (T)Convert.ChangeType(setting, typeof(T));
		}
	}
}

 
* Please note that this method will return the default value of type T if the setting key isn’t present, and throw an exception if the value is of an incorrect type. Other approaches to error handling might need to be implemented depending on your application.

And the usage:

var intValue = ConfigHelper.GetAppSetting<int>("IntValue");
var stringValue = ConfigHelper.GetAppSetting<string>("StringValue");

Custom MVC Role Authorize Attribute using App Settings

Here is a simple implementation of a custom [Authorize] attribute that uses appSettings get Role names:

The only important thing to note here is using Sytem.Web.Mvc, since the [Authorize] attribute also exists in System.Web.Http and has a slightly different implementation there.
I believe the two implementations have been unified in Asp.Net 5 / MVC 6, but that is still in preview.

using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MyApp.Web.Attributes
{
    public class RoleAuthorizeAttribute : AuthorizeAttribute
    {

        public RoleAuthorizeAttribute(params string[] roleNames)
        {
            var roles = roleNames.Select(roleName => 
			ConfigHelper.GetAppSetting<string>(roleName)).ToList();

            this.Roles = string.Join(",", roles);
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            return base.AuthorizeCore(httpContext);
        }
    }
}

Now you decorate your controllers / methods with [RoleAuthorize(“MyRoleName”)]

[RoleAuthorize("MyRoleName")]
public class MyController : Controller
{
	public ActionResult Index()
	{
		return View();
	}
}

And this is all for this post =)

Partial Authentication with Azure Active Directory with WS Federation in an MVC Application

Given: an MVC web application that is authenticated with Azure Active Directory using WS Federation.

Problem: allow non-authenticated users to access the application, restrict some of the pages to authenticated users only.

Challenge: when you start a new project in Visual Studio 2013 and choose Azure Active Directory as your Identity provider you get a setup that is pre-configured to put the entire site behind authentication.

To re-configure your application to allow non-authenticated users, you will need to do the following:

Web.config
Change the authorization snippet to allow users.

<system.web>
    <authorization>
      <allow users="*" />
    </authorization>
	...
</system.web>

 
AccountController.cs
Add the SignIn Method

public ActionResult SignIn()
{
	if (Request.IsAuthenticated)
	{
		// Redirect to home page if the user is already signed in.
		return RedirectToAction("Index", "Home");
	}
	// Redirect to home page after signing in.	
	WsFederationConfiguration config = 
		FederatedAuthentication.FederationConfiguration
			.WsFederationConfiguration;

	string callbackUrl = 
		Url.Action("Index", "Home", 
			routeValues: null, protocol: Request.Url.Scheme);
		
	SignInRequestMessage signInRequest = 
		FederatedAuthentication.WSFederationAuthenticationModule
			.CreateSignInRequest(
				uniqueId: String.Empty,
				returnUrl: callbackUrl,
				rememberMeSet: false);

	signInRequest.SetParameter("wtrealm", IdentityConfig.Realm ?? config.Realm);
	return new RedirectResult(signInRequest.RequestUrl.ToString());
}

 
Now you can decorate the appropriate controllers and/or methods with the regular MVC [Authorize] attribute to require authentication.

 
If you have multiple Reply URLs configured for your application in Azure AD, you will need to add the following setting to your Web.config transforms for different environments:

<system.identityModel.services>
	<federationConfiguration>
		<wsFederation reply="EnvironmentSpecificReplyURL" 
				xdt:Transform="SetAttributes" />
	</federationConfiguration>
</system.identityModel.services>

 
Please view my subsequent blog post on how to handle 401 – Unauthorized errors properly.

 
Bonus Tip: consider switching your application from WS Federation to the newer and shinier OpenId Connect. See samples here.

Developer’s How To: Custom Generic MVC Helpers Utilizing LINQ

If there is anything in software development I shy away from, it is replicating the same lines of code, however few, over and over again. However sometimes you run into bits of code that seem to be harder to abstract away than to replicate. For a while this seemed to be one of them.

Read More (Company Blog Site)

Developer’s How To: Styling a File Upload in Microsoft MVC 4

Implementing a file upload in an MVC Web Application seems to be pretty easy to do. Styling the upload page, however, seems to be a bigger issue, but here I am going to give an example of how it can be done. The basic rule is that if you have an MVC 4 Web Application you most probably already have all the .css style sheets in place, and all you need is to get them applied to the file upload html element. Note: the code shown in the example is available for download.

Read More (Company Blog Site)