Is HttpCookie path case sensitive? - c#

This is for a C# ASP.NET MVC 5 web application, using .NET Framework 4.6.1.
System.Web.HttpCookie has a Path property which is, "the virtual path to transmit with the cookie." The default value of "/" means the server root.
Is that path case sensitive? I think that it should be case insenstive, since the path parts of a URL are case insenstive. [Update: this assumption on my part is wrong. Vide answer below.] However, my experience is proving otherwise.
Fetch cookie
The web app has a FooBarController, which has a index() method where I fetch the value of a cookie and then render a view.
using System.Web;
using System.Web.Mvc;
public class FooBarController : Controller
{
public ActionResult index( )
{
HttpCookie cookie = HttpContext.Request.Cookies.Get( "page_length" );
if ( cookie != null )
{
// Do something with the page length provided by cookie.Value.
...
}
...
return View( somedata );
}
...
The following is the routing to the methods of that controller. Note that it does not matter if "foobar" or "FooBar" is used in the routing. I have tried both.
public class RouteConfig
{
public static void RegisterRoutes( RouteCollection routes )
{
routes.MapRoute( "", "foobar/index", defaults: new { controller = "foobar", action = "index" } );
routes.MapRoute( "", "foobar/load", defaults: new { controller = "foobar", action = "load", HttpVerbs = HttpVerbs.Post } );
}
}
Store cookie
In a different method of that controller, I store a value in a cookie. Take it as given that the following load() method is correctly called during the lifetime of the web app, and the cookie is correctly stored in the client side browser.
public JsonResult load( TableData data )
{
HttpCookie cookie = new HttpCookie( "page_length" );
cookie.Value = data.PageLength.ToString();
// *** This is the important part. ***
cookie.Path = "/FooBar";
cookie.Expires = DateTime.Now.AddDays( 30 );
HttpContext.Response.Cookies.Add( cookie );
...
}
Test 1: Case sensitive path: Works
As mentioned previously, the cookie is successfully stored in the client side browser.
I launch the web app, and navigate to the following URL. Observe that the URL contains the mixed case "FooBar", even though it is not necessary to reach the desired page.
/localhost:12345/FooBar
In FooBarController.index(), I can successfully fetch the cookie and use its value.
Test 2: Case insensitive path: Does not work
The cookie is still successfully stored in the client side browser.
I launch the web app, but navigate to the following URL. Observe that the URL contains a lowercase "foobar", which is sufficient to reach the desired page.
/localhost:12345/foobar
In FooBarController.index(), I cannot fetch the cookie. The cookie is hidden from that method, presumably because it is associated only with the mixed case path of "/FooBar".
Test 3: Mitigation
Store the cookie with a lowercase path of "/foobar".
public JsonResult load( TableData data )
{
HttpCookie cookie = new HttpCookie( "page_length" );
cookie.Value = data.PageLength.ToString();
// *** This is the important part. ***
cookie.Path = "/foobar";
...
I launch the web app, and navigate to the following URL, same as Test 2.
/localhost:12345/foobar
In FooBarController.index(), I can successfully fetch the cookie and use its value.
Why?
Why is it necessary for the path of the cookie to be the same case as the path part of the URL for the page being navigated?

Yes, the path of an HttpCookie is case sensitive.
As pointed out in the comments, my assumption that the path part of URL is case insensitive is wrong. Therefore, it is reasonable to expect that the path stored in cookie should be case sensitive.
The routing configuration for a C# ASP.NET web application is more forgiving. When calling RouteCollection.MapRoute(), the URL pattern provided can be in a different case than that of the actual URL used for navigation. The controller and action names provided to MapRoute() can also be in a different case than that of the actual controller class name and method name. Thus, I was misled.
https://www.ibm.com/docs/en/cics-ts/5.2?topic=concepts-components-url says:
The scheme and host components of a URL are not defined as case-sensitive, but the path and query string are case-sensitive. Typically, the whole URL is specified in lowercase.
Most other URL documentation I found may specifically say that other parts, such as the scheme and host, are case insensitive. And since it does not say anything about the case of the path part, I am supposed to infer that it is case sensitive.

Related

Using RemoteAuthenticationHandler CallbackPath with IApplicationBuilder path match

Related questions
Using IApplicationBuilder.Map generates nested paths with UseMvc
CallbackPath implementation
Redirect URI with Google using asp.net MVC
How to configure ASP.net Core server routing for multiple SPAs hosted with SpaServices
Problem
I have a service running under a specific path on a domain, e.g. https://www.example.com/myservice. The myservice path is dedicated to my service and other services have other paths at the same domain. It is setup like this in startup configure:
app.Map("/myservice", builder =>
{
builder.UseStaticFiles();
builder.UseMvcWithDefaultRoute();
});
I am using a library that implements a custom RemoteAuthenticationHandler. By default, the callback path routes to /x-callback which results in the browser trying to access https://www.example.com/x-callback.
Since my service does not process url's without the /myservice prefix I get a 404. Changing the URL in the browser to /myservice/x-callback manually loads the callback and all is fine.
I can set the callback path for the handler in startup options as expected in startup configure services.
services.AddSomething(options =>
{
options.AddThingX((o) =>
{
o.CallbackPath = new PathString($"/myservice{o.CallbackPath}");
});
});
When I set the callback path like that the browser tries to load /myservice/x-callback. But, this URL now returns a 404. It seems the handler for the callback also has its URL changed. Changing the URL in the browser to /myservice/myservice/x-callback loads the callback as expected.
The RemoteAuthenticationHandler
This is the code in the handler that handles the challenge and uses the callback path. It sets the callback path as a query parameter to the login url.
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
// Add options etc
// ...
// ...
// This defines the login url, with a query parameter for the CallbackPath
var loginUrl = GetLoginUrl(loginOptions);
Response.Redirect(loginUrl);
return Task.CompletedTask;
}
private string GetLoginUrl(MyServiceLoginOptions loginOptions)
{
// This is where the return url is set. The return url
// is used after login credentials are verified.
return $"{Options.LoginPath}" +
$"?returnUrl={UrlEncoder.Encode(Options.CallbackPath)}" +
$"&loginOptions={UrlEncoder.Encode(_loginOptionsProtector.Protect(loginOptions))}";
}
The login controller
This is where the user can provide the credentials and have them verified. After verification, the user is redirected to the callback path.
private async Task<ActionResult> ChallengeComplete(LoginStatusRequest request, ChallengeResponse challengeResponse)
{
// auth logic
// ...
// All is fine, the users credentials have been verified. Now
// we can redirect to the CallbackPath.
return Ok(Response.Finished(returnUri));
}
Note
I could do a URL rewrite but if possible, I would like to use the "correct" /myservice path to avoid confusion and perhaps causing issues for other services (though very unlikely).
Question
How can I prefix the callback path with /myservice so my application can process it without also adding the duplicate prefix?
MapMiddleware is adding the matched path to the Request.PathBase, so you can use it when creating the return url
string returnUrl = Context.Request.PathBase + Options.CallbackPath;

Store data and retrieve after redirect to third party URL

I have some data that I need to get after redirecting my URL to yahoo auth for authentication and access token. I tried using Session and tempData, but both get cleared after redirection and callback to another ActionMethod. Tried using HttpCookie too but it doesn't retain the value either.
How do I store this value and get it after redirection to callback function? Whatever I tried, I get null value. It gets saved at first but gets erased after redirection.
public async Task<ActionResult> YahooAuth(int Id)
{
List<DataAccess.event_dates> yahooEvents = _iadminSettingsService.GetEventDatesByEventId(Id);
Session["yahooEventObj"] = yahooEvents;
TempData["yahoEvnts"] = yahooEvents;
System.Web.HttpCookie cookie = new System.Web.HttpCookie("eventID", Id.ToString());
Response.Cookies.Add(cookie);
var url = "https://api.login.yahoo.com/oauth2/request_auth?client_id=XXX&redirect_uri=https://b0552ca5.ngrok.io/Event/YahooCalendar&response_type=code&language=en-us";
return Redirect(url);
}
[HttpGet]
public async Task<ActionResult> YahooCalendar(string code)
{
List<DataAccess.event_dates> yahooEvents = (List<DataAccess.event_dates>)Session["yahooEventObj"];
List<DataAccess.event_dates> lst = (List<DataAccess.event_dates>)TempData["yahoEvnts"];
string Id = Request.Cookies["eventID"].ToString();
List<DataAccess.event_dates> yahooEvents = _iadminSettingsService.GetEventDatesByEventId(Convert.ToInt16(Id));
. . .
return Redirect("https://calendar.yahoo.com/");
}
In my opinion all method by Session, Tempdata and Cookies can work fine.
I check your code and found you are using ngrock for localhost redirection.
please make sure when you start your application if it's hosting with http://localhost:port and after redirection if it's with ngRock domain name then any of method not working
Session, Tempdata and Cookies store by domain name
please check with application starting with ngRock domain and check after redirection you get data or not?
May this help you.
Thanks

Redirect any incoming URLs with diacritics to equivalent with no diacritics

How does one implement URL redirecting for URLs with accents? I'd like all potential URL requests with accents to be rewritten without the accent. This is for client names on a French language site. Users should be typing the name without the accent, but should they not do so then I'd like them to land on the correct page either way.
Example
User enters: www.mysite.fr/client/andré ==> user is redirected towww.mysite.fr/client/andre
The resource identifier (clientName) in the database is stored without the accent. Here's my RouteConfig.cs :
routes.MapRoute(
name: "ClientDetails",
url: "client/{clientName}",
defaults: new { controller = "Client", action = "ClientDetails" }
I understand that there are various methods for removing accents on a string. And yes, this would allow me to remove accents from the received URL parameter within the Controller method. However, I would like to be consistent, so that users always see URLs displayed without accents. Therefore, how do I implement redirecting URLs throughout my web application? Is there an easy way to do this in RouteConfig.cs or Web.config ?
I think you mean Redirect and not Rewrite given that Rewrite would mean that the URL stays the same, and you display the intended content (which I think is what you don't want).
The strategy that I think you want, can be implemented by creating a custom route constraint that matches everything that has an any special chars on it.
Make this custom route to be the first thing to be evaluated in your route table, and map this custom route to a "RedirectController" (that you will create) that takes care of removing the special chars from the URL and redirecting the user to a URL with no special chars
At the beginning of every request, you can make this check to perform a redirect. In your Global.asax.cs file, include the following...
protected void Application_BeginRequest()
{
var originalUri = HttpContext.Current.Request.Url;
var finalUri = new UriBuilder(originalUri);
finalUri.Path = RemoveAccents(
originalUri.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped)
);
// Check if the URL has changed
if (!originalUri.Equals(finalUri.Uri))
{
HttpContext.Current.Response.Redirect(finalUri.Uri.ToString(), true);
HttpContext.Current.Response.End();
}
}
You might also want to add another line for finalUri.Query and UriComponents.Query. For RemoveAccents, I tried the following code but you can use what you'd like:
public string RemoveAccents(string input)
{
return Encoding.UTF8.GetString(Encoding.GetEncoding(1251).GetBytes(input));
}

ASP.net MVC routing parameters

I have route which looks like this:
http://localhost:1936/user/someOne/##feed%233dbfc1b3-44b4-42b4-9305-4822ac151527
My routes are configured like this:
routes.MapRoute("Profile", "user/{userName}/{treningId}",
new { controller = "Profile", action = "Index", userName = UrlParameter.Optional, treningId = UrlParameter.Optional }
);
And Action in my controller like this:
public ActionResult Index(string username, string treningId)
{
//more code here
}
But I can't get this part: ##feed%233dbfc1b3-44b4-42b4-9305-4822ac151527, when I debugged code, I saw treningId was null.
My question is how can I get part of URL after ## ?
This is not possible as hashes are not included in the request; they are reserved for client-side use in URLs (typically to mark a specific location in/portion of a document).
You have two options. Either encode the hash character as %23:
%23%23feed%233dbfc1b3-44b4-42b4-9305-4822ac151527
Or use a different character/route.
You can't.
The browser doesn't send the stuff after the first # to the server at all (you can verify this by using the debugger in your browser to inspect the traffic between your browser and the server). That is because the # (hash) character carries special meaning in HTTP.
Why don't you just
use regular query parameters (so, something like http://localhost:1936/user/someOne?myparameter=feed%233dbfc1b3-44b4-42b4-9305-4822ac151527), or, alternatively,
choose another delimiter than "##".
The way you have your URL written, the browser would not submit any portion of the fragment identifier - the hash part of the URL. As such, your MVC app will never receive the value and thus will always have it as null.
To make that value available to MVC, escape the # to avoid them marking beginning of fragment portion of URL.

MVC4 -> Mapping one path to another ( not a controller )

OK, let me ask this a different way. We have a web app. We have paid and trial subscribers. Each of them current has a folder named after their user id, and that's where their code lives. The code gets their user id from the URL, so we can't change the URL. I want to consolidate the location of our code, so that people go to the same URL as they are today ( sitename/username ), but it will return the application code ( which is a folder structure ) from a single location. I've tried everything I can think of, but Server.Transfer can't transfer to a folder, transfering to the index.html does not work, Server.TransferLocation and ( of course ) Response.Redirect change the URL on the client side.
I have tried doing this in a RouteHandler, and in a Controller, it makes no difference, I cannot find a way to redirect the user transparently to a central code base. Ideally, I'd do it in code, so I can validate what sort of user they are and redirect them accordingly.
After many iterations, here is the core code, right now:
public class FarmHttpHandler : IRouteHandler
{
enum UserType { Invalid = -1, Standard = 1, Tester = 2, Developer = 3}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Note - this has to be a valid URL, or it will loop forever, as our HTTP handler is at the base level.
// We need to add a 'bad request' page, or it could default to the demo ( this would be a support nightmare though, if someone mistyped their URL,
// they would think it was fine, at first.
string redirect = "/Home/Error";
var routeValues = requestContext.RouteData.Values;
if (routeValues.ContainsKey("farmName"))
{
string farmName = routeValues["farmName"].ToString();
string baseUrl = "/";// HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.AbsolutePath, "/");
switch(GetUserType(farmName))
{
case UserType.Invalid:
break;
case UserType.Developer:
redirect = baseUrl + Settings.BetaAppPath;
break;
case UserType.Standard:
redirect = baseUrl + Settings.FullAppPath;
break;
case UserType.Tester:
redirect = baseUrl + Settings.TesterAppPath;
break;
}
}
HttpContext.Current.Server.TransferRequest(redirect);
requestContext.HttpContext.RewritePath(redirect);
return requestContext.HttpContext.Handler;
}
This gets called, but the rewritepath doesn't do anything ( the error says no IHttpHandler was returned ) and the TransferRequest works, but the URL changes in the browser. The code also uses relative paths for images, etc, and these do not load ( not sure if I can actually fix this ).
The answer is
return BuildManager.CreateInstanceFromVirtualPath(URl, typeof(Page)) as Page;
That's how you return a redirect from a routehandler. Sadly, it won't work for me, the browser can't work out to redirect the requests within our web app, and it doesn't make requests to a full URL, which means I can't detect them to remap them.

Categories