xero api fails with unauthorized (401 or 403) after calling auth, refresh and gettenants when calling getinvoices - xero-api

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);

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

(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());

Related

JWT validation with external authority doesn't work. ASP .NET Core 3.0

I am trying to authorize my requests using external service with IdentityServer4. I use following code
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = Configuration["IdentityUrl"];
options.RequireHttpsMetadata = false;
options.Audience = "myapi";
});
I got my token using token client
var tokenResponse = await tokenClient.RequestPasswordTokenAsync(
new PasswordTokenRequest
{
ClientId = clientId,
ClientSecret = clientSecret,
GrantType = GrantTypes.Password,
Address = discoveryDocument.TokenEndpoint,
UserName = user.UserName,
Password = user.PasswordHash,
});
It works and gives me token, but then when I try to authorize any request by including this token into authorization header it gives me 401 with no explanation. I don't see anything in output of either idenity server application or my client application.
This is what I do in postman to test authorization
Any ideas on what is wrong and how to debug it?
Try to see what the logs says?
Or try to set this one to true in the AddJwtBearer options and then look at the response from the API to see if it contains some additional details about the failure.
options.IncludeErrorDetails = true;
It it works you should see an additional WWW-authenticate header, like this one:
HTTP/1.1 401 Unauthorized
Date: Sun, 02 Aug 2020 11:19:06 GMT
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"

MSGraph Authenticating to dynamics with correct token gives 401

Using the following code I can connect to MSGraph (different resource url) and fetch the data that I need, but I'm struggling to realise how to do the same for Microsoft Dynamics (CRM)
public static async Task<string> GetTokenForApplication(string resourceUrl)
{
AuthenticationContext authenticationContext =
new AuthenticationContext(AppModeConstants.AuthString, false);
ClientCredential clientCred = new ClientCredential(AppModeConstants.ClientId, AppModeConstants.ClientSecret);
AuthenticationResult authenticationResult = await authenticationContext
AcquireTokenAsync("resourceUrl", clientCred);
TokenForApplication = authenticationResult.AccessToken;
}
public static async Task CrmTest()
{
var token = await AuthenticationHelper.GetTokenForApplication(#"https://myapp.crm4.dynamics.com/");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(#"https://myapp.crm4.dynamics.com/api/data/v8.2/accounts");
}
Although I fetch the right token I'll still get a 401: Unauthorized:
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
REQ_ID: 790b38b0-c8c0-4878-a318-e490ee7db57a
Strict-Transport-Security: max-age=31536000; includeSubDomains
Date: Thu, 11 May 2017 15:33:41 GMT
Set-Cookie: crmf5cookie=!UXdAbawdawdal8sNiJ9xy74dAiawdawdDnDsomethingAolDYjkR7innjWIYQ1pV+DcZ9A=;secure; path=/
Server: Microsoft-IIS/8.5
WWW-Authenticate: Bearer authorization_uri=https://login.windows.net/b8faag21-awda-awda-90s4-f8652ss86ddb/oauth2/authorize, resource_id=https://myapp.crm4.dynamics.com/
X-Powered-By: ASP.NET
Content-Length: 49
Content-Type: text/html
}}
A 403 I would understand, but a 401 means that I'm simply using the wrong authentication. Any input on this would be appreciated.
Edit: Is it even possible to access CRM data as an application? There are only delegated permissions available in the azure portal, no application permissions.
While both Dynamics CRM and Graph leverage Azure AD accounts, they have different permission scopes and prerequisites.
To get started, you'll want to follow this Walkthrough: Register a Dynamics 365 app with Azure Active Directory.

How to get id_token from TokenEndpoint of IdentityServer4 through authorization_code flow?

I would like to get "access_token" and "id_token" from Token endpoint through Authorization Code flow. But, I am getting "invalid_grant" error while calling the token endpoint with following parameters on postman.
POST /connect/token HTTP/1.1
Host: localhost:2000
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: a8a29659-0ea3-e7dc-3bd6-6e6630a7370d
client_id=client
&client_secret=client
&grant_type=authorization_code
&username=admin
&password=admin
&scope=openid+profile
Client Configuration:
new Client
{
ClientId = "client",
ClientSecrets =
{
new Secret("client".Sha256())
},
AllowedGrantTypes = new List<string> { OidcConstants.GrantTypes.AuthorizationCode },
AllowedScopes = {
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
}
}
What is wrong in my client configuration section? and, How do i make a successful post request to Token Endpoint?
The authorization code grant type requires a code parameter to be sent during the token request (see RFC6749 section 4.1.3).
This code is issued by the authorization server after the resource owner authorized the client (see RFC6749 section 4.1.2).

