5

In an ASP.NET MVC3 web application, an entire controller has an [Authorize] attribute attached to it. So if the user is not logged in or the session expired, they get redirected to the login page. This is working...sometimes. The URLs in the "works" list below correctly redirect to the login page; the URLs in the "does not work" list instead show the IIS 401 error screen - they do not redirect to the login page.

Works

Does Not Work

The model for the MyAction action has a public string ReturnUrl { get; set; } in its base class. It also has other properties, but adding those to the query string does not affect the login redirection. It seems to be only the ReturnUrl parameter.

I'm not sure what else to look into. Any ideas why the ReturnUrl parameters would be causing trouble?

Routes

routes.MapRoute("Default-Title-ID", "{Controller}/{Action}/{Title}_{ID}", namespaces);
routes.MapRoute("Default-ID", "{Controller}/{Action}/{ID}", namespaces);
routes.MapRoute("Default", "{Controller}/{Action}", new { Controller = "Home", Action = "Index" }, namespaces);
routes.MapPageRoute("Reports-View", "ViewReport_{ID}", "~/Views/Reports/View.aspx");

Working Example (Well, not working, but illustrates the problem.)

Download the solution here: https://docs.google.com/file/d/0B4o6vqgNLpvbeVo4bVdKZWFMcEE/edit?usp=sharing

And then try to visit:

Josh M.
  • 26,437
  • 24
  • 119
  • 200
  • stunner. I assume no exotic routes being used. – Dave Alperovich Apr 20 '13 at 03:05
  • @DaveA normal routes - added them above. – Josh M. Apr 20 '13 at 03:07
  • Looking over these routes, I suspect the route engine is getting confused... There is no effective difference between `Default-Title-ID` and `Default-Title-ID` -- They both have a 3rd ID param, but the route engine would NOT know which to choose when using url's. True `Default` is the complement, in that both Controller and Action are optional, but that route is traditionally implemented as part of the 1st of 2nd to give the route engine more flexibility. I would strongly suggest trying to merge the 3 routes, then seeing if you still have the same problem. – Dave Alperovich Apr 20 '13 at 05:34
  • @DaveA - The first two routes **are** different. One specifies that there is text before the ID, the other specifies that there is not. Yes, I can likely combine some of these routes but for clarity I've left them as you see them. I'll try commenting out the first one anyway and see if the problem persists...but I think it will. Thanks. – Josh M. Apr 22 '13 at 01:02
  • @DaveA - The problem persists after commenting out the `Default-Title-ID` and `Default` routes. – Josh M. Apr 23 '13 at 11:23
  • IC. in your web.config, what is your `forms loginUrl` setting? – Dave Alperovich Apr 23 '13 at 12:09
  • Added a working example solution. – Josh M. Apr 23 '13 at 13:12
  • @DaveA - Login URL is "~/Public/Login" and keep in mind that this works correctly when there is no query string! – Josh M. Apr 23 '13 at 13:14
  • strange. since `401` is unauthorized, i wonder if you are being sent to another action... i'm speculating, but this is hard to fathom – Dave Alperovich Apr 23 '13 at 13:46
  • @DaveA - Feel free to take a look at the working example solution: https://docs.google.com/file/d/0B4o6vqgNLpvbeVo4bVdKZWFMcEE/edit?usp=sharing (if you haven't already). – Josh M. Apr 23 '13 at 14:50
  • Not the same problem, but has some interesting links about [authentication and ReturnUrl](http://stackoverflow.com/questions/16305962/what-initially-sets-the-returnurl-parameter-when-using-authorizeattribute) that could help out: – mateuscb Mar 16 '14 at 02:57

1 Answers1

2

I wanted to post this as a comment, but I is too long. I needed a dynamic redirect for one of my apps, and used the following solution (it uses the controller that called it instead of the static URL in web.config). When testing this with your example, it fixes the issue. But I can not figure out why. Maybe it will lead you to the right path or someone else can clarify.

using System.Web.Mvc;
using System.Web.Routing;

namespace MvcApplication1.App_Start
{
    public class LoginRequiredAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);

            if (filterContext.Result is HttpUnauthorizedResult)
            {
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary 
                {
                    { "controller", filterContext.RouteData.Values[ "controller" ] },
                    { "action", "Login" },
                    { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                });
            }
        }
    }
} 

Then just change the action to use the new attribute:

[LoginRequired]
public ActionResult TestMe()
mateuscb
  • 10,150
  • 3
  • 52
  • 76
  • After a couple years I came back to this problem and, having completely forgotten that I ever created this question, created the same question again (http://stackoverflow.com/questions/29973280/mvc-route-returns-401-only-if-returnurl-parameter-is-in-querystring). Then I was reminded of this initial question, implemented your suggestion, and now I'm happy because it's working. _But I still don't know why it wasn't working in the first place._ Thanks! – Josh M. Apr 30 '15 at 17:50