0

I really been struggling with exchanging the code received from Xero after a user from another Xero organisation has authorised my app.

Whatever I tried I consistently receive a 400 - Bad Request, For obvious security reasons "Bad Request" is all the information in the response.

I have tried using StringContent. Setting the accept headers of the HttpClient. Using httpClient.PostAsync().

Here's the what the request headers and body need to contain:

POST https://identity.xero.com/connect/token
authorization: "Basic " + base64encode(client_id + ":" + client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=xxxxxx
&redirect_uri=https://myapp.com/redirect

where xxxxx is the code returned to the callback method.

Below is the the method in my service which handles the App authorisation callback. I'd be super appreciative if someone could point out what I'm not doing right.

    public async Task<string> AuthoriseApp(string code, string state)
    {
        try
        {
            var clientState = TokenUtilities.GetCurrentState();

            if (state != clientState)
            {
                return "Cross site forgery attack detected!";
            }

            var paramss = new Dictionary<string, string>
            {
                { "grant_type", "authorization_code" },
                { "code", code },
                { "redirect_uri", xeroConfig.CallbackUri.AbsoluteUri }
            };

            var body = new FormUrlEncodedContent(paramss);

            var httpClient = new HttpClient();

            using var requestMessage = new HttpRequestMessage(HttpMethod.Post, xeroConfig.XeroIdentityBaseUri + "/connect/token")
            {
                Content = body
            };

            requestMessage.Headers.Authorization = new BasicAuthenticationHeaderValue(xeroConfig.ClientId, xeroConfig.ClientSecret);

            var response = await httpClient.SendAsync(requestMessage);

            return "Success"; // This will change when I actually receive a 200 and the access token
        }
        catch (Exception ex)
        {
            logger.LogError(ex.Message);
            return ex.Message;
        }
    }

Thanks in advance

Jon

Jon Little
  • 13
  • 4
  • That looks right to me (assuming you are using the BasicAuthenticationHeaderValue for IdentityModel). I would post it to a requestbin instead of Xero and see how it looks. Try here: https://requestbin.com/r – Matt Bommicino Aug 24 '22 at 04:19
  • @MattBommicino Awesome tool - thank you! The request headers and body are correct. – Jon Little Aug 24 '22 at 04:51
  • Are you sure that you didn't initiate the PKCE authorization flow in the browser? That would require different fields in the request. – Matt Bommicino Aug 24 '22 at 05:06
  • If I use Postman with exactly the same headers and body the request to Xero succeeds and I get the access token back. Using Requestbin for my code and postman I can see the the body is identical. The postman request has 2 extra headers but they're both related to postman. Very odd – Jon Little Aug 24 '22 at 06:06

0 Answers0