I'm subscribing to Facebook's Realtime API (using the Koala gem, etc.) to get data every time the pages that a user manages, changes...
For some reason, I do not receive any POST data from Facebook, but:
1.) The test 'GET' method works fine through Facebook
2.) When I POST from a different source, it works fine.
3.) The user has granted the manage_pages permissions
4.) The callback url is public - (i.e. why step 1 works)
Here's the code from the controller that handles the requests from Facebook:
class RealtimeupdatesController < ApplicationController
def verify
if request.request_method == "GET"
challenge = Koala::Facebook::RealtimeUpdates.meet_challenge(params,'testtoken')
if(challenge)
render :text => challenge
else
render :text => 'Failed to authorize facebook challenge request'
end
#elsif request.request_method == "POST"
else
Merchant.create(:email => "user#user.com")
end
end
end
Related
I am working on a project that is divided in two apps :
- one rails JSON API that is dealing with the database and is rendering data as JSON
- one "front-end" rails app that is sending requests to the API whenever it needs and displaying the json data in a nice way.
Authentification for the API is token based using gem'simple_token_authentication' meaning that for most of the requests that are sent to the API you have to send the user token & his email in the header for the request to be authorized.
The one who worked on the project before me had also installed Devise authentification system on the API side to allow direct access to the API methods from the navigator after successfull login with email & password.
I just started coding on the "front-end app" that is supposed to request the API and I am having trouble especially with the authentification system.
As Devise was already installed on the API, I thought it would be a good idea to make the user login on the front-end app which would then request devise's methods present on the API for creating user, auth, reseting password...
The problem is that devise's methods are rendering html and not JSON so I actually had to override most of devise's controller. To give you a quick idea of how it works :
You fill the sign up form on the front-end app then the params are sent to the front-end app controller that is then requesting devise's register user method on the API :
1) front-end app controller :
def create
# Post on API to create USER
#response = HTTParty.post(ENV['API_ADDRESS']+'users',
:body => { :password => params[:user][:password],
:password_confirmation => params[:user][:password_confirmation],
:email => params[:user][:email]
}.to_json,
:headers => { 'Content-Type' => 'application/json' })
# si le User est bien crée je récupère son email et son token, je les store en session et je redirige vers Account#new
if user_id = #response["id"]
session[:user_email] = #response["email"]
session[:user_token] = #response["authentication_token"]
redirect_to new_account_path
else
puts #response
#errors = #response["errors"]
puts #errors
render :new
end
end
2) API overrided devise controller :
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
#user = User.new(user_params)
if #user.save
render :json => #user
else
render_error
end
end
def update
super
end
private
def user_params
params.require(:registration).permit(:password, :email)
end
def render_error
render json: { errors: #user.errors.full_messages }, status: :unprocessable_entity
end
end
This works ok. Here I send back the user that was just created on the API as JSON and I store is auth token and his email in the session hash.
My problem is with the reset_password method for which I am trying to reuse some of devise code.
First, I ask for a reset of the password which generates a reset password token for the user who requested the change. This also generates an email to the user with a link (with the token inside) pointing to the reset password form for the specific user. This is working well. I am getting the link in the email then going to the edit_password form on my front-end app :
Change your password
<form action="/users/password" method='post'>
<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
<%= hidden_field_tag "[user][reset_password_token]", params[:reset_password_token] %>
<%=label_tag "Password" %>
<input type="text" name="[user][password">
<%=label_tag "Password Confirmation" %>
<input type="text" name="[user][password_confirmation]">
<input type="Submit" value="change my password">
</form>
When the form is submitted it goes through my front-end app controller :
def update_password
#response = HTTParty.patch(ENV['API_ADDRESS']+'users/password',
:body => {
:user => {
:password => params[:user][:password],
:password_confirmation => params[:user][:password_confirmation],
:reset_password_token => params[:user][:reset_password_token]
}
}.to_json,
:headers => { 'Content-Type' => 'application/json' })
end
which then calls my overrided Devise::PasswordController (update method) :
# app/controllers/registrations_controller.rb
class PasswordsController < Devise::RegistrationsController
# POST /resource/password
def create
if resource_params[:email].blank?
render_error_empty_field and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
render_success
else
render_error
end
end
def update
self.resource = resource_class.reset_password_by_token(resource_params)
yield resource if block_given?
if resource.errors.empty?
resource.unlock_access! if unlockable?(resource)
render_success
else
render_error
end
end
private
# TODO change just one big method render_error with different cases
def render_success
render json: { success: "You will receive an email with instructions on how to reset your password in a few minutes." }
end
def render_error
render json: { error: "Ce compte n'existe pas." }
end
def render_error_empty_field
render json: { error: "Merci d'entrer un email" }
end
end
However the request is always Unauthorized :
Started PATCH "/users/password" for ::1 at 2016-02-05 11:28:30 +0100
Processing by PasswordsController#update as HTML
Parameters: {"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "reset_password_token"=>"[FILTERED]"}, "password"=>{"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "reset_password_token"=>"[FILTERED]"}}}
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms)
I dont understand why is this last request unauthorized ?
Your predecessor likely made a mess of things on the API side just for his convenience.
We know that using cookies for API's is a really bad idea since it leaves the doors wide open for CSRF/XSRF attacks.
We can't use the Rails CSRF protection for an API because it only works as sort of guarantee that the request originated from our own server. And an API that can only be used from your own server is not very useful.
Devise by default uses a cookie based auth strategy because thats what works for web based applications and Devise is all about making auth in web based applications easy.
So what you should do is either remove Devise completely from the API app or convert Devise to use a token based strategy. You also should consider removing the sessions middleware from the API app. Also the Devise controllers are so heavily slanted towards client interaction so that trying to beat them into API controllers is going to be very messy.
Updating a password in an API is just:
class API::V1::Users::PasswordsController
before_action :authenticate_user!
def create
#user = User.find(params[:user_id])
raise AccessDenied unless #user == current_user
#user.update(password: params[:password])
respond_with(#user)
end
end
This is a very simplified example - but the point is if you strip off all the junk from the controller related to forms / flashes and redirects there is not that much you are really going to re-use.
If your front-end app is a "classical" client/server Rails app then you can use a regular cookie based auth (Devise) and let it share the database with the API app. Token based auth does not work well with classical client/server apps due to its stateless nature.
If the front end app is a SPA like Angular or Ember.js you might want to look into setting up your own OAuth provider with Doorkeeper instead.
I currently have a Rails application that is connected to an existing SQL database. I am using Devise for my user management, however the pre-existing User table in the database uses a very customized password encryption method.
There is a web service I can connect to that passes a JSON object with the login information to authenticate whether it is valid or not, and I have to manage my own session and everything after that.
I attempted to follow "Railscast #250", and combine it with Devise and some Stack Overflow searches, but things are not going very well.
This is what I have now, but it isn't doing anything, and I just don't feel like I am on the right track with this.
class SessionsController < Devise::SessionsController
def new
super
end
def create
post_params = {
"RuntimeEnvironment" => 1,
"Email" => params[:session][:email],
"Password" => params[:session][:password]
}.to_json
user_params = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
user = User.authenticate(user_params)
if user
session[:user_id] = user.user_id
redirect_to root_path
else
flash.now.alert = "Invalid Username or Password"
render "new"
end
end
end
This is the JSON Object returned if there is a successful login:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"","ResultCode":0,"User":{"AccountCompleteFlag":1,"CreationDtime":"\/Date(1430848539000-0400)\/","DeleteFlag":0,"Email":"john#doe.com","FailedPasswordCount":1,"HistoricalFlag":0,"IsDirty":false,"IsAdminFlag":0,"IsSiteAdminFlag":0,"LastLoginDtime":"\/Date(1447789258000-0500)\/","NameFirst":"Ttest","NameLast":"test","Password":"TRQt3d2Z7caDsSKL0ARVRd8nInks+pIyTSqp3BLxUgg=","PasswordLockDtime":"\/Date(-62135578800000-0500)\/","PasswordLockFlag":0,"PasswordResetCode":"","PasswordResetStatus":0,"Phone":"1-X-5555555555-","RegistrationSource":"Registration","UserId":100029,"UserType":1,"PhoneInfo":{"AreaCode":"555","CountryCode":"X","Extension":"","FirstThree":"555","InternationalPhoneNumber":"","IsDirty":false,"IsInternational":false,"LastFour":"5555"}}}}
And what is returned for a failed one:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"Invalid email address","ResultCode":1,"User":null}}
Is there a way where I can use Devise's session management while connecting to the API?
You can still authenticate through Devise using the email and password that the user provided. The RestClient would just be like a double check: just make sure that there are no routes that the user can authenticate through besides going through the RestClient. You can check this by doing rake routes.
For checking whether the result code was valid, you can do some JSON parsing as follows:
authentication_response = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
json_authentication_response = JSON.parse(authentication_response)
result_code = json_authentication_response["LoginResultData"]["ResultCode"]
if result_code == 0
# Authenticate
else
# Don't authenticate
end
Short version
Is there a way to have facebook POST back to your return url from their oauth dialog after logging in?
With background story
I am developing a Rails application in which a user can buy products. In order to buy a product, he has to request it by creating a Request. That Request stores the product's id, the product owner's id as well as the requester's id, just to associate the three with each other.
All this happens through a form that is sent, of course, through a post request.
Here comes the tricky part: The user has to be logged in to place a request. They can log in through facebook. In my application controller, I currently have the following method to check if a user is logged in:
def user_signed_in?
unless current_user # from other method
flash[:error] = "Please log in first."
redirect_to root_path and return false
end
true
end
helper_method :user_signed_in?
I want to change root_path to the facebook auth dialog url. At the moment, I have the following:
def user_signed_in?
unless current_user
raise "https://www.facebook.com/dialog/oauth/?client_id=#{ENV['FACEBOOK_APP_ID']}&redirect_uri=#{request.url}&scope=#{User::BASIC_PERMISSIONS}"
redirect_to "https://www.facebook.com/dialog/oauth/?client_id=#{ENV['FACEBOOK_APP_ID']}&redirect_uri=#{request.url}&scope=#{User::BASIC_PERMISSIONS}" and return false
end
true
end
So far, all this works fine - after logging in, facebook redirects me to the right url. However, the redirect would have to happen through a post request, because otherwise the Request record won't be created. Does facebook support that? Ideally, in a way that my post parameters don't get lost along the way? I have checked facebook's documentation on the matter and searched the web, but couldn't find anything.
I'm currently working on a rails Spree application(v 1.3.x).
I have also another application that i have created couple of months ago just suppose myfirstapp.com that is live and so many users have created account on this application so they are registered users now.
So, now i want in my new application which is one i'm currently working on just suppose it is **mysecondapp.com to allow registered users (i.e users who created account on myfirstapp.com) to sign_in in my second app without using my secondapp signup proccess. They should authenticate from myfirstapp.com and get into my secondapp.**
Any way to achieve this?
I think you could use your first app authentication on the seconde one, you can create a link just like facebook or twitter with redirects to a controller and then this controller sends a request to your first app(you will need the security code to do that) and then get the response, save the data you want(user_id or account) and works great...
Api class using protect_from_forgery:
class ApiController < ApplicationController
protect_from_forgery :except => 'custom_login'
end
Doing this you will avoid the default rails protection, with verifies the
<%= hidden_field_tag :authenticity_token, form_authenticity_token -%>
commonly used on rails forms.
Then you can implement your secret hash code, and verifies it like:
class ApiController < ApplicationController
protect_from_forgery :except => :login_from_app2
def login_from_app2
if params[:authentication] == auth_hash
if login(params[:user_credentials])
render :text => "Success" #better you return a json
else
render :text => "Fail login" #better you return a json
end
else
render :text => 'Invalid security hash'
end
end
def auth_hash
"8f2e4b354f193744272fe246ca9e8bf5"
end
end
That way you have a code with only app2 will send on a "post" request to access app1
and you can control the login.
I dont know if this is 100% secure, i think it is, but i will be glad if anyone could explain why this is not a good approach in this case.
Rails AuthenticityToken automatically protects POST/PUT/DELETE requests from CSRF attacks. But I have another use case in mind.
I am showing a video on my site that I don't want to be embeddable on other sites. How this works is that my flash player sends a request for a signed URL from my CDN that expires in a few seconds. Up until now a user had to be logged in to watch videos, so that was the authentication. However now I want any visitor to the site to be able to watch the video without allowing the signed URL to be requested from another site (such as if they embedded our player on their site).
My first thought went to AuthenticityToken since it seems to have these exact semantics... all I need to do is plug it into a GET request. Any ideas?
Rails, opinionated as it is believes that all GET requests should be idempotent. This means Rails of course does not check authenticity tokens for GET requests, even verified_request? gives every GET a pass.
def verified_request?
!protect_against_forgery? ||
request.method == :get ||
!verifiable_request_format? ||
form_authenticity_token == params[request_forgery_protection_token]
end
So we have to write our own logic. We can use form_authenticity token. All this does is create a random string and cache it in the session:
def form_authenticity_token
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
end
We can therefore make a before filter that tests the equality of a url parameter to the session token. Thereby ensuring that only bonafide visitors can view videos.
Controller:
class CDNController < ActionController::Base
# You probably only want to verify the show action
before_filter :verify_request, :only => 'show'
# Regular controller actions…
protected
def verify_request
# Correct HTTP response code is 403 forbidden, not 404 not found.
render(:status => 403) unless form_authenticity_token == params[:token]
end
end
The view:
<%= video_path(:token => form_authenticity_token) %>
To plug the authenticity token in your url:
<%= video_path(:token => form_authenticity_token) %>
In your CDN's controller, you could check if the authenticity token is correct with a before_filter:
def verify_token
render_404 unless form_authenticity_token == params[:token]
end
def render_404
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
end