UriBuilder points to localhost instead of domain name - c#

My site has a public section that is accessed by http and a https part to requires logging in. When logging out of the site, it redirects to the http public index page.
Previously I had done this with stating the full url to point too. Recently I had to get rid of such things so the site can be run on numerous domains, for testing.
I tried using UriBuilder to convert https links to http link so that a website no longer has to use direct pointing to a specific url. This should allow the site to use any domain name. Right now it points to the computer name.
if (Request.IsSecureConnection)
{
UriBuilder ub = new UriBuilder();
ub.Path = "/html/index.html";
ub.Scheme = Uri.UriSchemeHttp;
ub.Port = -1; // use default port for scheme
Response.Redirect(ub.Uri.ToString(), true);
//An old direct link to the site
//Response.Redirect("http://www.someaddress.com/html/index.html");
}
When the code is triggered remotely on the test server instead of pointing to the right domain it returns me to the address
http://localhost/html/index.html
Instead of
http://testserver/html/index.html
I have no idea why it is doing this instead of returning the address I am connecting to the server via.

If you don't specify host than default host ("localhost") will be used - see UriBuilder() constructor article on MSDN.
Fix: specify host (probably based on incoming request's host).
ub.Host = GetMeIncomingHost();

Because in the URI to which you are redirecting, you haven't specified an authority (host). Thus, your redirect sends a 302 Found HTTP status and the response contains a location: header that looks like something like this:
location: /html/index.html
That is a relative URI, relative to the current URI from which the redirected request originated. That means it inherited the scheme and authority component of the requesting page (which, obviously, in your case, was an http://localhost:xx/....
To fix this, seed your UriBuilder in its constructor with HttpContext.Current.Request.Url. That should about do it:
UriBuilder ub = new UriBuilder( HttpContext.Current.Request.Url );
ub.Path = "/html/index.html";
Response.Redirect(ub.Uri.ToString(), true);

Related

How to maintain the right URL in C#/ASP.NET?

I am given a code and on one of its pages which shows a "search result" after showing different items, it allows user to click on one of records and it is expected to bring up a page so that specific selected record can be modified.
However, when it is trying to bring up the page I get (by IE) "This page cannot be displayed".
It is obvious the URL is wrong because first I see something http://www.Something.org/Search.aspx then it turns into http://localhost:61123/ProductPage.aspx
I did search in the code and found the following line which I think it is the cause. Now, question I have to ask:
What should I do to avoid using a static URL and make it dynamic so it always would be pointing to the right domain?
string url = string.Format("http://localhost:61123/ProductPage.aspx?BC={0}&From={1}", barCode, "Search");
Response.Redirect(url);
Thanks.
Use HttpContext.Current.Request.Url in your controller to see the URL. Url contains many things including Host which is what you're looking for.
By the way, if you're using the latest .Net 4.6+ you can create the string like so:
string url = $"{HttpContext.Current.Request.Url.Host}/ProductPage.aspx?BC={barCode}&From={"Search"}";
Or you can use string.Format
string host = HttpContext.Current.Request.Url.Host;
string url = string.Format("{0}/ProductPage.aspx?BC={1}&From={2}"), host, barCode, "Search";
You can store the Host segment in your AppSettings section of your Web.Config file (per config / environment like so)
Debug / Development Web.Config
Production / Release Web.Config (with config override to replace the localhost value with something.org host)
and then use it in your code like so.
// Creates a URI using the HostUrlSegment set in the current web.config
Uri hostUri = new Uri(ConfigurationManager.AppSettings.Get("HostUrlSegment"));
// does something like Path.Combine(..) to construct a proper Url with the hostName
// and the other url segments. The $ is a new C# construct to do string interpolation
// (makes for readable code)
Uri fullUri = new Uri(hostUri, $"ProductPage.aspx?BC={barCode}&From=Search");
// fullUrl.AbosoluteUri will contain the proper Url
Response.Redirect(fullUri.AbsoluteUri);
The Uri class has a lot of useful properties and methods to give you Relative Url, AbsoluteUrl, your Url Fragments, Host name etc etc.
This should do it.
string url = string.Format("ProductPage.aspx?BC={0}&From={1}", barCode, "Search");
Response.Redirect(url);
If you are using .Net 4.6+ you can also use this string interpolation version
string url = $"ProductPage.aspx?BC={barcode}&From=Search";
Response.Redirect(url);
You should just be able to omit the hostname to stay on the current domain.

WebAPI Basic Authentication Authorization Filter and machine keys

I'm using [WebAPI Basic Authentication Authorization Filter] to authorize users on consuming my simple web api data service.
Question is: everything work on localhost but when I publish my service to the web (http://www.mywebsite.com) client app always gets "Unauthorize" response.
This line is all I change (on client side) when switching from localhost to the web
//client.BaseAddress = new Uri("http://localhost:11992/"); // on localhost work
client.BaseAddress = new Uri("http://mywebsite.com/"); // returns 401 Unauthorized
Tried with adding machine key using remote IIS manager but same thing happens.
reference this machine key in system.web (web.config)
and authentication mode on IIS is as follows
Still doesnt work. Obviously I'm missing something here.
UPDATE
I'm extending basic auth. filter which I later apply on my controller action (the one I access from client side)
[MyBasicAuthenticationFilter]
public class DataController : RavenController { ... }
and inside this custom auth. filter there is hardcoded username and pass
public class MyBasicAuthenticationFilter : BasicAuthenticationFilter
{
public MyBasicAuthenticationFilter()
{ }
public MyBasicAuthenticationFilter(bool active)
: base(active)
{ }
protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
if (username == "myuser" && password == "mypass")
return true;
else
return false;
}
}
and from client side (wpf client)
client.BaseAddress = new Uri("http://mywebsite.com/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "myuser", "mypass"))));
I'm confused because same code work fine on localhost but when I publish code and change this client.BaseAddress to my actual website url it returns 401 error.
The BasicAuthenticationFilter class given Rick Strahl's Web Log uses the System's default encoding to extract the user credentials from the authorization header. As per the MSDN documentation for the Encoding.Default property:
Different computers can use different encodings as the default, and
the default encoding can even change on a single computer. Therefore,
data streamed from one computer to another or even retrieved at
different times on the same computer might be translated incorrectly.
In addition, the encoding returned by the Default property uses
best-fit fallback to map unsupported characters to characters
supported by the code page. For these two reasons, using the default
encoding is generally not recommended. To ensure that encoded bytes
are decoded properly, you should use a Unicode encoding, such as
UTF8Encoding or UnicodeEncoding, with a preamble. Another option is to
use a higher-level protocol to ensure that the same format is used for
encoding and decoding.
Based on the information provided, this is the mostly likely line of code which can potentially give different results based on the configuration of the server your code is deployed to.
I would therefore consider replacing the following line in the ParseAuthorizationHeader method of the BasicAuthenticationFilter class:
authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));
with the following code:
Encoding iso88591 = Encoding.GetEncoding("iso-8859-1");
authHeader = iso88591.GetString(Convert.FromBase64String(authHeader));
iso-8859-1 will give you best compatibility with all clients that send basic authentication header. See the following StackOverflow question on the recommend encoding to use when doing HTTP Basic Authentication: What encoding should I use for HTTP Basic Authentication?
Alternatively if you're the only one who will be calling the API from your WCF client, then just make sure you're encoding the username and password in the same character code as the server will be decoding it in.

Url works when pinging but causes UriFormatException

I have a mail server I'm trying to connect to with exchange web services. If I ping the server, it works, but it gets a UriFormatException when provided in code.
Urls that work in command prompt but fail in c#
myserver.mydomain.com
myserver
192.168.100.1 (my server's ip)
Urls that can be parsed into URIs but fail to be pinged
http://myserver.mydomain.com
http://192.168.100.1
I also tried adding \\ to the beginning but had no luck.
We do have a bit of a weird setup with connecting to our domain when on-network that I believe is what is causing http://myserver.mydomain.com to fail in ping. How can I turn the base url (without the http://) into a string that will be valid for a c# Uri?
Code:
var serverUrl = "myserver.mydomain.com"; //base string I'd like to use
_exchange.Url = new Uri(serverUrl); //causes UriFormatException: Invalid URI: The format of the URI could not be determined.
To consturct Uri from host name use UriBuilder:
var builder = new UriBuilder();
builder.Host = "myserver.mydomain.com";
var uri = builder.Uri;
Note that what you call "uri" (myserver.mydomain.com) is actually "host name" or "DNS name" which is what get resolved to IP and than used to Ping. "http://myserver.mydomain.com" is absolute Uri for particular protocol (HTTP).

Create cookie with cross domain

I am working on cookies. I am able to create cookies very easily. To create a cookie I am using this code:
HttpCookie aCookie = new HttpCookie("Cookie name");
aCookie.Value = "Value";
Response.Cookies.Add(aCookie);
This code is fine for me and it gives me localhost as Host. But the problem comes here when I try to add a domain name here like:
HttpCookie aCookie = new HttpCookie("Cookie name");
aCookie.Value = "Value";
aCookie.Domain = "192.168.0.11";
Response.Cookies.Add(aCookie);
Now the cookie is not generated. Any suggestions?
You can only set the domain to yourself (the current site) and sub-domains of yourself, for security reasons. You can't set cookies for arbitrary sites.
As Marc has said - you can't do this; unless the domain is a subdomain of the one returning the response.
The same limitation applies to javascript code on the client adding cookies as well - the same origin policy will apply.
A simple way to achieve this is generally to include on the page returned from abc.com somewhere a reference to a resource on the domain xyz.com - typically a javascript file or something like that.
You have to watch out there, though, because that's a third-party cookie and some users will have those disabled (because it's how ad-tracking works).
Assuming you have a known set of cookies you want to track across domains and that you own all the domains you are sharing cookies with, you can build this functionality yourself. Here is poor man's cross-domain cookie tracking:
You can add "?favoriteColor=red" to all links on abc.com that point to xyz.com.
XYZ Contact
Then do the same thing for all links on xyz.com that point to abc.com.
ABC Contact
Now on every page of abc.com and xyz.com need to check the http request path for ?favoriteColor=red and if it exists, set the favoriteColor cookie on that domain to red.
// Pseudocode
if(queryString["favoriteColor"] != null) {
setCookie("favoriteColor", queryString["favoriteColor"]);
}
Tip 1: Do some validation to ensure that the value you get is valid because users can enter anything.
Tip 2: You should be using https if you're going to do this.
Tip 3: Be sure to url escape your cookie name and value in the url.

How do I force a relative URI to use https?

I have a relative URI:
Uri U = new Uri("../Services/Authenticated/VariationsService.svc",
UriKind.Relative);
The problem is that depending on whether the user has typed https:// or http:// into their web browser to get to the silverlight application, it may use either http or https when trying to contact the service.
I want to force the program to use https for connecting to the service eitherway.
Initially I tried this:
Uri U = new Uri("../Services/Authenticated/VariationsService.svc",
UriKind.Relative);
string NU = U.AbsoluteUri;
U = new Uri(NU.Replace("http://", "https://"), UriKind.Absolute);
But it fails at U.AbsoluteUri because it can't convert the relative Uri into an absolute Uri at that stage. So how do I change the Uri Scheme to https?
The relative path has to be converted to absolute first. I do that using the Uri of the excuting Silverlight XAP file.
There might be ways to reduce this a bit (it feels wrong doing string operations with Uris) but it's a start:
// Get the executing XAP Uri
var appUri = App.Current.Host.Source;
// Remove the XAP filename
var appPath = appUri.AbsolutePath.Substring(0, appUri.AbsolutePath.LastIndexOf('/'));
// Construct the required absolute path
var rootPath = string.Format("https://{0}{1}", appUri.DnsSafeHost, appUri.AbsolutePath);
// Make the relative target Uri absolute (relative to the target Uri)
var uri = new Uri(new Uri(rootPath), "../Services/Authenticated/VariationsService.svc");
This does not include transferring a portnumber (which you might want to do in other circumstance). Personally I would put the above code in a helper method that also handles the port (and whatever you want to do differently when running localhost).
Hope this helps.
Instead, you should change your ASPX file which hosts your silverlight, and force user to redirect to SSL only if he/she is logged in using non SSL url. Because ideally it would be perfect if silverlight opens connection only to the same domain and scheme it loaded from.
The protocol is a separate component, so I think that you can just put it in front of your relative address:
Uri U = new Uri("https:../Services/Authenticated/VariationsService.svc", UriKind.Relative);
"Scheme" does not have any meaning in a relative URI. You'll have to convert it to an absolute URI at some point to change the scheme.

Categories