minimul / qbo_api

Ruby JSON-only client for QuickBooks Online API v3. Built on top of the Faraday gem.
MIT License
85 stars 46 forks source link

OAuth 2.0 requests returning 401 Unauthorized #84

Closed virtuexru closed 6 years ago

virtuexru commented 6 years ago

I'm trying to create a connection to QBO via OAuth 2.0 example you guys have. I get the access token & refresh token then create a connection like so:

api = QboApi.new(access_token: session[:access_token], realm_id: session[:realm_id])

Then making a call like:

api.get :customer, 58

returns me an authentication error:

AuthenticationErrorGeneral: SRV-110-Authentication Failure , statusCode: 401

Even though I can make this exact same call from their API Explorer (yes I've double and triple checked the URL's/Realm ID). I've also double checked the API keys from the settings page and everything matches. It's very strange that I get a proper access token but can make no subsequent calls.

I am using this example:

https://github.com/minimul/qbo_api/blob/master/example/oauth2.rb

Any help? Anyone else able to make OAuth 2.0 calls successfully? (This is for development sandbox)

minimul commented 6 years ago

Yes. OAuth2 is being used in development and production by many.

  1. Update to the latest 1.8.3. @bf4 refactored the examples in this version.
  2. Turn on logging. See README.
  3. If there is still a problem post the log removing sensitive info.
virtuexru commented 6 years ago

@minimul I appreciate you helping me out. Still getting the same error; I'm convinced I've missed something straightforward but can't figure out what.

