How to build a Url? - c#

Are there any helper classes available in .NET to allow me to build a Url?
For example, if a user enters a string:
stackoverflow.com
and i try to pass that to an HttpWebRequest:
WebRequest.CreateHttp(url);
It will fail, because it is not a valid url (it has no prefix).
What i want is to be able to parse the partial url the user entered:
Uri uri = new Uri(url);
and then fix the missing pieces:
if (uri.Port == 0)
uri.Port = 3333;
if (uri.Scheme == "")
uri.Scheme = "https";
Does .NET have any classes that can be used to parse and manipulate Uri's?
The UriBuilder class can't do the job
The value that the user entered (e.g. stackoverflow.com:3333) is valid; i just need a class to pick it apart. i tried using the UriBuilder class:
UriBuilder uriBuilder = new UriBuilder("stackoverflow.com:3333");
unfortunately, the UriBuilder class is unable to handle URIs:
uriBuilder.Path = 3333
uriBuilder.Port = -1
uriBuidler.Scheme = stackoverflow.com
So i need a class that can understand host:port, which especially becomes important when it's not particularly http, but could be.
Bonus Chatter
Console application.
From the other question
Some examples of URL's that require parsing:
server:8088
server:8088/func1
server:8088/func1/SubFunc1
http://server
http://server/func1
http://server/func/SubFunc1
http://server:8088
http://server:8088/func1
http://server:8088/func1/SubFunc1
magnet://server
magnet://server/func1
magnet://server/func/SubFunc1
magnet://server:8088
magnet://server:8088/func1
magnet://server:8088/func1/SubFunc1
http://[2001:db8::1]
http://[2001:db8::1]:80
The format of a Url is:
foo://example.com:8042/over/there?name=ferret#nose
\_/ \_________/ \__/\_________/\__________/ \__/
| | | | | |
scheme host port path query fragment
Bonus Chatter
Just to point out again that UriBuilder does not work:
https://dotnetfiddle.net/s66kdZ

If you need to ensure that some string coming as user input is valid url you could use the Uri.TryCreate method:
Uri uri;
string someUrl = ...
if (!Uri.TryCreate(someUrl, UriKind.Absolute, out uri))
{
// the someUrl string did not contain a valid url
// inform your users about that
}
else
{
var request = WebRequest.Create(uri);
// ... safely proceed with executing the request
}
Now if on the other hand you want to be building urls in .NET there's the UriBuilder class specifically designed for that purpose. Let's take an example. Suppose you wanted to build the following url: http://example.com/path?foo=bar&baz=bazinga#some_fragment where the bar and bazinga values are coming from the user:
string foo = ... coming from user input
string baz = ... coming from user input
var uriBuilder = new UriBuilder("http://example.com/path");
var parameters = HttpUtility.ParseQueryString(string.Empty);
parameters["foo"] = foo;
parameters["baz"] = baz;
uriBuilder.Query = parameters.ToString();
uriBuilder.Fragment = "some_fragment";
Uri finalUrl = uriBuilder.Uri;
var request = WebRequest.Create(finalUrl);
... safely proceed with executing the request

You can use the UriBuilder class.
var builder = new UriBuilder(url);
builder.Port = 3333
builder.Scheme = "https";
var result = builder.Uri;

To be valid a URI needs to have the scheme component. "server:8088" is not a valid URI. "http://server:8088" is. See https://www.rfc-editor.org/rfc/rfc3986

Related

ASP.Net Core 3.1 Identity - Generating Password Reset Token Issue

