I am working on an application that uses Angular.js and ASP.NET MVC. The routes for angular.js contain a '#'. Some pages in the application can be viewed without needing to log in. These pages however contain URL's to pages in the application that need the user to be logged in. They look like:
Want this voucher?
So when these links are clicked, MVC forms authentication redirects to the sign in page with the return URL query string that looks like this:
http://localhost:18030/signin?ReturnUrl=%2f#/shop/voucher/6bc1
So on the sign in pages, when i try to look at the query string I only get '/' for return URL as that is the %2f part. I lose the remaining value because of the #.
If I try to URL encode the link, the # and other '/' characters do get encoded but then the routing gets messed up and I get 404 errors.
Is there anyway to access the full query string value with the '#' in it? I am not sure why forms authentication does not encode the #.
The url fragment is never sent to the server, so you have to get a little creative here.
One way I have solved this in the past is to use client side code to modify the action URL for the login, and append the fragment as a query string parameter.
//Pulls out everything past the '#'
var fragment = $location.path();
$scope.fragmentParam = "&fragment=" + fragment;
You can append that query string param to your form action, and then handle it on the server side in order to redirect the user appropriately.
public ActionResult Login(MyLoginInfo info, string returnUrl, string fragment){
//Normal login code
var redirectUrl = String.Format("{0}#{1}", returnUrl, fragment);
Redirect(redirectUrl);
}
This is a simplified version, but you should be able to get the general idea. You need to pass that information to the server, and then reconstruct a redirect url that includes the fragment.
Related
I am working on an application with ASP.NET MVC Routing + AngularJS routing.
My URL lookslike:
https://example.com/Request/#/Search/Request/123
when I breakdown this (http://example.com/Request) is handled by ASP.NET MVC routing. i.e. (Area = Request, controller = "Default", action = "Index")
(#/Search/Request/123) is handled by AngularJS routing.
This works perfectly when I am on http://localhost:8080/
The issue is when I deploy this application to https://example.com/
In this case, If user clicks on above link (received via email),IE 9 recognizes only (https://example.com/Request/") and the server never gets (#/Search/Request/123).
We have enterprise SSO implemented on web server. SSO client intercepts http request and uses URL to redirect back to requested page after authentication.
if # fragment is not sent as part of http request url, sso is not able to redirect back to same page.
I believe this to be a common scenario/issue. I would keep changing the URL scheme as last resort. e.g. (# to !).
How to solve this?
Just found a blog that dealt with this issue exactly:
http://codetunnel.io/how-to-persist-url-hash-fragments-across-a-login-redirect/
He offers two ideas:
When the page loads there simply needs to be some JavaScript that accesses the hash fragment and appends it to the redirect URL in the hidden field. Here's an example using JQuery for simplicity
$(function () {
var $redirect = $('[name="redirect"]');
$redirect.val($redirect.val() + window.location.hash);
});
Or, alternatively
Instead of appending the hash fragment to the hidden field value, you could avoid sending it to the server at all and simply append it to the form action URL.
$(function () {
var $loginForm = $('#loginForm');
var actionUrl = $loginForm.attr('action');
$loginForm.attr('action', actionUrl + window.location.hash);
});
Fragments (the part of the URL after the #) are not necessarily sent to the server-side by the browser. They are for client-side usage only (navigating to a specific location in the document, JavaScript support).
RFC 2396 section 4.1:
When a URI reference is used to perform a retrieval action on the
identified resource, the optional fragment identifier, separated from
the URI by a crosshatch ("#") character, consists of additional
reference information to be interpreted by the user agent after the
retrieval action has been successfully completed. As such, it is not
part of a URI, but is often used in conjunction with a URI.
(emphasis added)
Therefore, the URL scheme you came up with will not work reliably unless you change the # to another character. Alternatively, you could use JavaScript to transfer the information from the fragment in an input that will be reliably passed back to the server. But do note that solution will only work if JavaScript is enabled in the browser, so it is (also) not a 100% reliable solution that will work with all clients.
Either way, using a URL without a fragment is a more reliable approach and IMO a better design choice if you expect that part to be interpreted by the server.
I would remove ugly URL's from your application all together.
This article will walk you through removing ugly URL's in a asp.net-mvc project. It will also ensure that you have your RouteConfig.cs setup correctly.
http://www.codeproject.com/Articles/806500/Getting-started-with-AngularJS-and-ASP-NET-MVC-P
I have a webform application that when you login takes me to the homepage of my MVC application. In the process of logging in, I am forming a url that takes me to my MVC app. In this URL I am attaching an encrypted User ID. The problem is when this encrypted User Id contains special character such as "+" or "/". It messes up the and takes me to a 404 page. If there are no special characters then it works fine and takes me to the right page.
This how I form the URL
Response.Redirect("http://mysite/UserRedirect/" + encryptedUserId)
MVC method
[HttpGet]
public ActionResult UserRedirect(string id = "")
{
//Decrypts UserID, then takes you to the homepage
return("Index");
}
If the encryptedUserId contains special characters like the example below it messes up.
7hnQ+Nc= -> http://mysite/UserRedirect/7hnQ+Nc= ("+" sign)
or
E/VDb7l= -> http://mysite/UserRedirect/E/VDb71 ("/" sign)
Otherwise if there are no special characters like below, then it works fine
NIatmgg= -> http://mysite/UserRedirect/NIatmgg=
This issue is occuring when the app is deployed on the server. How should I approach this?
When there is a "/" it's not even getting to my UserRedirect method.
Thanks
turns out you need to form the url with a "?=" instead of a slash "/". This now works. Thanks
Are you making sure to encode (via UrlEncode) the encryptedUserId variable before the redirect call and decode (via UrlDecode) the id variable in the UserRedirect action?
My MVC web application generates an activation link that can include any character (%,+,/,etc.). I URL encode the string and generate link:
new UrlHelper(HttpContext.Current.Request.RequestContext)
.RouteUrl("AccountActivation",
new { id = HttpContext.Current.Server.UrlEncode(activationString) };
then add the domain and it looks like:
http://localhost/AccountActivation/asdlkj223%25asd%2Basw3fgasdme
The URL is then passed to the user.
The route in this case is:
routes.MapRoute(
"ActivateAccount",
"AccountActivation/{id}",
new { controller = "Account", action = "Activate", id = ""});
It seem fine to me, but the ASP.NET development server and IIS give me HTTP Error 400 - Bad request. Which means there's a problem with the URL that I can't see.
When get rid of the {id} in the route description (I also tried {*id} with no success):
routes.MapRoute(
"ActivateAccount",
"AccountActivation",
new { controller = "Account", action = "Activate"});
the URLs look like:
http://AccountActivation?id=asdlkj223%25asd%2Basw3fgasdme
and they work just fine...
I though those 2 approaches do exactly the same thing. What is the difference between them? Is it the MVC engine that performs something more for me or I miss something with the URL encoding.
Try UrlPathEncode instead of UrlEncode - some characters are illegal in the path that are legal in a query string.
That said - I believe the analysis of whether a character is 'bad' is performed after path-decoding occurs; and is done by IIS. It will reject some characters because of the possibility that the URL maps to the physical file-system and therefore could allow a user access to things they really shouldn't have access to. Equally it's done to prevent requests from sending data that really shouldn't be sent.
Generally if there's operationally no benefit from having a parameter mapped as a route parameter, then don't try too hard to map it - especially in this case where the string could be anything.
By the way - if that's an encoding of binary data; you can instead consider hex-encoding it or using modified base-64 for URLs instead - which will not cause errors if mapped as a route parameter.
Is there away to get the current fragment from a route that was issued via action link. This is how I am getting the action from the route.
string currentAction = requestContext.RouteData.Values["action"] as string ?? "index";
Can I do something similar to this?
string currentFragment = requestContext.RouteData.Values["Fragment"] as string ?? "";
No, you can't do anything like this. The fragment (everything that follows the # sign in an url) is never sent to the server by the browser, so the sole fact of talking about getting the url fragment server side simply doesn't make sense.
So if you have the following url: http://example.com/foo/bar?key1=value1#abc the server will never be able to fetch abc simply because the client will never send it.
As it has already been pointed out that is not possible. Document fragments (the string after the hash as you call it) are intended for the browsers only to correctly position the viewport. They have no meaning for the server and therefore are not transmitted there.
There is however a workaround you can use. Repeat the fragment as part of your url to make it accessible for the server.
Look at the permalink to the answers in this question. For instance, the link to my answer looks like this:
http://stackoverflow.com/questions
/6285833/get-current-fragment-in-route-asp-net-mvc/6286097#6286097
See how the value 6286097 is duplicated as the last route parameter. It's intentional. You can use this technique as well.
P.S. The fragment must point to an identifier in the document (id of some HTML element). At least in XHTML only identifiers work as fragments. Valid ids may not begin with a digit therefore instead of #6286097 use something like #answer-6286097.
P.S.#2. Do not use any JavaScript trickery to get around this limitation. Basic site functionality and design must work without JavaScript - don't listen to anyone who tells you otherwise. Fragments obviously belong to the basic tool box. Use JavaScript only for advanced interactivity.
I have a workaround for you, but first of all lets get more into the problem.
The strings after the hash symbol which are called Fragment values are not query parameters but they are strings to be read by the client-side (living in the browser) and the server cannot read them because they are not sent to the server by the browser.
Some authentication providers like Google and Azure send the access token as Fragment value for security reasons so that they are not transferred over the internet after they get sent as direct response from the authentication provider.
The only way you can come around that is to use javascript to convert the fragment values to query parameters by replacing the '#' with '?' and redirecting to the endpoint in your server controller.
I suppose the easiest way is to handle all that from server, meaning you get get the request in server, send a javascript code to the browser on the fly, that replaces the '#' into '?' and redirects to your second endpoint which reads the token as strong parameter.
Here how you can do it in ASP.NET Core 3.1:
[AllowAnonymous]
[HttpGet("authredirect")]
[Produces("text/html")]
public virtual ContentResult ConvertUrlFragmentToQueryParamThenRedirect()
{
return Content("<html><script>window.location.href=window.location.href.replace('#', '?').replace('authredirect', 'authparams')</script></html>", "text/html");
}
[AllowAnonymous]
[HttpGet("authparams")]
public virtual void GetAccessToken([FromQuery] string access_token)
{
// now you have your access token server side here
}
Please remember to set your redirectUrl to the correct one, in this case 'YOUR REDIRECT URL/authredirect'.
When the characters %20 appears in between paramaters a url, my MVC routing stops considering that a string.
Why is that, and how can I approach handling "%20" characters in my URL?
Example URL
http://localhost:40494/ListContents/Delete/asdf%20/5430f394...
public ActionResult Delete(string DNSName, Guid id)
{...}
routes.MapRoute(
"Delete", // Route name
"ListContents/Delete/{DNSName}/{id}", // URL with parameters
new { controller = "ListContents", action = "Delete" } // Parameter defaults
);
However
Both the following URLs work fine
http://localhost:40494/ListContents/Delete/asdf%20SOMETHING_HERE/5430f394...
http://localhost:40494/ListContents/Delete/%20asdf/5430f394-946c-4f82-ac13-9d5efafe9127
If an empty space is at the end of any section of the URL before the next slash, it throws a HttpException in the System.Web.Util.FileUtil.CheckSuspiciousPhysicalPath() method which is handled by MVC and you'll get a HTTP 404 response.
You can verify that yourself by checking the checkbox for Throw in:
Visual Studio
Debug
Exceptions
Common Language Runtime Exceptions
Generally you should not have empty spaces in your URLs. I personally format my urls, that all spaces becomes a dash (-).
I think the problem is that in the example where it doesn't work is because it can't be parsed as a valid URL, it will be read as
http://localhost:40494/ListContents/Delete/asdf /5430f394...
Instead, you would be safe to just remove the %20 from that url safely.
Check if the id field of the table isn't a string (nchar(x)). If so, check if the respective id has the exact lenghth defined in the type declaration. If not (if it has less chars), that's the problem (it should have the EXACT lenghth you declared). This worked for me.