I have been integrating QuickBooks's API to my web application. I made requests to get tokens (both request token and secret token), I stored them to database. So, how can I validate connection at second time or more, I mean because I had tokens in database, so I don't want to re-authorize to get other new tokens. I tried to find solutions to resolve it, but there's no anything. Please give me some methods or advices to get it done. Thank you very much!
So, how can I validate connection at second time or more,
You can validate tokens by doing a query against a v3 service, and checking to see if you get a 200 OK or a 401 Unauthorized response back.
Pseudo-code should look something like this:
function am_i_connected_to_quickbooks()
{
if (you have tokens stored in your database)
{
results = do_qb_query("SELECT * FROM Customer MAXRESULTS 1")
if (you got a 200 OK back from Intuit)
{
// You are connected, and the tokens are still valid/have not been revoked
return true
}
}
// You don't have tokens, or your tokens are invalid (revoked, expired, incorrect, etc.)
return false
}
Related
When using Bearer Token Authentication, SignalR passes the JWT token through the query string. Obviously this bears the risk that the the token gets logged along with the request and thus anybody who can read the log can impersonate by copying the token.
The docs say:
If you have concerns about logging this data with your server logs, you can disable this logging entirely by configuring the Microsoft.AspNetCore.Hosting logger to the Warning level or above (these messages are written at Info level)
At this stage a side question pops into my mind: who guarantees that if the log level is Warning and something bad happens, the log won't still contain the request URL?
The docs continue with:
If you still want to log certain request information, you can write a middleware to log the data you require and filter out the access_token query string value (if present).
I guess the idea is to entirely switch off the default logging of requests and replace it with a custom logging middleware. This does not sound trivial to me. So I was wondering:
Is there any way of hooking into the logger and then customize what's actually being logged?
Or can we leverage the existing HTTP Logging for that? At the first glance it also seems to be a all-or-nothing option in regards to logging of query strings or is there a way of customizing that?
Is there a NuGet package that solves the issue?
How did others solve this problem?
I've resorted to take an approach where the JWT token does need to be sent as part of the query string, as explained here.
To summarize, when set as a cookie, the cookie will automatically be sent as part of the SignalR connection initialization by the browser:
document.cookie = `X-Authorization=${token}; path=/; secure; samesite=strict`; // https://stackoverflow.com/a/48618910/331281
const newConnection = new HubConnectionBuilder()
.withUrl('/background-queue-hub', {
skipNegotiation: true, // to avoid CORS problems (see: https://stackoverflow.com/a/52913505/331281)
transport: HttpTransportType.WebSockets,
})
...
However, this runs the risk of CSWSH. So, server-side we have to check the origin header to mitigate that. It can be done right where the cookie value is copied to the JWT Bearer authentication context:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => // https://stackoverflow.com/a/66485247/331281
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// for SignalR authentication we need to read the access token form the cookie
// since we don't want to pass the authentication token through the query string
// as the query string is being logged
if (context.HttpContext.Request.Path.StartsWithSegments(SignalRHubPath))
{
var allowedOrigins = Configuration["SignalR:AllowedOrigins"].Split(';');
if (allowedOrigins.Contains(context.Request.Headers["Origin"].ToString())) // see: https://www.tpeczek.com/2017/07/preventing-cross-site-websocket.html
{
context.Token = context.Request.Cookies["X-Authorization"];
}
else
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
}
}
return Task.CompletedTask;
}
};
});
I hope this makes sense. I am using the Zoho C# SDK examples to write records to the CRM Leads. While calling recordOperations.CreateRecords(); it fails after trying to GetToken from the Token Store.
Here is the token I am saving
Token token = new OAuthToken(
"xxxx.clientid.goes.here.xxxxx",
"xxxx.clientsecret.goes.here.xxxx",
"REFRESH/GRANT token",
TokenType.GRANT,
string.Empty);
TokenStore tokenStore = new CustomTokenStore();
tokenStore.SaveToken(user, token);
And I am sending this token into the SDKInitilizer.Initialize. No errors at this point. Next I try and create a lead. When it gets inside of recordOperations.CreateRecords(); it tries GetToken and I've hard coded it to return exactly what was in the token object above. CreateRecords throws an error for "invalid_code". Here is what is in the log file
21-09-07 16:49:34 [INFO]: Initialization successful for Email Id : myemail#email.com in Environment : https://www.zohoapis.com.
21-09-07 16:49:47 [INFO]: Access Token has expired. Hence refreshing.
21-09-07 16:49:50 [ERROR]: Exception in authenticating current request : {"Code":"INVALID CLIENT ERROR","Message":"invalid_code","Cause":null,"Details":null,"StackTrace":" at Com.Zoho.API.Authenticator.OAuthToken.ParseResponse(String response)\r\n at Com.Zoho.API.Authenticator.OAuthToken.RefreshAccessToken(UserSignature user, TokenStore store)\r\n at Com.Zoho.API.Authenticator.OAuthToken.Authenticate(APIHTTPConnector urlConnection)\r\n at Com.Zoho.Crm.API.Util.CommonAPIHandler.APICall[T](Type className, String encodeType)","Data":{},"InnerException":null,"HelpLink":null,"Source":"ZCRMSDK","HResult":-2146233088}
It appears to be failing when it tries to refresh the token so I assume I am not sending in the right info in the token object?
*** Edit for #sucasa ***
This is what I am sending into the Initialize method.
What I have figured out since my first post is, I'm not getting the initial token from Initialize and its not calling the custom TokenStore.SaveToken() I created and it should, right? If I save it, all I have is what is above, not an actual token. So I think when I go to create the lead, I don't actually have the initial token to refresh. I hope that's clearer.
Access Tokens expire and must be refreshed using a refresh token. The error message indicates this. Can you log the value of token and report back here?
These are my temporary API settings:
wallet:accounts:read wallet:addresses:read wallet:buys:read wallet:checkouts:read wallet:contacts:read wallet:deposits:read wallet:notifications:read wallet:orders:read wallet:payment-methods:read wallet:sells:read wallet:transactions:read wallet:transactions:request wallet:transactions:send wallet:transactions:transfer wallet:user:read wallet:withdrawals:read
Reading account data:
HTTP REQUEST
GET https://api.coinbase.com/v2/accounts
SCOPES
wallet:accounts:read
... works well.
But the request of transactions data (I only need the buys as shown in the picture)...
Coinbase Dashboard Picture
Coinbase API Transactions
HTTP REQUEST
GET https://api.coinbase.com/v2/accounts/:account_id/transactions
SCOPES
wallet:transactions:read
... gives me an empty result:
{"pagination":{"ending_before":null,"starting_after":null,"limit":25,"order":"desc","previous_uri":null,"next_uri":null},"data":[]}
Anyone an idea what I could miss here?
I have no idea why you cannot get transactions. Let me tell; I've transactions on my BTC account. Retrieved and Sold BTC. When I call
https://api.coinbase.com/v2/accounts/account_id/transactions
with a CB-VERSION of 2018-05-09 (which is newly released), it returns.
Be sure about;
1- You have transactions in your selected account. When you check from coinbase.com, be sure you are seeing transactions.
2- Be sure your account id is going to API with URL
3- Be sure selected account id is matches with sending account id.
3- Be sure your request headers are ok.
4- Be sure your Auth token is not expired.
I'm pretty sure that you're missing somethings in your code. If none of them above don't solve your problem, find me. I can assist.
Thanks for your answer - I got it.
My fault was to think I need to request my transactions for one and only account id, something like user id. But after reading the account JSON a little bit more accurate I found out that there is an account id for every wallet. I read out the EUR wallet where I have of course no single transaction.
Thanks anyway.
Sebastian.
I'm developing a public website and what I want to do is pretty straightforward, but I'm pulling my hair out trying to get everything working right.
I administer an open Facebook group and I want to display the public facebook events of this group on my website.
I can't seem to figure out how to setup my authentication so that I can access the event data. Here is my code for using my application to get an auth token:
var fb = new FacebookClientWrapper();
dynamic result = fb.Get("oauth/access_token", new
{
client_id = AppSettings.AppID,
client_secret = AppSettings.AppSecret,
grant_type = "client_credentials"
});
fb.AccessToken = result.access_token;
I know this works fine because I can access some information - for example, if I access a specific event by its ID, I can retrieve that information.
The problem occurs when I try to retrieve a list of events with fields within a date range:
[HttpGet]
public object GetEventDetails(string unixStartDateTime, string unixEndDateTime)
{
var parms = new Dictionary<string, object>();
parms.Add("fields", new[] { "id","name","description","start_time","venue" });
if (!String.IsNullOrEmpty(unixStartDateTime)) { parms.Add("since", unixStartDateTime); }
if (!String.IsNullOrEmpty(unixEndDateTime)) { parms.Add("until", unixEndDateTime); }
var eventsLink = String.Format(#"/{0}/events", AppSettings.GroupID);
return ObjectFactory.GetInstance<IFacebookClient>().Get(eventsLink,parms);
}
(I'm aware that even if this did succeed, the return value wouldn't be serializable - I'm not concerned about that quite yet).
This GET request returns the following message:
(OAuthException - #102) A user access token is required to request this resource.
So the message is quite clear: I need a user access token to get the data I've requested. The question is - what is the best way to do this? Can I give my application a certain permission to read this data? I've looked over all the permissions available to apps, but I don't see one that would do the trick.
I don't want to require people to log onto Facebook to look at public event data, and I love the idea of allowing people with no technical experience to essentially update the website content by posting Facebook events to the group. Right now, I have to duplicate anything they do.
I would think this kind of application would be very common, but no matter what I've read or tried, I can't quite find an example of the same thing that works.
From the docs at https://developers.facebook.com/docs/graph-api/reference/v2.0/group/events you need
A user access token for a member of the group with user_groups permission.
To avoid the hassle, you could create such an Access Token via the Graph Explorer and then store it in your application. Remember to exchange that Access Token to a long-lived one (https://developers.facebook.com/docs/facebook-login/access-tokens/#extending), and that you have to renew the Access Token every 60 days afterwards.
in my C# application, I am trying to get aouth2 access and refresh tokens:
https://developers.google.com/accounts/docs/OAuth2InstalledApp
On the phase: Handling the Response, When I make the call I am supposed to get something like:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
but I get
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
}
Thus: refresh_token is missing. I debugged and I am sure I hit the api method: FetchAccessAndRefreshTokens but I have no refresh_token.
PS: I am using 201306 API
Any ideas?
In the API, within OAuth2ProviderForApplications.cs file, in GetAuthorizationUrl() method, on line 100 if you add &approval_prompt=force to the string:
return string.Format("{0}?scope={1}&state={2}&redirect_uri={3}&response_type={4}&" +
"client_id={5}&access_type={6}&approval_prompt=force"
it works. But this is a horrible workaround plus it might create apache license issues.
How found: in google oauth2 playground (https://developers.google.com/oauthplayground/) this parameter (approval_prompt=force) is set and if you omit it, it does not give refresh token.