I am developing a site where the users will be able to click a "Forgot My Password" button to reset their passwords.
Currently, once the email has been validated, the following code should generate a token to be emailed to the user:
if(validUser != null)
{
var generationTime = DateTime.Now;
var pwToken = await _userManager.GeneratePasswordResetTokenAsync(validUser);
await _userManager.UpdateAsync(validUser);
var url = $"https://{Request.Host}/verify/{HttpUtility.UrlEncode(pwToken)}";
//EmailHelper.SendMagicLinkEmail(validUser, url, Request);
return new RedirectResult("/");
}
All information online regarding this seems to suggest that this is the way to do things. I have set up the Default token providers in the Startup.csfile too:
identityOptions: o => {
o.User.RequireUniqueEmail = true;
o.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultProvider;
o.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultProvider;
},
Yet when a token is generated it produces a large token such as this:
CfDJ8CnvAYtZf+1IjXpKUM7+umDYEaImg2SPFglPX3Y8RmYpEfg5zpK8xL54lvlbJUd54CaIzzYlff/GU+xKKS8mmG5UdC1zdk24nOsJNpIlmC3P5V72BchS4P9DGFTR77XiKbMAAYymnMomS2zCdTKh+E4bn9RI6FVinMecG1HR7nSHmOI2McbXHBFTanI/0uwxH5WI/Dj4AFTBP39ni7mfKkeWz2nJ5pTemELJJ6pYP50+
The problem here is obviously the forward slashes, which cause issues with routing so are encoded out here:
var url = $"https://{Request.Host}/verify/{HttpUtility.UrlEncode(pwToken)}";
The problem is that even with that, .Net Core seems to un-encode it and produce the following error when the generated link is accessed:
This error isn't necessarily the issue, and I do understand it's importance. Yet I can't seem to find any explanation as to why this token is behaving this way. All online examples seem to produce a fairly standard GUID style token, not something such as this.
Does anyone know why this might be happening?
Cheers
You may want to try the Url.Action() method:
Example:
var token = userManager.GeneratePasswordResetTokenAsync(user).Result;
var resetLink = Url.Action("ResetPassword","Account", new { token = token }, protocol: HttpContext.Request.Scheme);
var message = "Click here to reset your password";
//Then send your message to the user
Note in the example above the email must be HTML for the link to work
The token looks fairly normal to me.
I think the URL encoding method you'd want to use is Uri.EscapeDataString. What I've personally done is using a UriBuilder and escaped the query string values (in this case for email confirmation):
var uriBuilder = new UriBuilder
{
Scheme = "https",
Host = "my.website.com",
Path = "/confirmEmail",
Query = $"email={Uri.EscapeDataString(email)}&token={Uri.EscapeDataString(token)}"
};
var fullUrl = uriBuilder.Uri.AbsoluteUri;
For you that'd be:
var uriBuilder = new UriBuilder
{
Scheme = "https",
Host = Request.Host,
Path = $"/verify/{Uri.EscapeDataString(pwToken)}"
};
var fullUrl = uriBuilder.Uri.AbsoluteUri;

Separating a part of the current URL

I want Separating a part of the current URL
Example:localhost:50981/Admin/AddCustomer.aspx
The part I want: AddCustomer
or
Example:localhost:50981/Request/Customer.aspx
The part I want: Customer
You can use AbsolutePath in your onLoad function of page.
//AddCustomer or Customer
string yourPath = HttpContext.Current.Request.Url.AbsolutePath.Split('/').Last().Split('.')[0];
You can make use of string.Split():
var url = "localhost:50981/Admin/AddCustomer.aspx";
var result = url.Split('/').Last().Split('.')[0];
To get the current Url path in Asp.Net:
var url = HttpContext.Current.Request.Url.AbsolutePath;
Note:
If you are interested in how to get the different parts of an url have a look at this answer:
var scheme = Request.Url.Scheme; // will get http, https, etc.
var host = Request.Url.Host; // will get www.mywebsite.com
var port = Request.Url.Port; // will get the port
var path = Request.Url.AbsolutePath; // should get the /pages/page1.aspx part, can't remember if it only get pages/page1.aspx

