skip to Main Content

I’m a rank noob at this, so excuse my ignorance. I’ve got an MVC web application to login, get the access and refresh tokens, and tenant list OK. I can even get it to refresh the refresh token. No problems.

When I try to run the GetInvoices endpoint either directly or via the sdk, I get 403 (skd) or 401 from the direct api call.

From the latest run with direct call I get this response

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: 
System.Net.Http.HttpConnectionResponseContent, Headers:
{
  Server: nginx
  Strict-Transport-Security: max-age=31536000
  WWW-Authenticate: OAuth Realm="api.xero.com"
  Cache-Control: no-store, no-cache, max-age=0
  Pragma: no-cache
  Date: Wed, 21 Jul 2021 11:19:56 GMT
  Connection: close
  X-Client-TLS-ver: tls1.2
  Content-Type: text/html; charset=utf-8
  Content-Length: 95
  Expires: Wed, 21 Jul 2021 11:19:56 GMT
}, Trailing Headers:
{
}}

I know that the access token and tenant id used in the GetInvoices step are correct because I checked them against the values pulled in from the auth steps character by character.

The app is being run in Visual Studio 2019, using the self-signed development SSL certificate.

Why is it rejecting the request?

my controllers have the following

private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";

        private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";

            string[] tenant = (string[])TempData.Peek("tenant");
        var client = new HttpClient();

        var formContent = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("summaryOnly", "true"),
        });

        client.DefaultRequestHeaders.Add("Authorization", (string)TempData.Peek("accessToken"));

        client.DefaultRequestHeaders.Add("Xero-Tenant-Id", tenant[0]);

        client.DefaultRequestHeaders.Add("Accept", "application/json");

        var response = await client.PostAsync("https://api.xero.com/api.xro/2.0/Invoices", formContent);

2

Answers


  1. Chosen as BEST ANSWER

    (DOH!) The Tenant returns an Id and a TenantId. I was using the Id.

    Thanks to SerKnight and droopsnoot for helping.

    I've added code from the OAuth2. The help does not mention to get and cast the return type of RequestAcessTokenAsync.

        XeroOAuth2Token authToken = (XeroOAuth2Token)await client.RequestAccessTokenAsync(authorisationCode);
    

    also to check the state on return, you must set in xconfig. mine reads

        XeroConfiguration xconfig = new()
                {
                    ClientId = Global.XeroClientID,
                    ClientSecret = Global.XeroClientSecret,
                    CallbackUri = new Uri(Global.RedirectURI),
                    Scope = Global.XeroScopes,
                    State = Global.XeroState
                };
    
                var client = new XeroClient(xconfig);
    
                return Redirect(client.BuildLoginUri());
    

  2. The SDK should handle this for you in the helper methods for the client and OAuth flow but i’ve included what looks like is missing from just a raw API call below.

    Core API call – looks like you need to prefix the token with the Bearer string.

    Authorization: "Bearer " + access_token

    If you are wanting to use the SDK note that there is a sub Nuget package for OAuth helpers that will help you obtain an access token which you need to pass to core api calls.

    https://github.com/XeroAPI/Xero-NetStandard/tree/master/Xero.NetStandard.OAuth2Client

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search