Dresel / RouteLocalization

RouteLocalization is an MVC and Web API package that allows localization of your attribute routes.
MIT License
67 stars 13 forks source link

Owin redirect problem on latest RL version #30

Closed mdmoura closed 10 years ago

mdmoura commented 10 years ago

Hello,

This is a recurrent problem but it seems there are problems with Owin.

I have the following Owing configuration as I had before:

//LoginPath = new PathString(url.Action(MVC.User.Login())),
Provider = new CookieAuthenticationProvider() {
    OnApplyRedirect = 
        context => context.Response.Redirect(
            url.Action(MVC.User.Login().AddRouteValues(
               new { culture =   Thread.CurrentThread.CurrentCulture.Name })))
     } 

When I try to access a action that requires authentication I get a 401 error.

I have the following Route Localization configuration:

  RouteTable.Routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);
  RouteTable.Routes.Localization(x => {
    x.AcceptedCultures = Settings.I18N.AcceptedLanguages;
    x.DefaultCulture = Settings.I18N.DefaultLanguage;
    x.AttributeRouteProcessing = RouteLocalization.Mvc.Setup.AttributeRouteProcessing.AddAsDefaultCultureRoute;
    x.AddCultureAsRoutePrefix = false;
    x.AddTranslationToSimiliarUrls = false;
  }).TranslateInitialAttributeRoutes().Translate(x => { x.Process(); });
  CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = context => { return context.GetCulture(); };
  GlobalFilters.Filters.Add(new CultureSensitiveActionFilterAttribute(true, true));

I also changed:

    OnApplyRedirect = 
        context => context.Response.Redirect(
            url.Action(MVC.User.Login().AddRouteValues(
               new { culture =   Thread.CurrentThread.CurrentCulture.Name })))
     } 

To:

    OnApplyRedirect = context => context.Response.Redirect(url.Action(MVC.User.Login())

As I am not setting a route prefix. But the problem remains ...

If I remove RouteLocalization, comment Provider and Uncomment LoginPath and simply use RouteTable.Routes.MapMvcAttributeRoutes() then everything works fine.

Am I missing something, did something change on latest version or is this something related with my RouteLocalization configuration?

Thank You, Miguel

Dresel commented 10 years ago

What Url does

url.Action(MVC.User.Login().AddRouteValues(new { culture = Thread.CurrentThread.CurrentCulture.Name }))

generate.

Which Url should it generate. What happens If you call the route directly. Is OnApplyRedirect executed?

Dresel commented 10 years ago

Don't forget to not comment out LoginPath (#16).

mdmoura commented 10 years ago

Yes, I tried this:

    LoginPath = new PathString(url.Action(MVC.User.Login())),
    Provider =  new CookieAuthenticationProvider() { OnApplyRedirect = context => context.Response.Redirect(url.Action(MVC.User.Login()) },

And also tried with LoginPath commented. And I also tried with culture added to MVC.User.Login.

It never worked. In Owin Setup I checked the values of the following:

  var a = url.Action(MVC.User.Login());

  var b = url.Action(MVC.User.Login().AddRouteValues(new { culture = System.Threading.Thread.CurrentThread.CurrentCulture.Name }));

They are both null ... If I remove the RouteLocalizationMVC configuration and use only:

RouteTable.Routes.MapMvcAttributeRoutes();

Then both routes become:

 /account/login
 /account/login?culture=pt-PT

So the problem might be somewhere inside RouteLocalization?

Dresel commented 10 years ago

What is Settings.I18N.DefaultLanguage?

Does

var c = url.Action(MVC.User.Login().AddRouteValues(new { culture = Settings.I18N.DefaultLanguage }))

also return null?

mdmoura commented 10 years ago

Ok, so to isolate things I tried the following configuration:

  RouteTable.Routes.MapMvcAttributeRoutes(Localization.LocalizationDirectRouteProvider);
  RouteTable.Routes.Localization(x => {
    x.AcceptedCultures = new HashSet<String>(new String[] { "en" });
    x.DefaultCulture = "en";
    x.AttributeRouteProcessing = AttributeRouteProcessing.AddAsDefaultCultureRoute;
    x.AddCultureAsRoutePrefix = false;
    x.AddTranslationToSimiliarUrls = false;
  }).TranslateInitialAttributeRoutes().Translate(x => { });
  CultureSensitiveHttpModule.GetCultureFromHttpContextDelegate = context => { return new CultureInfo("en"); };
  GlobalFilters.Filters.Add(new CultureSensitiveActionFilterAttribute(true, true));

Then Owin I checked the following:

  var a = Thread.CurrentThread.CurrentCulture.Name;
  var b = url.Action(MVC.User.SignIn());
  var c = url.Action(MVC.User.SignIn().AddRouteValues(new { culture = Thread.CurrentThread.CurrentCulture.Name }));  

a is "pt-PT" when it should be "en". b and c are null.

Am I missing something in my Route Localization Configuration?

Dresel commented 10 years ago

I guess you are setting a, b, c outside of OnApplyRedirect?

You are then in the Application StartUp routine which is different from a normal HttpRequest. Check a, b, c within OnApplyRedirect.

mdmoura commented 10 years ago

I guess you are setting a, b, c outside of OnApplyRedirect?

Yes, now I tried the following:

  UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext);
  application.UseCookieAuthentication(new CookieAuthenticationOptions {
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    CookieHttpOnly = true,
    CookieName = "MyApp",
    CookiePath = "/",
    CookieSecure = CookieSecureOption.SameAsRequest,
    ExpireTimeSpan = TimeSpan.FromDays(14),
    LoginPath = new PathString(url.Action(MVC.User.Login())),
    Provider =  new CookieAuthenticationProvider() { OnApplyRedirect = context => context.Response.Redirect(Test(url)) },
    ReturnUrlParameter = "r",
    SlidingExpiration = true
  });

public String Test(UrlHelper url) {
  var a = url.Action(MVC.User.Login());
  var b = url.Action(MVC.User.Login().AddRouteValues(new { culture = System.Threading.Thread.CurrentThread.CurrentCulture.Name }));
  return url.Action(MVC.User.Login().AddRouteValues(new { culture = System.Threading.Thread.CurrentThread.CurrentCulture.Name }));
}

When using RouteLocalization configuration the Test method does not fire when I try to access a route that requires authentication.

If I simply use RouteTable.Routes.MapMvcAttributeRoutes() then the Test method fires and I am redirected with no problems.

To be clear, with RouteLocalization Test method does not fire either I use: //LoginPath = new PathString(url.Action(MVC.User.Login())), or LoginPath = new PathString(url.Action(MVC.User.Login())),

Dresel commented 10 years ago

And what happens instead?

mdmoura commented 10 years ago

I get the 401 error page in the browser:

Most likely causes: The authenticated user does not have access to a resource needed to process the request.

Dresel commented 10 years ago

Try to use

LoginPath = new PathString("/")

instead of

new PathString(url.Action(MVC.User.Login()) /* null */)
mdmoura commented 10 years ago

Ok, that seems to solve it ... Let me test a little bit more before closing this.

Why is that "/" works?

Dresel commented 10 years ago

When using OnApplyRedirect the Property LoginPath is not used at all - but it has to be set to a correct path (this path doesn't have to exist though, you could also use "/doesnt-matter-which-path") - but string.Empty or null (url.Action(MVC.User.Login()) => null in your example) are not considered as correct paths.