How to create Uri without fragmentation (convert # to %23)

I am trying to do advanced searches by using the query string but when I include # it doesn't get converted to %23 when creating a uri.
var webAddress = "www.worldwideweb.com/abc#d#e";
var uri = new Uri(webAddress).AbsoluteUri;
When I do that, an exception is thrown.
When I only include a # symbol it fragments it instead. Like in this example
var webAddress = "https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [java]"
var uri = new Uri(webAddress).AbsoluteUri;
Uri now equals
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#]%20OR%20[java]
How do I make
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [f#]
into
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c%23]%20OR%20[f%23]
I'm using .Net Framework 4.6.2
My approach is to use the an UriBuilder and a Dictionary for each Query parameter. You can then UrlEncode the value from each parameter so you get a valid Url.
This is what your code would look like:
var ub = new UriBuilder("https", "api.stackexchange.com");
ub.Path = "/2.2/search/advanced";
// query string parameters
var query = new Dictionary<string,string> ();
query.Add("site", "stackoverflow");
query.Add("q", "[c#] OR [f#]");
query.Add("filter", "!.UE46gEJXV)W0GSb");
query.Add("page","1");
query.Add("pagesize","2");
// iterate over each dictionary item and UrlEncode the value
ub.Query = String.Join("&",
query.Select(kv => kv.Key + "=" + WebUtility.UrlEncode(kv.Value)));
var wc = new MyWebClient();
wc.DownloadString(ub.Uri.AbsoluteUri).Dump("result");
This will result in this Url in ub.Uri.AbsoluteUri:
https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=%5Bc%23%5D+OR+%5Bf%23%5D&filter=!.UE46gEJXV)W0GSb&page=1&pagesize=2
As the StackAPI returns the content zipped, use AutomaticDecompression on an subclassed WebClient (as shown here by feroze):
class MyWebClient:WebClient
{
protected override WebRequest GetWebRequest(Uri uri)
{
var wr = base.GetWebRequest(uri) as HttpWebRequest;
wr.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
return wr;
}
}
which, when combined with the other code, generates output for me:
{
"items" : [{
"tags" : ["c#", "asp.net-mvc", "iis", "web-config"],
"last_activity_date" : 1503056272,
"question_id" : 45712096,
"link" : "https://stackoverflow.com/questions/45712096/can-not-read-web-config-file",
"title" : "Can not read web.config file"
}, {
"tags" : ["c#", "xaml", "uwp", "narrator"],
"last_activity_date" : 1503056264,
"question_id" : 45753140,
"link" : "https://stackoverflow.com/questions/45753140/narrator-scan-mode-for-textblock-the-narrator-reads-the-text-properties-twice",
"title" : "Narrator. Scan mode. For TextBlock the narrator reads the Text properties twice"
}
]
}
If # only exists in query part, you can simply do this:
var webAddress = "https://api.stackexchange.com/2.2/search/advanced?site=stackoverflow&q=[c#] OR [java]"
var uri = new Uri(webAddress).AbsoluteUri;
var fixedUri = Regex.Replace(uri, "#", "%23");

How can I retrieve Basic Authentication credentials from the header?

