ESI SSO Access Token Request (Receiving "400 Bad Request" Response)

I am attempting to write some crude C# code to get an ESI Access Token, but it’s returning a “400 Bad Request” message.
If anyone has thoughts on what I may be doing wrong, I’d appreciate the feedback.

Link to formatted code: https://pastebin.com/72qjGsbA
Code is also below.

Thanks.

using System;

using System.Net;

using System.Collections.Specialized;

using System.Web;

using System.Security.Cryptography;

using System.Diagnostics;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace GetWalletTransactions

{

    class Program

    {

        static void Main(string[] args)

        {

            // Based on "OAuth 2.0 for Mobile or Desktop Applications" from https://docs.esi.evetech.net/docs/sso/native_sso_flow.html

            const string clientId = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // Dummy Client ID

            string authorizeUrl = @"https://login.eveonline.com/v2/oauth/authorize/";

            const string callback = @"http://127.0.0.1:12500/callback/";

            // Prepare Code Challenge

            Random random = new Random();

            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

            string codeChallenge = new String(Enumerable.Repeat(chars, 32).Select(s => s[random.Next(s.Length)]).ToArray());

            string origEncodedCodeChallenge = urlSafeBase64Encode(codeChallenge);

            string encodedCodeChallenge = ComputeSha256Hash(origEncodedCodeChallenge);

            encodedCodeChallenge = urlSafeBase64Encode(encodedCodeChallenge);

            // Prepare Request for Authorization Code

            string[] queryStringParameters =

            {

                "response_type=code",

                "redirect_uri=" + HttpUtility.UrlEncode(callback),

                "client_id=" + clientId,

                "scope=" + HttpUtility.UrlEncode("esi-wallet.read_character_wallet.v1"),

                "code_challenge=" + encodedCodeChallenge,

                "code_challenge_method=S256",

                "state=" + HttpUtility.UrlEncode("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") // Dummy Secret

            };

            string queryParameters = String.Join("&", queryStringParameters);

            authorizeUrl += "?" + queryParameters;

            // Setup Listener for Authorization Code Response

            HttpListener listener = new HttpListener();

            listener.Prefixes.Add(callback);

            listener.Start();

            Console.Write("Listening...");

            // Send Request for Authorization Code

            Process.Start("chrome.exe", authorizeUrl);

            Console.WriteLine(listener.IsListening);

            HttpListenerContext context = listener.GetContext();

            HttpListenerRequest request = context.Request;

            Uri uri = new Uri(request.Url.AbsoluteUri);

            NameValueCollection parameters = HttpUtility.ParseQueryString(uri.Query);

            Console.WriteLine("Returned string: {0}", uri.Query);

            string authCode = parameters["code"];

            Console.WriteLine("Auth code: {0}", authCode);

            // Prepare Request for Access Token

            const string authBaseUrl = "https://login.eveonline.com/v2/oauth/token/";

            NameValueCollection post_params = new NameValueCollection();

            post_params.Add("grant_type", "authorization_code");

            post_params.Add("code", authCode);

            post_params.Add("client_id", clientId);

            post_params.Add("code_verifier", origEncodedCodeChallenge);

            

            using (WebClient client = new WebClient())

            {

                client.Encoding = Encoding.UTF8;

                client.Headers.Clear();

                client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                client.Headers.Add("Host", "login.eveonline.com");

                Console.WriteLine();

                Console.WriteLine($"Authorization URL: {authBaseUrl}");

                try

                {

                    byte[] response = client.UploadValues(authBaseUrl, "POST", post_params);

                    Console.WriteLine(Encoding.UTF8.GetString(response));

                }

                catch (Exception ex)

                {

                    Console.WriteLine(ex);

                }

            }

            Console.ReadKey();

        }

        public static string urlSafeBase64Encode(string input)

        {

            byte[] byteArray = Encoding.UTF8.GetBytes(input);

            string working = Convert.ToBase64String(byteArray).TrimEnd('=');

            return working;

        }

        // This function was taken from: https://www.c-sharpcorner.com/article/compute-sha256-hash-in-c-sharp/

        public static string ComputeSha256Hash(string rawData)

        {

            using (SHA256 sha256Hash = SHA256.Create())

            {

                // ComputeHash - returns byte array

                byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));

                // Convert byte array to a string

                StringBuilder builder = new StringBuilder();

                for (int i = 0; i < bytes.Length; i++)

                {

                    builder.Append(bytes[i].ToString("x2"));

                }

                return builder.ToString();

            }

        }

    }

}

Whats the full error?

Right, but what’s in the response body? I.e. the information that tells you what’s actually wrong.

Guess that I had it in my head that there wouldn’t be a Response for a Bad Request.

The Response was:
{“error”:“invalid_grant”,“error_description”:“Failed code verification challenge.”}

Looks like it’s saying that the problem is with the way that I’m encoding the Code Challenge.
I had thought that there was a good chance that that’s where the problem is, but I guess I’m not familiar with it enough for it jump out at me.

I’m not super familiar with this SSO method, nor C#. However if I had to guess, my vote would be your 32 byte string isn’t correct.

const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; doesn’t seem like it should be required for you to just generate 32 random bytes.

Try something like https://repl.it/repls/OrchidUnacceptableDiscussions. The other issue might have been your Base64 encoding not being URL safe.

I appreciate the feedback. I will have a look at it.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.