I, [2018-04-12T10:49:58.996523 #10825]  INFO -- : [[QuickBooks]] GET https://sandbox-quickbooks.api.intuit.com/v3/company/193514000000000/customer/58
D, [2018-04-12T10:49:58.996623 #10825] DEBUG -- : [[QuickBooks]] "Accept: application/json\nContent-Type: application/json;charset=UTF-8\nUser-Agent: Faraday v0.9.0\n\n"
E, [2018-04-12T10:49:59.900271 #10825] ERROR -- : QboApi::Unauthorized - [{:fault_type=>"AuthenticationFault", :error_code=>"100", :error_message=>"General Authentication Error", :error_detail=>"AuthenticationErrorGeneral: SRV-110-Authentication Failure , statusCode: 401"}] (/Users/x/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/qbo_api-1.8.3/lib/qbo_api/raise_http_exception.rb:15:in `block in call')
QboApi::Unauthorized: [{:fault_type=>"AuthenticationFault", :error_code=>"100", :error_message=>"General Authentication Error", :error_detail=>"AuthenticationErrorGeneral: SRV-110-Authentication Failure , statusCode: 401"}]
from /Users/x/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/qbo_api-1.8.3/lib/qbo_api/raise_http_exception.rb:15:in `block in call'

I've confirmed the realm_id (aka the company id) and URL matches what I'm doing in API explorer. I've double checked the QB CLIENT key + secret. I'm receiving a proper access_token & refresh_token after doing resp = client.access_token! But every call afterward with QboApi.new -> .get fails.

Edit: Also I'm in Rails and not sinatra if that matters.

minimul commented 6 years ago

Are you sure the access_token is getting passed in correctly? It's a big string.

Also as soon as you receive the access_token save it somewhere besides in a session var so that you have something to compare it against (with respect to the access_token in the logs).

bf4 commented 6 years ago

Possibly dumb question, but are you using the authentication flow first to get the authorization_code and exchange that for the access token and refresh token?

Were you able to get a customer using the example app?

ntippie commented 6 years ago

https://developer.intuit.com/docs/00_quickbooks_online/2_build/10_authentication_and_authorization/10_oauth_2.0

Look at the examples there and make sure you indeed have the access token. It's much longer than the other tokens passed around during OAuth2.

Also, ensure that the client ID and client secret are both being read properly by the example.

virtuexru commented 6 years ago

Thank you for all the replies @minimul @bf4 @ntippie. I ran the Sinatra example and it works fine. I get the same length/type of access/refresh tokens that I do on my app with Rails; still not sure what the issue is because once I call the API using:

api = QboApi.new(access_token: access_token, realm_id: session[:realmId])
@resp = api.get :customer, 50

I'm getting the same error (SRV-110-Authentication Failure , statusCode: 401) meanwhile the Sinatra app is being called fine (and getting a proper return). I'm also using the same ENV variables for the QBO_API_CLIENT_ID & QBO_API_CLIENT_ID.

Is anyone else running this on Rails or just Sinatra? Maybe I'm missing some kind of setup outside of the controller work (in Rails).

bf4 commented 6 years ago

@virtuexru

  1. Get your access token. (not authentication token or auth code or refresh token). make sure there are no line breaks in it.
  2. Get your realm id/ company id

Edit the first two lines then run this and share. I've tested these and they work.

access_token="put the access token in these quotes"
realm_id="put the company id in these quotes"
cat <<EOF > qbo_api_test.rb
require 'bundler/inline'

install_gems = true
gemfile(install_gems) do
  source 'https://rubygems.org'

  gem 'qbo_api'
end

require 'qbo_api'

QboApi.log = true
api = QboApi.new(access_token: "${access_token}", realm_id: "${realm_id}")
p api.get :company_info, "${realm_id}"
EOF
ruby qbo_api_test.rb

If that doesn't work, can you try just making a request in postman or with curl?

access_token="put the access token in these quotes"
realm_id="put the company id in these quotes"
curl -X GET "https://sandbox-quickbooks.api.intuit.com/v3/company/${realm_id}/companyinfo/${realm_id}" \
 -H "Accept: application/json" \
 -H "Content-Type: application/json;charset=UTF-8" \
 -H "User-Agent: virtuexru  test" \
 -H "Authorization: Bearer ${access_token}"
virtuexru commented 6 years ago

@bf4

Thanks for the response. Same issue (Sinatra access_token works, Rails does not). The curl response to the access_token I get from the Sinatra app gives me the proper customer object, the Rails app gives me:

{"warnings":null,"intuitObject":null,"fault":{"error":[{"message":"message=AuthenticationFailed; errorCode=003200; statusCode=401","detail":"","code":"3200","element":null}],"type":"AUTHENTICATION"},"report":null,"queryResponse":null,"batchItemResponse":[],"attachableResponse":[],"syncErrorResponse":null,"requestId":null,"time":1524495489389,"status":null,"cdcresponse":[]}%

The only real difference I can see (besides some dependency) is that I'm going through the flow on Sinatra via the Javascript button whereas on Rails I'm using:

oauth2_client.authorization_uri

UPDATE:

The only thing I was missing in the URL was the two extra data sources set by the example app in the QB Button (data_sources: ["quickbooks", "payments"]). Am still having the same issue however (Unauthorized).

SECOND UPDATE:

I've narrowed down that this is basically a Rails problem. I can access the api.get properly from any environment OUTSIDE of Rails (using the token generated via Sinatra example) and directly via cURL but using the same token in Rails gives me the Unauthorized error.

bf4 commented 6 years ago

@virtuexru

I've narrowed down that this is basically a Rails problem. I

If by 'Rails' you mean your app. Did you try the snippets I gave you? I took the time to write them and confirm them. I run a Rails app using OAuth2 and I don't have this problem. Those snippets have no dependencies, so it would be helpful.

Now, it sounds like you have some oauth2_client. It could be a config there. Who knows. I don't use the javascript in the example app. I use the flow described by intuit without any oauth2 client. But that's not Rails, that's your app.

I'm not really sure how to get you to share what your'e doing in a way we can reproduce it. Until you do, I can't help. You're not the only person using the gem in Rails, so it might not be the gem.

virtuexru commented 6 years ago

@bf4 yes sorry I should've been more specific; by Rails I meant our Rails app & its dependancies. I appreciate you taking the time out to write those snippets; they do work (depending on what app I use to get the initial access_token [sinatra example vs my rails app]). I'm going to close this issue now as it is not a QboApi specific problem. I appreciate all the help!

bf4 commented 6 years ago

@virtuexru Why don't you share you oauth2 client config? My money is that's where the problem is

rikkipitt commented 5 years ago

@virtuexru did you get to the bottom of your Rails issue? I'm having the same problem - CURL works, but in a Rails app, it fails as above...

Cheers!

rikkipitt commented 5 years ago

To answer my own question (and maybe others), my faraday_middleware gem was locked down at v0.10.x (from 2015). Updating to the latest solves this issue for me. In this case ~>v0.12.2.

minimul commented 5 years ago

Thanks @rikkipitt

sedhha commented 3 years ago

Can somebody explain the correct procedure guys? I am still struggling in 2021 to make the correct request.

minimul commented 3 years ago

@sedhha Need to provide very specific details if you're going to get any help.