This project has moved. For the latest updates, please go here.

OAuth 2 HTTP Basic authentication - credentials decoding

Sep 9, 2016 at 2:13 PM
Edited Sep 9, 2016 at 2:50 PM
My application uses Client Credentials Grant flow. The application supports both HTTP Basic authentication scheme and client credentials in the request-body scheme. To get credentials I use OAuthValidateClientAuthenticationContext.TryGetFormCredentials and OAuthValidateClientAuthenticationContext.TryGetBasicCredentials methods. Something like this :
string clientId;
string clientSecret;
if (context.TryGetFormCredentials(out clientId, out clientSecret) || context.TryGetBasicCredentials(out clientId, out clientSecret))
{
// validate here clientId and secret
}
I have an issue when I use POSTMAN to authenticate. POSTMAN uses Basic authentication flow. My password contains special characters like '+' or sign (valid base 64 characters). When POSTMAN sends the credentials it first escapes them then transforms to Base 64 string. So when context.TryGetBasicCredentials method is invoked it returns clientSecret set to "123%2Babc%2Fg" instead of "123+abc/g". To fix the issue I have to call Uri.UnescapeDataString. When credentials are sent in request body then decoding happens inside TryGetFormCredentials method and credentials are returned with expected values. The problem would not arise if POSTMAN hadn't escape the characters before encoding them to Base 64. My question what is should be escaped characters before converting them to base 64 or not? According to RFC6749 credentials should always be encoded :
   Clients in possession of a client password MAY use the HTTP Basic
   authentication scheme as defined in [RFC2617] to authenticate with
   the authorization server.  The client identifier is encoded using the
   "application/x-www-form-urlencoded" encoding algorithm per
   Appendix B, and the encoded value is used as the username; the client
   password is encoded using the same algorithm and used as the
   password....
For me it seems that POSTMAN is working according to specification. So I think OAuthValidateClientAuthenticationContext.TryGetBasicCredentials should unescape credentials to work as expected because OAuthValidateClientAuthenticationContext.TryGetFormCredentials method unescapes them. Now in order to fix the issue I've added Uri.UnescapeDataString and it seems to work fine:
string clientId;
 string clientSecret;
if (context.TryGetFormCredentials(out clientId, out clientSecret) || context.TryGetBasicCredentials(out clientId, out clientSecret))
{
     clientId = Uri.UnescapeDataString(clientId);
     clientSecret = Uri.UnescapeDataString(clientSecret);
    // validation staff goes here....
}
I think TryGetBasicCredentials method should look similar to this:
public bool TryGetBasicCredentials(out string clientId, out string clientSecret)
        {
            // some code is removed
            {
                try
                {
                    byte[] data = Convert.FromBase64String(authorization.Substring("Basic ".Length).Trim());
                    string text = Encoding.UTF8.GetString(data);
                    int delimiterIndex = text.IndexOf(':');
                    if (delimiterIndex >= 0)
                    {
                       clientId = Uri.UnescapeDataString(text.Substring(0, delimiterIndex));
                        clientSecret = Uri.UnescapeDataString(text.Substring(delimiterIndex + 1));
// the rest is not thanged
I think that there is not point escaping characters if they are encoded in base 64 but I understand specification as it should be always done and POSTMAN seems to work in this way.