Tumblr OAuth Callback URL - oauth

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.

Related

What auth flow to use with spa and service account msal

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.

Etsy oauth and fetching info

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.

Linkedin Oauth with Rails

We have been connecting to Linkedin for awhile now successfully. However, we get some errors from time to time and I'm hoping someone can help shed some light on this. Here's our code:
def linkedin_login
request_token = Linkedin.client.request_token(oauth_callback: "http://#{SITE_URL}/linkedin/auth/")
session[:linkedin_request_token] = request_token.token
session[:linkedin_request_secret] = request_token.secret
redirect_to request_token.authorize_url
end
def linkedin_auth
raise "Don't have proper session or oauth_verifier" if session[:linkedin_request_token].blank? or session[:linkedin_request_secret].blank? or params[:oauth_verifier].blank?
access_token = Linkedin.client.authorize_from_request(session[:linkedin_request_token], session[:linkedin_request_secret], params[:oauth_verifier])
raise "Nil access token" if access_token.blank?
redirect_to linkedin_process_path(token: access_token.first, secret: access_token.second)
end
We're hitting the "raise 'Don't have proper session or oauth_verifier'" more than I would expect. When looking at the ENV for the errors, those people don't have the session values set from the original method. We have before_filters set on the application controller so initialize the session, so I know it's active.
My next thought was whether "request_token" was generating a value request_token, and I've tried many times and they all bring something back. We get many of these a day. After the error, if the user tries again, it works fine, which is why I'm so confused.
any thoughts on what could cause this?
Based on your code, it looks like you're making the request token call every time the user logs into your application. That's not the proper method to authenticate. You really only need to fetch the request token once, then use that to upgrade for an access token (as you're doing in your linkedin_auth method). From there, just save the access token and secret in your DB and fetch it anytime you need to make an API call for that particular user.
Our authentication is described more in detail here: https://developer.linkedin.com/documents/authentication
Also, this is just a personal preference, but I like using the OAuth gem for Rails as opposed to using a LinkedIn wrapper. It's easy to use and light weight.
Just as an example, you could do your auth this way:
require 'oauth'
def auth
api_key = 'XXXXXXXXX'
api_secret = 'XXXXXXXXX'
configuration = { :site => 'https://api.linkedin.com',
:authorize_path => 'https://www.linkedin.com/uas/oauth/authenticate',
:request_token_path => 'https://api.linkedin.com/uas/oauth/requestToken',
:access_token_path => 'https://api.linkedin.com/uas/oauth/accessToken' }
consumer = OAuth::Consumer.new(api_key, api_secret, configuration)
#Request token
request_token = consumer.get_request_token
# Output request URL to console
puts "Please visit this URL: https://api.linkedin.com/uas/oauth/authenticate?oauth_token=" + request_token.token + " in your browser and then input the numerical code you are provided here: "
# Set verifier code
verifier = $stdin.gets.strip
# Retrieve access token object
#access_token = request_token.get_access_token(:oauth_verifier => verifier)
end
You would only need to invoke this method when the user first authorizes your app. Save their access token then use it for subsequent API calls. Note, my example makes use of the console to enter the PIN verifier. In a real world example you'd want to programmatically save the PIN in a session variable or in memory, then use it to get the access token.

Facebook OAuth: custom callback_uri parameters

I'd like to have a dynamic redirect URL for my Facebook OAuth2 integration. For example, if my redirect URL is this in my Facebook app:
http://www.mysite.com/oauth_callback?foo=bar
I'd like the redirect URL for a specific request be something like this, so that on the server, I have some context about how to process the auth code:
http://www.mysite.com/oauth_callback?foo=bar&user=6234
My redirect gets invoked after the authorization dialog is submitted, and I get back an auth code, but when I try to get my access token, I'm getting an OAuthException error back from Facebook. My request looks like this (line breaks added for clarity):
https://graph.facebook.com/oauth/access_token
?client_id=MY_CLIENT_ID
&redirect_uri=http%3A%2F%2Fwww.mysite.com%2Foauth_callback%3Ffoo%3Dbar%26user%3D6234
&client_secret=MY_SECRET
&code=RECEIVED_CODE
All of my parameters are URL-encoded, and the code looks valid, so my only guess is that the problem parameter is my redirect_uri. I've tried setting redirect_uri to all of the following, to no avail:
The actual URL of the request to my site
The URL of the request to my site, minus the code parameter
The URL specified in my Facebook application's configuration
Are custom redirect URI parameters supported? If so, am I specifying them correctly? If not, will I be forced to set a cookie, or is there some better pattern for supplying context to my web site?
I figured out the answer; rather than adding additional parameters to the redirect URL, you can add a state parameter to the request to https://www.facebook.com/dialog/oauth:
https://www.facebook.com/dialog/oauth
?client_id=MY_CLIENT_ID
&scope=MY_SCOPE
&redirect_uri=http%3A%2F%2Fwww.mysite.com%2Foauth_callback%3Ffoo%3Dbar
&state=6234
That state parameter is then passed to the callback URL.
If, for any reason, you can't use the option that Jacob suggested as it's my case, you can urlencode your redirect_uri parameter before passing it and it will work, even with a complete querystring like foo=bar&morefoo=morebar in it.
I was trying to implement a Facebook login workflow against API v2.9 following this tutorial. I tried the solutions described above. Manuel's answer is sort of correct, but what I observed is url encoding is not needed. Plus, you can only pass one parameter. Only the first query parameter will be considered, the rest will be ignored. Here is an example,
Request a code via https://www.facebook.com/v2.9/dialog/oauth?client_id={app-id}&redirect_uri=http://{url}/login-redirect?myExtraParameter={some-value}
You'd get a callback for your url. It will look like http://{url}/login-redirect?code={code-from-facebook}&myExtraParameter={value-passed-in-step-1}. Note that facebook would make a callback with myExtraParameter. You can extract the value for myExtraParameter from callback url.
Then you can request access token with https://graph.facebook.com/v2.9/oauth/access_token?client_id={app-id}&client_secret={app-secret}&code={code-from-facebook}&redirect_uri=http://{url}/login-redirect?myExtraParameter={value-extracted-in-step-2}
Additional parameter passed in step 1 after the first query parameter will be ignored. Also make sure to not include any invalid characters in your query parameter (see this for more information).
You're best off specifying a unique callback for each oAuth provider, /oauth/facebook, /oauth/twitter etc.
If you really want the same file to respond to all oAuth requests, either include it in the individual files or setup a path that will call the same file on your server using .htaccess redirects or something similar: /oauth/* > oauth_callback.ext
You should set your custom state parameter using the login helper as such:
use Facebook\Facebook;
use Illuminate\Support\Str;
$fb = new Facebook([
'app_id' => env('FB_APP_ID'),
'app_secret' => env('FB_APP_SECRET'),
'default_graph_version' => env('FB_APP_VER'),
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = [
'public_profile',
'user_link',
'email',
'read_insights',
'pages_show_list',
'instagram_basic',
'instagram_manage_insights',
'manage_pages'
];
$random = Str::random(20);
$OAuth2Client = $fb->getOAuth2Client();
$redirectLoginHelper = $fb->getRedirectLoginHelper();
$persistentDataHandler = $redirectLoginHelper->getPersistentDataHandler();
$persistentDataHandler->set('state', $random);
$loginUrl = $OAuth2Client->getAuthorizationUrl(
url('/') . '/auth/facebook',
$random,
$permissions
);
Hey if you are using official facebook php skd then you can set custom state param like this
$helper = $fb->getRedirectLoginHelper();
$helper->getPersistentDataHandler()->set('state',"any_data");
$url = $helper->getLoginUrl($callback_url, $fb_permissions_array);

Rails EOF Error when using HTTP.get_response to retrieve Facebook access token

I trying to implement a Login with Facebook feature on my site, and hitting a roadblock trying to get the access token back from Facebook. Here is my code:
if params[:error_reason] == "user_denied" then
flash[:error] = "To login with Facebook, you must click 'Allow' to let the site access your information"
redirect_to :login
elsif params[:code] then
token_uri = URI.parse("https://graph.facebook.com/oauth/access_token?client_id=****************&redirect_uri=http://localhost:3000/auth/fblogin&client_secret=***************&code="+URI.escape(params[:code]))
response = Net::HTTP.get_response(token_uri)
session[:response] = response
data = ActiveSupport::JSON.decode(response)
access_token = data[:access_token]
flash[:error] = access_token
redirect_to :register
end
This is inside a fblogin controller function that is the target of the initial redirect to get an authorization code (the params[:code]).
But when I run through this, I get the following error:
EOFError in AuthController#fblogin
on the Net::HTTP.get_response(token_uri) line. I've searched all over, and can't find anything to indicate what this means. Could it be the obscure characters Facebook uses in their access tokens? I'm totally lost!
You are receiving an EOFError because you are trying to connect to an https URL using code that only works with http. See the section entitled "SSL/HTTPS request" at this Net::HTTP Cheat Sheet for the basics.
However, I would recommend using a third-party library to manage this for you, such as OAuth2 for utilizing Facebook's OAuth2 API, where you'd write code like this:
def client
OAuth2::Client.new('app_id', 'app_secret', :site => 'https://graph.facebook.com')
end
# in your callback code:
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => 'http://localhost:3000/auth/fblogin')
user = JSON.parse(access_token.get('/me'))
If you really want to make the requests yourself, you can look at libraries like Faraday to execute the HTTPS requests for you.

Resources