1

I am trying to implement an OAuth flow and in my Services for my client I have the following lines of code:

services.AddAuthentication(config => {
            // We check the cookie to confirm that we are authenticated
            config.DefaultAuthenticateScheme = "ClientCookie";
            // When we sign in we will deal out a cookie
            config.DefaultSignInScheme = "ClientCookie";
            // use this to check if we are allowed to do something.
            config.DefaultChallengeScheme = "OurServer";
        })
            .AddCookie("ClientCookie")
            .AddOAuth("OurServer", config => {
                config.ClientId = "client_id";
                config.ClientSecret = "client_secret";
                config.CallbackPath = "/oauth/callback";
                config.AuthorizationEndpoint = "https://localhost:44360/oauth/authorize";
                config.TokenEndpoint = "https://localhost:44360/oauth/token";
                config.SaveTokens = true;

                config.Events = new OAuthEvents()
                {
                    OnCreatingTicket = context =>
                    {
                        var accessToken = context.AccessToken;
                        var base64payload = accessToken.Split('.')[1];
                        var modified = base64payload.Replace(" ", "");
                        var bytes = Convert.FromBase64String(base64payload);
                        var jsonPayload = Encoding.UTF8.GetString(bytes);
                        var claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonPayload);

                        foreach (var claim in claims)
                        {
                            context.Identity.AddClaim(new Claim(claim.Key, claim.Value));
                        }

                        return Task.CompletedTask;
                    }
                };
            });

When it goes to try and convert the string from its base64 representation it throws a: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

This confuses me as I am following a tutorial on YouTube that, as far as I can tell from following it and looking at the github code is IDENTICAL. The supposedly invalid Base64 string is:

eyJzdWIiOiJzb21lX2lkIiwiR3Jhbm55IjoiQ29va2llIiwibmJmIjoxNjEwMTYwMjE5LCJleHAiOjE2MTAyNDY2MTksImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNjAvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM2MC8ifQ

This is odd as checking it on https://www.base64decode.org/ properly returns the following content:

{"sub":"some_id","Granny":"Cookie","nbf":1610160219,"exp":1610246619,"iss":"http://localhost:44360/","aud":"http://localhost:44360/"}

Which appears to be valid as it matches the claims and content of the JWT token i've set in my controller on the Server side. What exactly am I doing wrong? I've checked other places that deal with issues of tokens, but I don't see any invalid characters there such as -, +, etc.

EDIT:

The fix for it was to do the following, thanks to LinkedListT for mentioning that it isn't a multiple a 4.

private string AddPaddingToBase64(string base64)
        {
            if(base64.Length % 4 == 0) 
                return base64;
            else
            {
                var builder = new StringBuilder(base64);
                builder.Append("=");
                return AddPaddingToBase64(builder.ToString());
            } 

        }

Then where we parse the string first have it go through that method and then pass the modified base64 to the converter. This is also the first time in 3+ years of professional development where recursion is actually simpler and more straight forward than iterative.

SomeStudent
  • 2,856
  • 1
  • 22
  • 36
  • *I don't see any invalid characters there such as -, +, etc* - `-` is valid in Base64, `+` is valid in Base64Url encoding. The real reason for your problerm is, that JWT uses Base64Url encoding and not Base64.See [my answer here](https://stackoverflow.com/questions/63597137/verify-signature-of-jwt-token-c-sharp/63597479#63597479) for details. And therefore it's also not sufficient to just add padding, as there are also two character substitutions. – jps Jan 09 '21 at 09:59

1 Answers1

2

The problem is your base64 string isn't valid.

The length of a base64 encoded string is always a multiple of 4. If it is not a multiple of 4, then = characters are appended until it is. A query string of the form ?name=value has problems when the value contains = characters (some of them will be dropped, I don't recall the exact behavior). You may be able to get away with appending the right number of = characters before doing the base64 decode. https://stackoverflow.com/a/2925959/9936356

So you need to append '==' to the base64 string in question. I.e.

Convert.FromBase64String("eyJzdWIiOiJzb21lX2lkIiwiR3Jhbm55IjoiQ29va2llIiwibmJmIjoxNjEwMTYwMjE5LCJleHAiOjE2MTAyNDY2MTksImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNjAvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM2MC8ifQ==");
LinkedListT
  • 681
  • 8
  • 24
  • 1
    This indeed was the case, I figured it out after reading more about base64. I suppose the question is how come that it didn't properly convert to a base64. Oh well, fix is easy. I will edit my answer with the fix and mark yours as correct. I wish C#s converter was intelligent enough to just append "=" to it to make it valid as "=" is meaningless (at the end) in base64 other than for length from my understanding – SomeStudent Jan 09 '21 at 03:11