I am trying to use etsy API and validate with oauth gem. I have successfully got a successful token by doing this in the first request url:
scopes = ["email_r", "feedback_r", "listings_r", "transactions_r"]
oauth_consumer = OAuth::Consumer.new(Rails.application.secrets.etsy_api_key,
Rails.application.secrets.etsy_api_secret,
site: "https://openapi.etsy.com/v2"
)
oauth_consumer.options[:request_token_path] = "/oauth/request_token?scope=#{scopes.join('%20')}"
request_token = oauth_consumer.get_request_token(oauth_callback: new_etsy_authentication_url)
redirect_to request_token.params[:login_url]
Then the user passes the etsy validation pages and on callback url I have the following:
current_user.update(etsy_auth: {
oauth_token: params["oauth_token"],
oauth_verifier: params["oauth_verifier"],
oauth_created_at: Time.current.to_i
})
Where I save etsy oauth_token and oauth_verifier successfully.
The problem starts after that. I've tried many things to do a request to user but I always got oauth_token=rejected. Here a sample of what I've done so far:
oauth_consumer = OAuth::Consumer.new(Rails.application.secrets.etsy_api_key,Rails.application.secrets.etsy_api_secret,site: "https://openapi.etsy.com/v2")
access_token = OAuth::AccessToken.new(oauth_consumer, oauth_token: current_user.etsy_auth["oauth_token"], oauth_secret: current_user.etsy_auth["oauth_verifier"])
access_token.request(:get, "/users/__SELF__")
Should I do another request before that, to actually got another temporary oauth_token and oauth_secret?
I've tried doing this:
request_token = oauth_consumer.get_request_token and I got a temporary oauth_token and oauth_token_secret as well oauth_consumer_key (which I am not sure how I should use). I got the temporary tokens and tried many combinations without much success, I am always getting oauth_token=rejected . I've still haven't figured out if and where should I use oauth_consumer_key and if oauth_verifier is actually the oauth_secret.
What am I missing? Any help is appreciated.
I finally managed to find what needed to do. After the initial request to Etsy, I have to store oauth_token and oauth_secret. Then Etsy returns as well the oauth_verifier.
For fetching and doing each request after that, you need to send all three of them to work.
Related
There's so many different flows in the Microsoft docs that I have no clue what one is needed for me. I am using React and Python. (I understand node, so if someone explains using node/express its fine)
What user should see:
A page with a button to login, nav is there but wont work till logged in. The login creates a popup to sign in with Microsoft account. Once signed in, the user will be able to use nav to see dynamics information.
What I am trying to do:
This app needs to sign in a user and obtain the users email through 'https://graph.microsoft.com/v1.0/me'.(no client secrets needed) Then I need to send that email in this request;
(The tenant == {company}.crm.dynamics.com.)
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
This backend request needs to have a client secret to obtain the information. So I believe my backend also needs to be logged on to a service account. I believe I need to get a token for my backend to make requests on behalf of the service account.
What I have:
I have a React frontend that is signing a user in and calling 'https://graph.microsoft.com/v1.0/me' correctly and getting that email. Once I get the email, I am sending it to my backend.
Now I have no clue how to proceed and have tried many things.
What I have tried for backend:
Attempt 1: I get a token but error: {'error': {'code': '0x80072560', 'message': 'The user is not a member of the organization.'}}. Problem is, this id is the Azure AD ID. It should def work
#app.route('/dynToken', methods=['POST'])
def get_dyn_token():
req = request.get_json()
partnerEmail = req['partnerEmail']
token = req['accessToken']
body = {
"client_id": microsoft_client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": SCOPE_DYN,
}
TENANTID = '{hash here}'
res = requests.post(
f'https://login.microsoftonline.com/{TENANTID}/oauth2/v2.0/token', data=body).json()
dyn_token = res['access_token']
headers = {
"Prefer": "odata.include-annotations=\"*\"",
"content-type": "application/json; odata.metadata=full",
"Authorization": f"Bearer {dyn_token}"
}
try:
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
print(allInfo)
Attempt 2:
Same code but instead of f'https://login.microsoftonline.com/{TENANTID}/oauth2/v2.0/token' its
f'https://login.microsoftonline.com/common/oauth2/v2.0/token'. Error: An exception occurred: [Errno Expecting value] : 0. Because it returns an empty string.
Now I don't know if I am even on the right path or where to go. I know the routes work themselves if the token is correct. I used only SSR with no react and these routes work. But I need the React to be there too. I just don't know what flow to use here to get what I need. The docs make it easy for /me route to work. But the {company}crm.dynamics.com docs don't really provide what I am trying to do.
Additional info after comment:
What 'f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq '{email}'', headers=headers" is trying to get are API keys. Full code :
try:
allInfo = requests.get(
f'https://{TENANT}api/data/v9.0/company_partneruserses?$filter=company_email eq \'{email}\'', headers=headers).json()
partner_value = allInfo['value'][0]['_company_partner_value']
response = requests.get(
f'https://{TENANT}api/data/v9.0/company_partnerses({partner_value})', headers=headers).json()
return {'key': response['company_apikey'], 'secret': response['company_apisecret']}
Then once it has the keys:
def api_authentication(apikey, apisecret):
headers = get_headers() #<-- same headers as above with using dyn_token
response = requests.get(
f'https://{TENANT}api/data/v9.0/company_partnerses?$filter=company_apikey eq \'{apikey}\' and company_apisecret eq \'{apisecret}\'&$select=company_apikey,company_apisecret,_company_account_value,_company_primarycontact_value,blahblah_unassignedhours,company_reporturl', headers=headers).json()
return response
Afterwards I am able to get all the information I am looking for to send back to my frontend for the client to see. (By making multiple request to crm with these keys)
The client_credentials grant that you are using should work, provided the CRM trusts the token issued to the client (your python backend). Please use MSAL library instead of hand crafting the token request. It will save you time and eliminate errors.
I need to put on a website the complete instagram feed of my customer. I have tried several ways to access to instagram and get the whole feed, but I get some errors.
Using the gem "instagram" (the easiest and simplest method) i got the following error when I try to get the access token.
Instagram::BadRequest: POST https://api.instagram.com/oauth/access_token/: 400: OAuthException: Matching code was not found or was already used.
I tried to follow the indication on the instagram documentation https://www.instagram.com/developer/authentication/ using my browser and I can get the access token, but I am not able to do the http calls manually in ruby.
I need to download all the data and keep it inside my database (as I always done) so I need everything in the controller.
my code is very simple:
accessToken = Instagram.get_access_token(Instagram.client_id, :redirect_uri => CALLBACK_URL)
client = Instagram.client(access_token: accessToken)
response = client.user_recent_media
#other code to cycle inside posts and put everything in db
i'm sure to have included the same redirect_url in my instragram registration app and in my code.
As shown in the sample application in the gem's repo, you will need to call authorize_url first:
Instagram.authorize_url(:redirect_uri => CALLBACK_URL)
and then you will get request on the CALLBACK_URL, where you should have an action with the code that you provided:
response = Instagram.get_access_token(params[:code], :redirect_uri => CALLBACK_URL)
client = Instagram.client(:access_token => response.access_token)
# do stuff with the client
SOLVED!
I have misunderstood the usage of the api.
I got my access token following this https://elfsight.com/blog/2016/05/how-to-get-instagram-access-token/
and i can access my posts using in my model:
access_token = Instagram.access_token
client = Instagram.client(access_token: access_token)
response = client.user_recent_media
and in my config file:
Instagram.configure do |config|
config.client_id = "CLIENT_ID"
config.client_secret = "CLIENT_SECRET"
config.access_token = "ACCESS_TOKEN"
end
thanks a lot for your support
you should get the token when processing the callback in the omniauth.auth hash: request.env['omniauth.auth']['credentials']['token']
I'm trying to integrate Withings with a rails apps. I'm using an Omniauth provider someone wrote called omniauth-withings. I was able to configure the provider to allow me to visit /auth/withings which redirects to the Withings authorization page. After I allow access, the browser is redirected to the callback url /auth/withings/callback. I have this routed to a controller action that attempts to get the measurement data from Withings using the simplificator-withings gem.
Withings.consumer_secret = ENV['withings_app_key']
Withings.consumer_key = ENV['withings_app_secret']
auth_hash = request.env['omniauth.auth']
user_id = auth_hash.extra.raw_info.body.users.first.id
withings_user = User.authenticate(user_id, auth_hash.credentials.token, auth_hash.credentials.secret)
measurements = withings_user.measurement_groups(:device => Withings::SCALE)
The problem happens when I call User.authenticate(), I get this:
An unknown error occurred - Status code: 2555
Is there something I'm missing here?
I was getting the same error with a django app. It turns out I was using the wrong token and secret. I was using the oauth_token and oauth_token_secret returned from step 1 of the authorization process, rather than the oauth_token and oauth_token_secret from step 3. Make sure you are using the values from step 3. The API documentation shows the same values returned from these calls, but they will be different. Hopefully this helps you too.
I am using Rails + Garb Gem (Sija Branch) + omniauth-google-oauth2 Gem and I can successfully authenticate with the Google Analytics API and extract data that our app is generating when using a user login, e.g.:
Garb::Session.login('USERNAME', '<PASSWORD>')
I can then use Garb to connect to the Analytics Profile I want and pull the data from it and display some charts on a webpage. This all works fine.
However, I want to use oAuth2 to authenticate with Analytics which is why I had to install the Sija branch of the Garb Gem from Github (it supports oAuth2) and I also installed the omniauth-google-oauth2 Gem. Now in theory I should be able to authenticate using the following code:
Garb::Session.access_token = access_token # an instance of OAuth2::Client
It's at this point that it gets a little hazy for me and I would greatly appreciate some guidance. Here's how far I have gotten:
1) I created a Project in the Google API console and turned on Analytics API under Services
2) This provided me with a Client ID and Client Secret
3) I came across this code which I could populate with the ID and Secret above:
client = OAuth2::Client.new(
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => '/o/oauth2/auth',
:token_url => '/o/oauth2/token'
})
4) Then there is the next bit of code:
response = OAuth2::AccessToken.new(
client,
STORED_TOKEN, {
refresh_token: STORED_REFRESH_TOKEN,
expires_at: STORED_EXPIRES_AT
})
5) and then in theory connect with:
Garb::Session.access_token = response
The problem I have is I don't have the token information in Point (4) above. It seems to me that with oAuth2 I need to do a "handshake" once and print out the return token values? Perhaps through Rails code which prints the values returned out and then paste the token values into a constant in the Rails app so that I can use them in the above code? I really am confused. As I mentioned earlier, the web app works fine using the user login authentication. All the web app is doing is authenticating with analytics, pulling down some data and drawing a chart. But I am stuck converting it over to oAuth2 as I just do not know how to get the Access Token that the Garb Gem is looking for. I should also note that this is not a public website with multiple users authenticating, this is a CMS website that is connecting to our own Analytics data.
I have seen some partial snippets of aspects of this but not a fully explained or working example. I would really appreciate any guidance and help with this question.
Many thanks in advance,
JR
I've soldiered through this over the last few weeks, so let me share what worked:
To use Oauth2 you need to get a 'refresh token' that you use to 're-authenticate' with google each time you make an API call. The steps for this are as follows:
1) Setup your account in the API console - https://code.google.com/apis/console/b/0/ (seems like you've done that well)
2) In your API account, make sure you have a redirect URI pointing back to your application:
http://some-url.com/auth/google_oauth2/callback
http://localhost:3000/auth/google_oauth2/callback
Note here that google won't let you call back to your local machine as 0.0.0.0:3000... so you'll need to use localhost explicitly
3) In your route file, tie that redirect url to an action in the controller where you're going to create the project or authentication
match '/auth/:provider/callback' => 'authentications#create'
The ':provider' simply lets you match on multiple types of oauth, but you could just put 'google_oauth2' there as well.
4) Now create that action in your controller
def create
auth = request.env["omniauth.auth"]
params = request.env["omniauth.params"]
project = Project.find(params['project_id'])
Authentication.create(:project_id => project.id, :provider => auth['provider'], :uid => auth['uid'], :access_token => auth['credentials']['refresh_token'])
flash[:notice] = "Authentication successful."
redirect_to owner_view_project_path(project)
end
5) The controller action should retrieve the relevant fields from the response object (details of response object here: https://github.com/zquestz/omniauth-google-oauth2) - in particular, you need to get the 'refresh_token' and save that to your project or authentication object - if you haven't added an 'access_token' attribute to the desired object, go do that now with a migration, then start saving the refresh token to that attribute
6) Now when you're ready to call that particular authentication and get API data for it, you can load up that object where you saved the access token, and use that to get a new session with the google API as follows:
#authentication = Authentications.find(params[:id])
client = OAuth2::Client.new GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => "/o/oauth2/auth",
:token_url => "/o/oauth2/token",
}
response = OAuth2::AccessToken.from_hash(client, :refresh_token => #authentication.access_token).refresh!
Garb::Session.access_token = response
#profiles = Garb::Management::Profile.all
What this code did was create an OAuth2 access token (response) by specifying the client and then a refresh_token, then calling 'refresh!' to get a refreshed access token... then use that access token to establish your Garb session, then call down all the profiles for a given account using the Gard::Management::Profile.all
Hope this helps - let me know if you have questions!
Just a note on what worked for me in:
For steps 3, 4 & 5 I used cURL instead to retrieve the Access/Refresh token. Step 6 is then the same for me (using the Sija branch of the Garb Gem). So using cURL:
Using the details associated with your Google app POST the following using cURL:
curl --data "code=<APP_CODE>&redirect_uri=http://localhost:3000/oauth2callback&client_id=<CLIENT_ID>.apps.googleusercontent.com&scope=&client_secret=<CLIENT_SECRET>&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token
The response takes the form:
{
"access_token" : "<ACCESS_TOKEN>",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "<REFRESH_TOKEN>"
}
which you can plug into the Garb Gem as per part 6.
The answer by #CamNorgate is valid.
If you don't have a "refresh_token" back from Omniauth on the callback make sure you are correctly initializing :google_oauth2
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{ :scope=>"https://www.google.com/m8/feeds, https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile",
:approval_prompt=>"force", access_type="offline"
}
end
Make sure to include :approval_prompt=>"force", access_type="offline" in order for the refresh_token to be sent back. The refresh_token is only provided on the first authorization from the user.
I'm setting the oauth_callback URL in the request header, when I do this in Twitter, it works fine and the user is redirected to the callback URL. But using Tumblr's API, the callback URL is ignored and the user is redirected to the default URL. Has anybody else experienced this? Is there anywhere else other than the header that I should be setting this? I tried passing it in as a parameter but that didn't really work either.
Any help would be appreciated.
According to Tumblr's developer blog, this was a bug in Tumblr's API and has been fixed.
Many of you have been dismayed that you could not override the
callback url when a user was attempting to authorize their
application. Good news: we’ve patched the bug that was causing this
particular issue.
Now, you can pass a url with the oauth_callback parameter and we will
redirect the user to that endpoint once you’re done.
Let’s go over a quick example.
When the user is presented with the screen to authorize your app, you
should be able to override your default callback with the
oauth_callback parameter in your url.
http://www.tumblr.com/oauth/authorize?oauth_token=your_token&oauth_callback=http%3A%2F%2Fmysite.com/oauth_callback/testing
The above url will redirect the user to
mysite.com/oauth_callback/testing and let you know if the user has
approved or denied your app.
Update March 14, 2013:
Starting today, Tumblr is no longer respecting the oauth_callback parameter. The blog post that I previously linked to has been deleted. I ended up using a variation of the accepted answer to work around it.
If you are trying a embed userid in callback url then this post can help you.
You can save your oauth token in a session and later on callback you can retrieve user from session.
on token request:
def ask_access
tumblr_consumer = get_consumer
if tumblr_consumer
#1. get a request token
request_token = tumblr_consumer.get_request_token
session[:request_token] = request_token
session[:user_token] = "#{request_token.params[:oauth_token]}_#{current_user.id}"
#2. have the user authorize
redirect_to request_token.authorize_url
else
render :text=> "Failed to acquire request token from Tumblr."
end
end
on call back:
def call_back
if params[:oauth_token] && params[:oauth_verifier]
request_token = session[:request_token]
user_id = session[:user_token].split("_")[1]
user = UserProfile.find user_id
##3. get an access token
access_token = request_token.get_access_token({:oauth_verifier => params[:oauth_verifier]})
user.tumblr_token = access_token.params[:oauth_token]
user.tumblr_secret = access_token.params[:oauth_token_secret]
user.save!
end
end
Tumblr does this (I assume) for security. They require that the callback URL is defined on application registration and they will not let it be overridden during implementation.
The security issue is to make sure that no one can steal your Application Token and try to use it to use your reputation to get access to customer's data. By forcing all callbacks to go to the default URL, they can guarantee that only your application is able get the Access Tokens.
The two ways to handle this are:
1) Have the default URL do a redirect to where you want it to go based on cookie or some other data
2) Have different application tokens for different callback URLs.
I can't respond to Jonathan Tran's answer, since my account is young, but posting the callback URL in the authorization URL no longer works, as he says. I asked on Twitter, and here was John Bunting's response:
https://twitter.com/codingjester/status/313248230987157505
I successfully was able to reroute my callback URL using the following (here in Python), after assigning all the proper keys:
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
resp, content = client.request(request_token_url, "GET")
resp, content = client.request(request_token_url, "POST", body=urllib.urlencode({"oauth_callback": "[your own URL here]"}))
Tumblr implements this behavior differently from Twitter, so the same use of the Ruby OAuth library yields different results.
For your value of #callback_url, this works in Twitter:
#request_token = #oauth.get_request_token({
oauth_callback:#callback_url
})
redirect_to #request_token.authorize_url
But for Tumblr, you will be redirected to your default URL. To specify a different URL, you should do this:
#request_token = #oauth.get_request_token
redirect_to #request_token.authorize_url + '&' + { oauth_callback:#callback_url }.to_query
This is consistent with their documentation/blog post (cited in another answer). I have not checked to see if this is "correct" according to the OAuth 1.0a specification.