servicestack auth breaks at 4.0.21

I am encountering a problem when I upgraded my ServiceStack recently. I separated the different versions to find the problem started at v4.0.21. All earlier versions work and all later versions do not work. It only happens with my call to authenticate (with Basic Auth). Also, it only happens when I make the call to authenticate from our iPad app. Since we have not made any changes to our iPad app I know that something changed with ServiceStack that is causing our problem. I looked through the release notes and found there were a lot of changes to the auth capability due to the addition of a Windows Auth Provider and a couple of other new OAuth providers.
Is there some changes to the settings that I need to make that I am not seeing in the Release Notes? Has anyone else encountered this problem?
Here is our code for registering for ss-auth in global.asax.cs:
public override void Configure(Funq.Container container)
{
SetConfig(new HostConfig {
EnableFeatures = Feature.All.Remove(Feature.Metadata),
AllowJsonpRequests = false,
HandlerFactoryPath = "api"
});
var authFeature = new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[] {
new MyBasicAuthProvider() // override of BasicAuthProvider
}
);
authFeature.HtmlRedirect = null;
authFeature.IncludeAssignRoleServices = false;
Plugins.Add(authFeature);
container.Register<ICacheClient>(new AzureCacheClient("default"));
var userRepository = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRepository);
}
Here is the request URL:
POST https://vh.azurewebsites.net/api/auth?format=json
And here are the headers that went with the HTTP Request:
Host: vh.azurewebsites.net
Authorization: Basic dXN2dONjb3R0OnBhc3N3b3Jk
Accept-Encoding: gzip, deflate
Accept: application/json
Cookie: ss-opt=perm
Accept-Language: en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5
Content-Length: 0
Connection: keep-alive
Proxy-Connection: keep-alive
User-Agent: mHealth/QA (iPad; iOS 7.1.1; Scale/1.00)
The auth provider was changed so that the implicit default /auth can now be used to tell if a user is authenticated which will return Session Info if a user is already authenticated or a 401 Unauthorized if they're not.
You need to call the explicit /auth/{provider} Auth Provider Route for the auth provider you wish to authenticate against which for Basic Auth is /auth/basic.

coinbase oauth2 sometimes failed to renew access token (using refresh token)

I am integrating coinbase in an iOS app and I am using Oauth2 to authenticate. I am able to get the access token after going through the usual workflow. I have also taken care to attempt refresh my access token whenever any requests hit a 401 (upon expiration) by calling this:
POST https://coinbase.com/oauth/token
Data:
grant_type=refresh_token&refresh_token=abcd1234&client_id=theclientid&client_secret=somesecretid
It works for a while but then from time to time, it would fail with a request response:
NSHTTPURLResponse: 0x15eb2730
{ URL: https://coinbase.com/oauth/token } { status code: 401, headers {
"CF-RAY" = "f67d477aae4052e-YYZ";
"Cache-Control" = "no-store";
Connection = "keep-alive";
"Content-Type" = "application/json; charset=utf-8";
Date = "Sun, 02 Feb 2014 15:14:14 GMT";
Pragma = "no-cache";
Server = "cloudflare-nginx";
"Set-Cookie" = "__cfduid=<some long alpha-numeric string>; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.coinbase.com; HttpOnly";
Status = "401 Unauthorized";
"Strict-Transport-Security" = "max-age=31536000";
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"Www-Authenticate" = "Bearer realm=\"Doorkeeper\", error=\"invalid_request\", error_description=\"The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.\"";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = SAMEORIGIN;
"X-Rack-Cache" = "invalidate, pass";
"X-Request-Id" = "<some long alpha-numeric string>";
"X-Runtime" = "0.012066";
"X-Ua-Compatible" = "IE=Edge,chrome=1";
} }
Has anyone encounter this error before? I have assume the request URL is correct always. I am not sure why it would complained about "missing required parameter" or "unsupported parameter". I havent figured out a pattern of failure yet. Hopefully, someone out there may have seen this before.
I haven't been able to find any documentation directly about refresh_token requests, but I think you're supposed to also include the redirect_uri in your refresh_token request (based on this: https://coinbase.com/docs/api/authentication#collapse2).
Also, I noticed that my official coinbase app required a re-auth a few days ago, but when I log in to my coinbase account, it says that the app was authorized 25 days ago. So, perhaps even the request_tokens have a timeout? Did your request_token request fail after not using the app for a while?
Or maybe coinbase reset something and invalidated all of their access_tokens and refresh_tokens because my app, which was previously working fine as far as refresh_tokens, is now failing on the refresh_token request.
So, I would suggest having your app re-authorize when this happens, and get a new access_token and refresh_token, since I think that's what the official coinbase app does.