How can I retrieve Basic Authentication credentials from the header?
Asked Answered
B

3

90

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.

Bergamot answered 15/9, 2014 at 19:40 Comment(0)
G
276

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:

  1. Check that the header isn't null
  2. 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:

  1. http://msdn.microsoft.com/en-us/library/system.string.substring(v=vs.110).aspx
  2. 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.");
}
Gut answered 15/9, 2014 at 19:41 Comment(2)
Perfect answer but note this solution won't work if username or password contains :Interjoin
IndexOf should pick the first occurrence of the character. Therefore, your username cannot contain a colon which is made clear by the RFC 7617: Furthermore, a user-id containing a colon character is invalid, as the first colon in a user-pass string separates user-id and password.Gut
G
25

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

Googol answered 7/11, 2019 at 9:58 Comment(0)
G
4

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
      }
    }
  }
Gleet answered 14/2, 2016 at 0:8 Comment(1)
Note with this approach you are stuck to decoding with the iso-8859-1 charset. If your data is actually encoded in utf-8 for example, you will get unexpected results.Twain

© 2022 - 2024 — McMap. All rights reserved.