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.
Related
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
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
I want users to test my application without registering for an account. In order to do this I thought about including a link in my commercial site that would make a POST to my application site (http://app/login using devise) sending a fixed user and password, and thus creating a session. I tried with link_to with :method => 'post' but I dont know how to send the params in the POST body! Any suggestions?
I'm trying with this
link_to 'Blah', {:params => 'asdf'}, {:method => :post, :href => 'http://localhost:3001/login'}
In order to pass the query params, you need to put them in as part of the URL like you would for a GET request. Like so:
<%= link_to "Click Here", "/login?username=testuser&password=moojuice", :method => :post %>
or alternatively if you are using Rails route helpers
<%= link_to "Click here", login_path(:username => "testuser", :password => "moojuice"), :method => :post %>
I'm not exactly sure based on the original question if you will be using this link in the same Rails application as the one you are logging into. If you aren't, there is one other slight gotcha. When posting from an external site, Rails is going to most likely ignore your request because you will be missing the CSRF token that Rails automatically generates when you loads a page. In order to accomplish this, you're going to have to put a protect_from_forgery method call in the controller that handles your login. Assuming your /login route maps to the SessionsController action create:
class SessionsController < ApplicationController
protect_from_forgery :except => [:create] # Need this to ignore CSRF token
def create
# Login logic
end
end
That being said, I wouldn't suggest removing the CSRF token because this will make your application less secure. I would suggest just implementing a special action in your SessionsController that just logs them in to their own Session like so:
class SessionsController < ApplicationController
def create
# Login logic
end
def demo_account
# Logic to create session for demo account
end
end
This way you can just forget about a link that does the POST request and just have a GET route that maps /demo_login to SessionsController's *demo_account* action. This would additionally allow this link to be used anywhere as opposed to just in your application (like in an email as one of the previous commenters #allaire suggested). But if you want it to work from a link in your Rails application, then just ignore the part of my answer about the CSRF token.
How would I set in my rails app so that if a first time user comes to my site www.example.com they see a page that they can sign in but if an already logged in goes to www.example.com it now displays their own posts but still at the same www.example.com url.
Would I do something like render template based if they are logged in or is there some other way to do this?
You can set the users#home to be the root URL:
UsersController:
def home
if logged_in?
#blogs = current_user.blogs
render :action => 'logged_in'
else
render :action => 'non_logged_in'
end
end
Have 2 files in the app/views/users folder:
logged_in.html.erb & non_logged_in.html.erb
A great article was writen by Steve Richert. He is using advanced constraint when defining the route, see here
It depends on how you are making your log in logic
Usually you should have two actions, one for home/login form and another for user logged in home. You can make a before_filter on your application controller, so you can test if the user is logged in or not and then redirect him to home (logged out) if not.
If you are not using your own code or another solution I would like to recommend you this gem called devise, it implements a lot of login logic itself and is easy to change too.
EDIT: I think this solutions is better than the others that were presented and I didn't put the code (although it is quite the same code of the before_filter link), so here it is:
class ApplicationController < ActionController::Base
before_filter :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
render :controller => 'home', :action => 'not_logged_in'
else
# whatever code you need to load from user
render :controller => 'home', :action => 'logged_in'
end
end
end
This solutions works perfectly because it tests if the user is logged in in every controller/action he tries to access.
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