FbGraph cannot update status - ruby-on-rails

I wrote a Rails application. I used omniauth for authentication.
session.rb
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
session[:user_id] = user.id
Everything seems normal as well, but when I want to change my facebook status, fbgraph cannot do that. I wrote this code
provider = User.find_by_provider_and_name('facebook', current_user.name)
#facebook = FbGraph::User.me(provider.token)
#facebook.feed!("loanminder-test-message-#{Time.now.utc}")
And the exception is like that
FbGraph::InvalidToken in LoansController#show
OAuthException :: Error validating access token: Session does not
match current stored session. This may be because the user changed the
password since the time the session was created or Facebook has
changed the session for security reasons.
How can I solve the problem, how can I change my status via Rails?

Your stored token is no longer being accepted. You will need to prompt the user to login again and use a new access token, or use the following token
app_id|secret
where if your app ID is 942523352 and your secret is bc76876876f67676ae0 then you access token is
942523352|bc76876876f67676ae0

Related

How to implement google smart lock one tap sign in

I want to implement Google's One tap sign-up and automatic sign-in in my website with the help of documentation https://developers.google.com/identity/one-tap/web/ but I am getting confused on how to implement in python.
def smartlock(request):
try:
CLIENT_ID='*******'
csrf_token_cookie = self.request.cookies.get('g_csrf_token')
if not csrf_token_cookie:
webapp2.abort(400, 'No CSRF token in Cookie.')
csrf_token_body = self.request.get('g_csrf_token')
if not csrf_token_body:
webapp2.abort(400, 'No CSRF token in post body.')
if csrf_token_cookie != csrf_token_body:
webapp2.abort(400, 'Failed to verify double submit cookie.')
# Specify the CLIENT_ID of the app that accesses the backend:
idinfo = id_token.verify_oauth2_token(csrf_token_cookie, requests.Request(), CLIENT_ID)
# Or, if multiple clients access the backend server:
# idinfo = id_token.verify_oauth2_token(token, requests.Request())
# if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
# raise ValueError('Could not verify audience.')
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')
# If auth request is from a G Suite domain:
# if idinfo['hd'] != GSUITE_DOMAIN_NAME:
# raise ValueError('Wrong hosted domain.')
# ID token is valid. Get the user's Google Account ID from the decoded token.
userid = idinfo['sub']
except ValueError:
# Invalid token
pass
'''
As mentioned in the 'Key Point' section of this page: The ID token is returned in the credential field, instead of the g_csrf_token field.
So, you need to get the idinfo with the code as below:
credential = self.request.get('credential')
idinfo = id_token.verify_oauth2_token(credential, requests.Request(), CLIENT_ID)
The g_csrf_token parameter is for different purpose. It makes sure the request was submitted from a page in your own domain, so as to prevent the cross-site-request-forge attacks.

Linkedin OAuth authentification with React Frontend + Rails API

So far my website has been using fullstack views generated directly by Rails, and for linkedin authentication I used a redirect_uri routed to a Devise::OmniauthCallbacksController to handle the logic.
GET https://www.linkedin.com/oauth/v2/authorization?redirect_uri=my_devise_omniauth_callback_url
I am wondering how to migrate this to a standalone React frontend + a standalone Rails server. The React app should "spawn" a linkedin popup for authorization, and wait until all the authorization process is done before closing itself and proceeding with registration on the main window.
I have been through the Linkedin OAuth guide and I see the way from approving the connection to getting user info is quite long. I am happy that Devise/Omniauth does most of the job for me server-side and I'd rather keep it this way, instead of coding that logic in the frontend.
(By the way, is it safe to assume the Linkedin OAuth flow be similar to the Google one mentionned in this answer ?)
I am unsure about whether this can be done the way I see it on the React app (especially the communication of data between the linkedin popup window and the main react view). Also from what I understand the omniauth gem in rails expects to receive the one-time token from the OAuth provider before it can query the provider twice more (first time to exchange the access token, second time to get user info). Would the following work ?
Assume my React Client app is hosted on client.example.com
Assume my server api is hosted on api.example.com
The React app opens a popup windows, that is used to go to the Linkedin auth URI with
(1) A redirect uri that would point directly to api.example.com
(2) A redirect_uri on client.example.com, and my React client would then have to forward the token to api.example.com
The authorization code somewhat reaches my server api.example.com, which gets an access code and retrieves user data from Linkedin. It returns to the browser an identity_id which can be used for the registration
The identity_id is actually returned to the popup window, and transferred back to the main React app (how ?)
I had the same problem and I found an hacky but interesting solution :-)
(I use all the OAuth logic from devise and omniauth-linkedin gems.)
In my application, you can start to fill up a form and you need to login before save it. I needed a way to log the user without loosing form data and without reloading the page.
When the user visit the page, he is given a unique uuid to identify him. This uuid is used to start a websocket connection so I can push data directly to this tab later.
I open the LinkedIn OAuth in a new tab with javascript window.open() so I can close this tab with window.close().
You can pass additional parameters to the OAuth authentication, I pass the unique uuid so I can recover it when the authentication is a success and with this uuid I can notify the first tab by sending a message via websocket (In my application I send the user infos to update state with current user).
After authentication, I redirect the user on a page containing only window.close().
Setup LinkedIn OAuth in Rails
You will need a functional Omniauth/Devise authentication working.
omniauth_callback_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def linkedin
user = User.connect_to_linkedin(request.env["omniauth.auth"], current_user)
guest_guid = request.env["omniauth.params"]["guestGuid"]
if user.persisted?
ActionCable.server.broadcast("guest:#{guest_guid}", { user: UserSerializer.new(user).to_h })
sign_in(user)
redirect_to landing_home_index_path
end
end
end
user.rb
def self.connect_to_linkedin(auth, signed_in_resource = nil)
user = User.where(provider: auth.provider, linkedin_uid: auth.uid).first
if user
return user
else
registered_user = User.where(email: auth.info.email).first
if registered_user
return registered_user
else
user = User.create(lastname: auth.info.last_name, firstname: auth.info.first_name,
provider: auth.provider, linkedin_uid: auth.uid, email: auth.info.email,
linkedin_token: auth.credentials.token, linkedin_secret: auth.credentials.secret,
linkedin_picture: auth.extra.raw_info.pictureUrl, password: Devise.friendly_token[0, 20])
end
end
end
routes.rb
devise_for :users, controllers: {
omniauth_callbacks: "omniauth_callbacks",
sessions: "users/sessions"
}
Now if you visit /users/auth/linkedin you will be able to log/create user with LinkedIn OAuth.
Websocket connection
create_job_offer_channel.rb
class CreateJobOfferChannel < ApplicationCable::Channel
def subscribed
stream_from "guest:#{params[:guest]}"
end
def unsubscribed
end
def receive(data)
ActionCable.server.broadcast("guest:#{params[:guest]}", data)
end
end
create_job_offer.js.coffee
class window.CreateJobOffer
constructor: (params) ->
self = #
#channel = App.cable.subscriptions.create {
channel: "CreateJobOfferChannel", guest: params["guest"]
}, self
#onReceive = params["onReceive"] || null
received: (data) =>
#onReceive(data) if #onReceive
React frontend
Link to LinkedIn OAuth (with devise and omniauth)
<div className="btn-linked-in">
<i className="fa fa-linkedin-square"></i>
<span onClick={() => open(`/users/auth/linkedin?guestGuid=${guestGuid}`)}>
Login with LinkedIn
</span>
</div>
Hope this help :)

Twitter 3-legged authorization in Ruby

I am trying my hand ruby on rails. Mostly I have written code in Sinatra. Anyway this question may not have to do anything with framework. And this question may sound a very novice question. I am playing with Twitter 1.1 APIs and OAuth first time.
I have created an app XYZ and registered it with Twitter. I got XYZ's consumer key i.e., CONSUMER_KEY and consumer secret i.e. CONSUMER_SECRET. I also got XYZ's own access token i.e ACCESS_TOKEN and access secret i.e. ACCESS_SECRET
XYZ application type: Read, Write and Access direct messages
XYZ callback URL: http://www.mysite.com/cback
And I have checked: Allow this application to be used to Sign in with Twitter
What I am trying to do is very simple:
1) Users come to my website and click a link Link your twitter account (not signin with twitter)
2) That opens twitter popup where user grants permission to XYZ to perform actions on his/her behalf
3) Once user permits and popup gets closed, XYZ app gets user's access token and secret and save in the database.
4) Then XYZ uses that user's token and secret to perform actions in future.
I may be moron that such work flow has been implemented on several thousands sites and Twitter API documentations explain this 3-legged authentication, still I am unable to figure it out.
I have read https://dev.twitter.com/docs/auth/3-legged-authorization and https://dev.twitter.com/docs/auth/implementing-sign-twitter Unfortunately no ruby code found on internet that explains with step by step example.
What link should be used to open twitter authentication page when user clicks Link your twitter account.
Can anyone here, write some pseudo code with my pseduo credential above to achieve my goal from beging till end of this work flow? Thanks.
UPDATE:
I started with requesting request token as
require 'oauth'
consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET,
{ site: "https://twitter.com"})
request_token = consumer.get_request_token oauth_callback: 'http://www.mysite.com/tauth'
redirect_to request_token.authorize_url
I'm not familiar with ROR but here is the workflow of the OAuth 'dance' that you need to follow when the user clicks your button:
Obtain an unauthorized request token from Twitter by sending a
request to
POST https://api.twitter.com/oauth/request_token
signing the request using your consumer secret. This will be done in the background and
will be transparent to the user.
You will receive am oauth_token and oauth_token_secret back from
twitter.
Redirect the user to
https://api.twitter.com/oauth/authorize?oauth_token=[token_received_from_twitter]
using the oauth token value you received from Twitter in step 2.
When the user authorizes your app they will be redirected to your
callback url with oauth_token and oauth_verifier appended to the
url. i.e.
http://www.mysite.com/cback?oauth_token=NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0&oauth_verifer=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY
Convert the request token into an access token by sending a signed
request along with the oauth_verifier to
POST
https://api.twitter.com/oauth/access_token
signing your request
with your consumer secret and the token secret received in step 2.
If everything goes ok, you will receive a new oauth_token and
oauth_token_secret from Twitter. This is your access token for the
user.
Using the access token and secret received in step 6 you can make
Twitter api calls on behalf the the user by sending signed requests
to the appropriate api endpoints.
Hope you solved your problem by this time, but I built this sample Sign in with Twitter ruby web app that provide all explanation you need to do this integration. Below there's a class that implements all necessary methods with comments:
require "net/https"
require "simple_oauth"
# This class implements the requests that should
# be done to Twitter to be able to authenticate
# users with Twitter credentials
class TwitterSignIn
class << self
def configure
#oauth = YAML.load_file(TWITTER)
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 1)
def request_token
# The request to get request tokens should only
# use consumer key and consumer secret, no token
# is necessary
response = TwitterSignIn.request(
:post,
"https://api.twitter.com/oauth/request_token",
{},
#oauth
)
obj = {}
vars = response.body.split("&").each do |v|
obj[v.split("=").first] = v.split("=").last
end
# oauth_token and oauth_token_secret should
# be stored in a database and will be used
# to retrieve user access tokens in next requests
db = Daybreak::DB.new DATABASE
db.lock { db[obj["oauth_token"]] = obj }
db.close
return obj["oauth_token"]
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 2)
def authenticate_url(query)
# The redirection need to be done with oauth_token
# obtained in request_token request
"https://api.twitter.com/oauth/authenticate?oauth_token=" + query
end
# See https://dev.twitter.com/docs/auth/implementing-sign-twitter (Step 3)
def access_token(oauth_token, oauth_verifier)
# To request access token, you need to retrieve
# oauth_token and oauth_token_secret stored in
# database
db = Daybreak::DB.new DATABASE
if dbtoken = db[oauth_token]
# now the oauth signature variables should be
# your app consumer keys and secrets and also
# token key and token secret obtained in request_token
oauth = #oauth.dup
oauth[:token] = oauth_token
oauth[:token_secret] = dbtoken["oauth_token_secret"]
# oauth_verifier got in callback must
# to be passed as body param
response = TwitterSignIn.request(
:post,
"https://api.twitter.com/oauth/access_token",
{:oauth_verifier => oauth_verifier},
oauth
)
obj = {}
vars = response.body.split("&").each do |v|
obj[v.split("=").first] = v.split("=").last
end
# now the we got the access tokens, store it safely
# in database, you're going to use it later to
# access Twitter API in behalf of logged user
dbtoken["access_token"] = obj["oauth_token"]
dbtoken["access_token_secret"] = obj["oauth_token_secret"]
db.lock { db[oauth_token] = dbtoken }
else
oauth_token = nil
end
db.close
return oauth_token
end
# This is a sample Twitter API request to
# make usage of user Access Token
# See https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
def verify_credentials(oauth_token)
db = Daybreak::DB.new DATABASE
if dbtoken = db[oauth_token]
# see that now we use the app consumer variables
# plus user access token variables to sign the request
oauth = #oauth.dup
oauth[:token] = dbtoken["access_token"]
oauth[:token_secret] = dbtoken["access_token_secret"]
response = TwitterSignIn.request(
:get,
"https://api.twitter.com/1.1/account/verify_credentials.json",
{},
oauth
)
user = JSON.parse(response.body)
# Just saving user info to database
user.merge! dbtoken
db.lock { db[user["screen_name"]] = user }
result = user
else
result = nil
end
db.close
return result
end
# Generic request method used by methods above
def request(method, uri, params, oauth)
uri = URI.parse(uri.to_s)
# always use SSL, you are dealing with other users data
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# uncomment line below for debug purposes
#http.set_debug_output($stdout)
req = (method == :post ? Net::HTTP::Post : Net::HTTP::Get).new(uri.request_uri)
req.body = params.to_a.map { |x| "#{x[0]}=#{x[1]}" }.join("&")
req["Host"] = "api.twitter.com"
# Oauth magic is done by simple_oauth gem.
# This gem is enable you to use any HTTP lib
# you want to connect in OAuth enabled APIs.
# It only creates the Authorization header value for you
# and you can assign it wherever you want
# See https://github.com/laserlemon/simple_oauth
req["Authorization"] = SimpleOAuth::Header.new(method, uri.to_s, params, oauth)
http.request(req)
end
end
end
More detailed explanation at:
https://github.com/lfcipriani/sign_in_with_twitter_sample

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.

Tumblr OAuth Callback URL

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.

Resources