Authenticating with Devise in a Facebook Canvas - ruby-on-rails

I am creating a Canvas Facebook application. In this canvas i want to show a built with rails (v.3.2.8) application, which authentication part is managed by Devise 2.1.2. I followed the Facebook documentation on Server side Authentication.
My canvas url is a controller action which do the following :
- get the facebook signed_request, decode it, redirect to the facebook authorization request page
- once the user authorized the application, and the facebook token is sent back to my canvas url, I fetch the User infos, save them in my User model and sign in the user with devise.
My problem is : even after i signed in the user, what i see in the canvas chrome is signed-out page only. Even if the user is correctly saved in the DB, the sign_in step doesn't work, when i check the session, it is empty ?
Can anyone help me on this please ? Thanks. Here is the code of the canvas action :
def canvas
#encoded_sig, #payload = params[:signed_request].split('.')
#payload += '=' * (4 - #payload.size.modulo(4))
# Decode the signed_request parameter
#signature = Base64.decode64(#encoded_sig)
#datas = JSON.parse(Base64.decode64(#payload))
# TODO Check Encoding Algorithm
# TODO Check Signature
if( !#datas.has_key?("user_id") && !#datas.has_key?("oauth_token") )
#oauth_url = "https://www.facebook.com/dialog/oauth/?client_id=#{#facebook_config[:app_id]}&redirect_uri=#{#facebook_config[:canvas_page_url]}&scope=email,user_location,user_relationships"
# render
else
user_datas = FbGraph::User.me(#datas["oauth_token"]).fetch
#user = User.find_by_email(user_datas.email)
if #user.nil?
#user = User.new(first_name: user_datas.first_name, last_name: user_datas.last_name, email: user_datas.email , password: SecureRandom.hex(10), terms: true, fb_uid: user_datas.raw_attributes[:id], access_token: #datas["oauth_token"])
#user.save!
#user.confirm!
#user.create_coachee_profile(name: user_datas.name, gender: user_datas.gender, location: '', language: user_datas.locale, relationship_status: user_datas.relationship_status, user_id: #user.id)
sign_in(:user, #user)
redirect_to user_coachee_profile_url(current_user.id)
else
sign_in(:user, #user)
redirect_to root_url
end
end
end

Related

Rails devise signup with JWE token payload

I need to create functionality where other microservice creates a link to my app with JWE token as a params in which is encrypted json user params e.g.:
json_payload = {
email: 'test#test.com',
external_id: '1234'
}.to_json
The flow should be:
user gets the url generated by different app with JWE token as params (e.g. http://localhost:3000/users/sign_up/?jwe_token=some_gigantic_string_123)
enter that url
under the hood Rails app creates new user based on encrypted params
after successful user creation redirect that user to the edit page
So as you see, the user shouldn't notice that there was an account creation but the first page it will see is the password edit.
Am I not doing some sort of antipaternity here with below code? Please take a look:
class Users::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
return redirect_to(new_user_session_path) unless params[:jwe_token]
json_payload = JWE.encrypt(payload, rsa_key)
payload = JSON.parse json_payload
user = User.new(user_params)
if user.save
redirect_to generate_password_url(request.base_url, user)
else
redirect_to new_user_session_path, alert: 'Something went wrong'
end
end
private
def generate_password_url(base_url, user)
path = edit_password_path(user, reset_password_token: fetch_token(user))
"#{base_url}#{path}"
end
def fetch_token(user)
user.send(:set_reset_password_token)
end
end
I assume that if user creation is to be handled by a link I have to use new method. Am I not creating an antipattern here? Is there any other way to do so?

How to get back to my Js App after authenticating with an Oauth server via Rails API?

I'm building an application with Rails 5 API and ExtJs.
My ExtJs single page app loaded via the public/index.html.
I get redirected to the Oauth Login page with the required params via a button click in the ExtJs app.
Then the Oauth Server calls my Rails App and go through authentication and getting the token back.
All works fine.
My problem is that now I have my user loaded, updated, access_token ready but I need to load the single page app in the browser and pass on the access_token. Somehow I can not manage this.
def login
if params[:code]
response = request_token(params[:code])
if response.header.code == '200'
token_data = JSON.parse response.body
user_info = JWT.decode(token_data['id_token'],nil,false).first
#user = User.find_by email: user_info['email']
#user ? #user : #user = User.new
#user.name = "#{user_info['given_name']} #{user_info['family_name']}"
#user.access_token = token_data['access_token']
#user.access_token_created_at = Time.zone.now
#user.token_data = response.body
#user.save
render file: '/public/index.html'
else
redirect_to('/', status: response.header.code, alert: response.header.message)
end
elsif params[:error]
redirect_to('/', status: 401, alert: params[:error])
end
end
I either get stuck in an empty browser window with the localhost:3000 url and the code param or if I redirect I get a message with You are being redirected that reloads the window but I think the parameters are not passed on.
I usually use doorkeeper gem to create OAuth server and save redirect_uri to session[:return_to].
resource_owner_authenticator do
session[:return_to] = request.fullpath
current_user || redirect_to(new_user_session_url)
end
Then after authentication, inject javascript
window.location = redirect_uri + params
OR create XMLHttpRequest to authentication server and then parse response like this:
parseAccessToken: function(response) {
return {
accessToken: response.match(/access_token=([^&]*)/)[1],
expiresIn: response.match(/expires=([^&]*)/)[1]
};
}

Rails + OmniAuth Facebook: how to obtain Access Token?

I am trying to fetch the list of friends from Facebook. Sign in through Facebook is not a problem, but the problem is to fetch person's friends - because of access token.
puts request.env["omniauth.auth"].inspect
puts '==='
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
#fb_user = FbGraph::User.fetch(#user.uid).friends
puts #fb_user.inspect
The problem is on the #4 line - in this case I am getting error
OAuthException :: An access token is required to request this resource.
When I put there something like this:
#fb_user = FbGraph::User.fetch(request.env["omniauth.auth"].credentials.token).friends
I'll get
OAuthException :: (#803) Some of the aliases you requested do not exist: PRINTED OUT TOKEN
What's the proper way to obtain the access token?
EDIT: Current flow
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
#fb_user = FbGraph::User.fetch(request.env["omniauth.auth"].credentials.token).friends
if !#user
flash[:error] = 'This email address is already used in the system.'
redirect_to :back
elsif #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
In User model:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:provider => 'facebook', :uid => data.id).first
user
elsif user = User.where('email = ? AND provider IS NULL', data.email).first
return false
else
...saving data...
end
return user if user
end
You can get an access token for test purposes via the Facebook Graph API Explorer. Make sure you select the proper fields that you want access to, and click "get access token". A more permanent solution is to register your app with Facebook so that you will be able to continually make requests without the token dying.
You should look into the Facebook OAuth dialogue.
I'm assuming you're trying to use the OAuth2 strategy instead of the Javascript SDK. Make sure you have set up a callback url like so:
client.redirect_uri = "http://your.client.com/facebook/callback"
In the controller that handles your callback, you should do something like this:
client.authorization_code = params[:code]
access_token = client.access_token! :client_auth_body
FbGraph::User.me(access_token).fetch
Make sure you've let fb_graph know what your app's id and secret are. You should look into this stackoverflow to keep your apps info safe.
I'll also plug the koala gem

devise + omniauth facebook : current_user nil after sign_in

We have registration and login via facebook using rails, devise, and omniauth. We're hosted on Heroku, running two web dynos.
sometimes login with facebook is failing. The actual handshake between our app and facebook is fine. In fact, in the code below #user is an actual User model instance, the omniauth data Hash contains all of the data from FB, sign_in seems successful AND the current_user is set.
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
#user = User.find_for_facebook(env["omniauth.auth"].extra.raw_info, current_user)
logger.info ">>>>>> OMNIAUTH FB BEGIN >>>>>>"
logger.info env["omniauth.auth"].inspect
logger.info "User is: #{#user.inspect}"
session["devise.facebook_data"] = request.env["omniauth.auth"].except("extra")
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
res = sign_in(:user, #user)
logger.info "Sign In Result: #{res.inspect}"
logger.info "CURRENT_USER: #{current_user.inspect}"
logger.info "<<<<<< OMNIAUTH FB END <<<<<<"
respond_to do |format|
format.json
end
end
end
The client-side does a redirect (different actions based on login context) to another URL on our site upon successful login. At that URL, we check for current_user... but current_user is returning nil. If the login process is repeated a few times, eventually it works.
We're on
Rails 3.2
Devise 2.1.2
Omniauth-facebook 1.4.0
Ruby 1.9.3 p194
I don't know whether this is really the solution to what you've described, but I wonder whether this:
#user = User.find_for_facebook(env["omniauth.auth"].extra.raw_info, current_user)
should read as this?
#user = User.find_for_facebook(request.env["omniauth.auth"].extra.raw_info, current_user)
that's how mine is anyways. NOt really sure that would explain why your app is arbitrarily letting the user sign in however.

Rails: Calling Devise authenticate_user! and handling invalid user/password exception

I have a popup that will only allow to view/save some information if the user is authenticated.
I am using devise.
In the controller before_filter it checks if user is signed in and if not, show a sign in page.
This sign in page is ripped down version of the site's sign in page, so that it fits nicely to the popup.
On the authenticate action I call authenticate_user!. Everything works fine when the user enters valid credentials. But when the credential is invalid, devise automatically redirects to site's sign in page (which as I stated is different and not fit for a popup)
I tried appending a rescue to the call, but to no avail.
Anyone could suggest a better/right way to do this please? :)
def authenticate
authenticate_user! rescue redirect_to "/popup/sign_in"
if user_signed_in?
respond_to do |format|
format.html {
flash[:notice] = I18n.t("logged_in_succesfully")
redirect_back_or_default(accounts_path)
}
else
flash[:error] = I18n.t("devise.failure.invalid")
render "/popup/sign_in"
end
end

Resources