I was under the impression that Rails will regenerate the form_authenticity_token after any POST, PUT, or DELETE action. But for some reason, after a successful POST to the users resource the form_authenticity_token does not regenerate. I'm free to POST as many time as I would like with the same CSRF token over and over.
I've got a namespaced API and I'm using the RABL gem to build out my responses. This is how I have everything setup...
class Api::V1::UsersController < Api::V1::ApplicationController
...
def create
#user = User.new(params[:user])
render "show", :status => (#user.save ? :ok : :unprocessable_entity)
end
...
end
class Api::V1::ApplicationController < ApplicationController
layout '/api/v1/layouts/application.json.erb'
respond_to :json
before_filter :authenticate_user!
...
end
class ApplicationController < ActionController::Base
protect_from_forgery
end
The post goes through fine, there are no errors or warning in the development.log or in the consoled $ rails s log.
I've check verified_request? from within the create method and it's returning true. I've removed the render and setup a create.json.rabl view with the same code as the show.json.rabl view... no dice.
I'm running Rails 3.1.3 w/ Ruby 1.9.2p290 w/ a cookie session store.
The authenticity token is being sent via request header (X-CSRF-Token)
You were under the wrong impressions. As you can see from the relevant bit of the rails source the form authenticity token stays the same for the lifetime of the session.
Related
I'm new to Ruby on rails. I need to maintain a project which is a complete web app. Now I need to introduce APIs in it. I've searched and got many tutorials on API and web app separately. But didn't get any of them showing how these things will work together. I'm confused how that authentication will work for both.
Here is the application_controller.rb:
class ApplicationController < ActionController::Base
helper_method :sort_column, :sort_direction
protect_from_forgery
before_filter :authenticate_user!
# before_filter :authenticate # HTTP AUTH DISABLED
rescue_from CanCan::AccessDenied do |exception|
render :file => "#{Rails.root}/public/403.html", :status => 403, :layout => false
## to avoid deprecation warnings with Rails 3.2.x (and incidentally using Ruby 1.9.3 hash syntax)
## this render call should be:
# render file: "#{Rails.root}/public/403", formats: [:html], status: 403, layout: false
end
def user_for_paper_trail
if user_signed_in?
current_user.full_name
end
end
def info_for_paper_trail
if user_signed_in?
{ :user_id => current_user.id }
end
end
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "admin" && password == "123"
end
end
end
I need to know how to authenticate APIs? If I use JWT then how to override sign_in methods and do all that stuff separately for APIs and that also look overhead to me because authentication is already there.
Moreover it would be helpful if I get to know how to involve API functions in controller? Like I've user controller and all the methods for that for web app. Now I need the same methods for API. So I need to make new controller for API or that controller can be used?
There are many questions here so I'll try to give a big picture answer:
In general, other controllers inherit from ApplicationController which (in your case) runs a before_filter. The filter can redirect or render and therefore prevent the execution of the specific route. Since all controllers inherit from ApplicationController, the filter is run before every action of your app (assuming the most common default case).
Presumably, API authentication is supposed to work in a different way than for the app's html frontend (perhaps an api key in a header). It looks like your app is using https://github.com/plataformatec/devise. I'd have a look at it to see if you can just "switch on" a suitable authentication method for your API with it.
I hope this helps.
The solution worked for me is to use friendly token with devise_token_auth for api. And here is my before filter now:
before_filter do
if check_api_request
authenticate_api_request
else
authenticate_user!
end
end
I am trying to make an API for a project. I followed instructions from this video - https://www.youtube.com/watch?v=lgdUqtw4weg&t=165s
Basically in the video, I make a token column for the user model. And set up the controller so I can use a post method.
But when i run the POST.
I get the error saying
{"error":"You need to sign in or sign up before continuing."}
I think Devise is interfering with the POST and sees that the user has not logged in when trying to visit non public pages.
How do I get past this and get the token ?
Here is my api controller.
class Api::V1::UserSerializedController < ApplicationController
protect_from_forgery except: :index
respond_to :json
def create
user = User.where(email: params[:email]).first
if user.valid_password?(params[:encrypted_password])
render json: user.as_json(only: [:email, :authentication_token]),status: :created
else
head(:unauthorized)
end
end
def show
end
end
You are inheriting ApplicationController and i'm guessing you have authenticate_user! before action set there.
You'd have to either change the parent class to something like ApiController (i'd prefer this)
OR
skip this action for your Api::V1::UserSerializedController
I'm actually building a system where users, after registering on my application and confirm their account by email, will be redirected to a wizard process page (build in Js) where they will be asked to fill in some more information.
To do that, I create a function in my app controller which will redirect the user to that process page, according to certain condition :
// application_controller.rb
class ApplicationController < ActionController::Base
before_filter :fill_in_profile
protected
def fill_in_profile
if user_signed_in?
if current_user.employee? && !current_user.filled?
redirect_to new_profile_path
end
end
end
end
The problem here is that Im getting a 302 status error because it's creating an infinite redirection loop.
So I wanted to know how you guys were handling those kind of page and process?
Thanks
Add a skip_before_action in your ProfilesController (or wherever new_profile_path points to):
class ProfilesController
skip_before_action :fill_in_profile
def new
# …
end
end
I'd love to recommend a link into the Rails API, but neither AbstractController::Callbacks nor AbstractController::Callbacks::ClassMethods.skip_before_filter looks promising…
I have a Rails 4 app (that was upgraded from Rails 3) in which I decided to delete one of the controllers. I then moved the methods from that deleted controller to the ApplicationController, which included before_filter :authenticate_user!
Here's what my ApplicationController looks like now:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!
respond_to :json
def index
gon.rabl
#user = current_user
gon.rabl "app/views/users/show.json.rabl", as: "current_user"
end
def markdown
require 'redcarpet'
renderer = Redcarpet::Render::HTML.new
extensions = {}
Redcarpet::Markdown.new(renderer, extensions)
end
helper_method :markdown
end
Now, I'm getting this error:
ActionController::UnknownFormat in Devise::SessionsController#new
I think this might be due to the fact that you have set your application controller to respond only to json. If your Devise Controller inherits from ApplicationController (I think this is the default), then it will expect to see a content-type: json header, or your urls must all end in .json
You shouldn't have the index method defined in application_controller. You should move it to the appropriate controller. If this is something you want to do before every action you might want to try something like this:
before_action :gon_user, only: :index
private
def gon_user
gon.rabl
#user = current_user
gon.rabl "app/views/users/show.json.rabl", as: "current_user"
end
Though i've to be honest that i'm not sure about the gon stuff, can't remember if it was for moving data from ruby to javascript or for responding to ajax/json request.
Thanks Slicedpan. Got me thinking about a
respond_to :json
Used in my Rails Application as an API with Angular. As in my Rails controllers I use for requests from my Angular Services.
respond_with
In my case I ended up adding html to the respond_to:
respond_to :json, :html
The Default Mime Types can be seen here:
http://apidock.com/rails/Mime
I'm having an issue with using OmniAuth with Rails 4.0.0.beta1 where a session value set in SessionsController is not being persisted across a redirect. I am trying to figure out if it's something in my code, a bug in Rails 4, or an incompatibility with the OmniAuth gem. I'm using the OmniAuth developer strategy.
I'm not sure if this means anything, but if I put a debugger in SessionsController#create after the session[:user_id] = user.id line and inspect class the session object, I get:
ActionController::RequestForgeryProtection::ProtectionMethods::NullSession::NullSessionHash
However, if I inspect that same session class in a different application running Rails 3.2 I get:
Hash
Maybe OmniAuth cannot handle the NullSessionHash object appropriately?
sessions_controller
class SessionsController < ApplicationController
skip_before_filter :authenticate_user!
def create
user = User.find_or_create_by_auth_hash(auth_hash)
session[:user_id] = user.id
redirect_to root_path
end
protected
def auth_hash
request.env['omniauth.auth']
end
end
config/initializers/secret_token.rb
MyApp::Application.config.secret_key_base = 'REMOVED'
config/initializers/session_store.rb
MyApp::Application.config.session_store :encrypted_cookie_store, key: '_my_app_session'
It turns out this is related to an issue between Rails 4 and using the omniauth gem developer strategy. I fixed it in https://github.com/intridea/omniauth/pull/674
Update
Since the PR didn't get merged, I figured I'd post an easy solution that seems to work for most people. The issue is that the developer strategy does not include the form authenticity token, which Rails requires by default. You can disable this in your session controller with the following:
class SessionsController < ApplicationController
skip_before_filter :verify_authenticity_token
# ...
end