I am trying to write some simple tests User Authentication mechanism which uses Basic Authentication. How can I retrieve the credentials from the header?
string authorizationHeader = this.HttpContext.Request.Headers["Authorization"];
Where do I go from here? There are several tutorials but I new to .NET and authentication, could you explain in your answer exactly step-by-step the what and why you are doing.
From my blog:
This will explain in detail how this all works:
Step 1 - Understanding Basic Authentication
Whenever you use Basic Authentication a header is added to HTTP Request and it will look similar to this:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Source: http://en.wikipedia.org/wiki/Basic_access_authentication
"QWxhZGRpbjpvcGVuIHNlc2FtZQ==" is just "username:password" encoded in Base64(http://en.wikipedia.org/wiki/Base64). In order to access headers and other HTTP properties in .NET (C#) you need to have access to the current Http Context:
HttpContext httpContext = HttpContext.Current;
This you can find in System.Web namespace.
Step 2 - Getting the Header
Authorization header isn't the only only one in the HttpContext. In order to access the header, we need to get it from the request.
string authHeader = this.httpContext.Request.Headers["Authorization"];
(Alternatively you may use AuthenticationHeaderValue.TryParse as suggested in pasx’s answer below)
If you debug your code you will see that the content of that header looks similar to this:
Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Step 3 - Checking the header
You've already extracted the header now there are several things you need to do:
Check that the header isn't null
Check that the Authorization/Authentication mechanism is indeed "Basic"
Like so:
if (authHeader != null && authHeader.StartsWith("Basic")) {
//Extract credentials
} else {
//Handle what happens if that isn't the case
throw new Exception("The authorization header is either empty or isn't Basic.");
}
Now you have check that you are have something to extract data from.
Step 4 - Extracting credentials
Removing "Basic " Substring
You can now attempt to get the values for username and password. Firstly you need to get rid of the "Basic " substring. You can do it like so:
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
See the following links for further details:
http://msdn.microsoft.com/en-us/library/system.string.substring(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/t97s7bs3(v=vs.110).aspx
Decoding Base64
Now we need to decode back from Base64 to string:
//the coding should be iso or you could use ASCII and UTF-8 decoder
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
Now username and password will be in this format:
username:password
Splitting Username:Password
In order to get username and password we can simply get the index of the ":"
int seperatorIndex = usernamePassword.IndexOf(':');
username = usernamePassword.Substring(0, seperatorIndex);
password = usernamePassword.Substring(seperatorIndex + 1);
Now you can use these data for testing.
The Final Code
The final code may look like this:
HttpContext httpContext = HttpContext.Current;
string authHeader = this.httpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic")) {
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
} else {
//Handle what happens if that isn't the case
throw new Exception("The authorization header is either empty or isn't Basic.");
}
Just adding to the main answer, the best way to get rid of the "Basic" substring is to use AuthenticationHeaderValue Class:
var header = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentials = header.Parameter;
It will throw a FormatException if the content of the header is not valid, e.g.: the "Basic" part is not present.
Alternatively if you do not want to have exception, use AuthenticationHeaderValue.TryParse
Awesome answer from #DawidO.
If you are just looking to extract the basic auth creds and rely on the .NET magic given you have HttpContext, this will also work:
public static void StartListener() {
using (var hl = new HttpListener()) {
hl.Prefixes.Add("http://+:8008/");
hl.AuthenticationSchemes = AuthenticationSchemes.Basic;
hl.Start();
Console.WriteLine("Listening...");
while (true) {
var hlc = hl.GetContext();
var hlbi = (HttpListenerBasicIdentity)hlc.User.Identity;
Console.WriteLine(hlbi.Name);
Console.WriteLine(hlbi.Password);
//TODO: validater user
//TODO: take action
}
}
}
Remember, using strings can be less secure. They will remain in memory untill they are picked by GC.

Absolute URL from base + relative URL in C#

I have a base URL :
http://my.server.com/folder/directory/sample
And a relative one :
../../other/path
How to get the absolute URL from this ? It's pretty straighforward using string manipulation, but I would like to do this in a secure way, using the Uri class or something similar.
It's for a standard a C# app, not an ASP.NET one.
var baseUri = new Uri("http://my.server.com/folder/directory/sample");
var absoluteUri = new Uri(baseUri,"../../other/path");
OR
Uri uri;
if ( Uri.TryCreate("http://base/","../relative", out uri) ) doSomething(uri);
Some might be looking for Javascript solution that would allow conversion of urls 'on the fly' when debugging
var absoluteUrl = function(href) {
var link = document.createElement("a");
link.href = href;
return link.href;
}
use like:
absoluteUrl("http://google.com")
http://google.com/
or
absoluteUrl("../../absolute")
http://stackoverflow.com/absolute

Categories