thoughts on coding

January 4, 2010

SSL support in ASP.NET MVC

Filed under: .NET, ASP.NET MVC — Frantisek @ 9:25 am

I have some views which I need to secure with SSL (HTTPS) and rest should be plain HTTP.  Thankfully, MVC 2 supports RequireSSL attribute to ensure the cuser is performing the action with HTTPS protocol. Fine! Cool!

But if the user is on the view secured by HTTPS then all links (ALL!) created by ActionLink extensions methods are secured! So the question is who/how and when to navigate back to the unsecured links/views?

I tought that the routes configuration is the part where to setup the routes and then we should just use route capabilities to create the links, redirects, etc. In other workds, the configuration is on 1 place and only once! and I can generate the links from all other places. If I want to change the links, I should change only route configuration and all should work well. And whole implemnetaiton should follow DRY principle. But it’s not true. I can’t specify that all links should be sent over HTTP except some special routes which should be handled by HTTPS.

I’ve read several sources how to implement it, i.e. http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ and Steve’s  implementation solves DRY principle but it overwrites VirtualPath to the absolute path and then the special context wrapper corrects it back.  I implemented it different way.

So what is my solution?

The idea behind the scene consists of 3 parts:

1) when configuring the routes, I add the data token scheme to the route definition

routes.Add(new Route("Account/Login", new MvcRouteHandler())
        {
            Defaults = new RouteValueDictionary(new { controller = "Account", action = "Login" }),
            DataTokens = new RouteValueDictionary(new { scheme = "https" })
        });

2) I had to implement and use my own ActionLink extentions methods called MyActionLink (hm, super name, isn’t?). MyActionLink methods are almost same as the default ActionLink implementation except the method with full parameter set.

public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName)
        {
            return htmlHelper.MyActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, routeValues, new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, routeValues, htmlAttributes);
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
        {
            if (string.IsNullOrEmpty(linkText))
            {
                throw new ArgumentNullException(linkText);
            }
            var requestContext = htmlHelper.ViewContext.RequestContext;
            var values = MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, new RouteValueDictionary(), true);
            var vpd = htmlHelper.RouteCollection.GetVirtualPath(requestContext, values);
            return htmlHelper.ActionLink(linkText, actionName, controllerName,
                (string)vpd.DataTokens["scheme"], (string)vpd.DataTokens["hostname"], null, routeValues, htmlAttributes);
        }

Note: The samilar changes can be done with RedirectToAction<T> and RedirectToAction methods in order to take care of “scheme”. But what I do is that I display the view that the secured action was done successfully and then the user is redirected to i.e. the index view of the controller.

3) decorate the appropriate action with RequireSSL attribute.

        [RequireHttps]
        [HttpPost]
        public ActionResult LogOn(LoginInput input)
        {
            ....
        }

And that’s all!

And you can use it as you would use ActionLink just with name MyActionLink.  So I replaced all ActionLink(s) to MyActionLink and that was all!

In my project I use MvcContrib. So I changed MvcRoute to include scheme “http” by default, all my links are created by MyActionLink extension method and sensitive actions are decorated with RequireSSL.

Do you know other, better implementation? Please, let me know.

Advertisements

2 Comments »

  1. Skvělý blog, Františku, jen tak dál! 🙂

    Comment by Augi — January 6, 2010 @ 4:12 pm

    • Vdaka/Thanks 🙂

      Comment by Frantisek — January 6, 2010 @ 7:09 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: