I have the following piece of code:
class Foo < ActiveRecord::Base
protect_from_forgery
end
My doubt is when the protect_from_forgery will be called? when an instance of Foo is created?
Thanks in advance
Someone with more knowledge than myself might know a better answer but here is how I understand it:
When the browser sends a post request, rails includes an additional authenticity token with the requests that corresponds to that users session. If I knew another users authenticity token, I could add an html element on the page that includes their token and submit requests posing as their user. This is called Cross Site Request Forgery. To protect your site from such attacks, rails includes a method called protect_from_forgery. This method should be placed at the top of your Application Controller so check each request for authenticity.
Further reading can be found on the Rails Guide to Security.
Related
I'm using Rails 4.1.0.
I am in a project were my options are very limited. I need to have a form submit data to an external API. I stored the values of the form in the session because this application is multi-form based.
The problem is that when the API POSTs back to my Rails application, the session is nullified.
I know this happens because protect_from_forgery in my app/controllers/application_controller.rb
How can I keep the session just a little longer, until the API POSTs back to my confirmation page (saying the form was submitted successfully)?
You can turn off request forgery protection for just a single action:
skip_before_action :verify_authenticity_token, only: :my_action_name
Replace :my_action_name with the name of the action the API POSTs back to.
Source: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html#method-i-protect_from_forgery
I've implemented my own classes for handling authorization via OAuth to GitHub using Faraday in Ruby. I've verified that under the following conditions:
Not logged into GitHub
No token exists for the app
that a request for authorization via GET to "/login/oauth/authorize" with a random state variable:
Redirects to the GitHub login page
Redirects to the Authorize Application page after login
Executes callback to my app with temporary code after authorizing
Responds with access_token when I POST to "/login/oauth/access_token" with temporary code
The problem I have is when I alter the first condition, I'm not already logged into GitHub. The same GET request is sent to GitHub, I see the correct URL with the right parameters. I then see what appears to be the correct redirect by GitHub with a return_to parameter, but it quickly just redirects again back to the GitHub home page.
I'm hoping it's something easy like forgetting a header parameter or something, and someone might spot the problem right away. Anyway, any help is appreciated...
Code to setup Faraday connection:
def connection
#connection ||= Faraday.new(url: 'https://github.com') do |faraday|
faraday.request :url_encoded
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
end
Code to send authorization request:
def request_authorization(client_id, redirect_uri, redirect_id, scope, expected_state)
response = connection.get '/login/oauth/authorize', {
client_id: client_id,
redirect_uri: "#{redirect_uri}?id=#{redirect_id}",
scope: scope,
state: expected_state
}
if response.status == 302
response.headers[:location]
else
nil
end
end
I didn't show the code, but my controller does a redirect to the URL reply from request_authorization(). Again, I definitely see the redirect from my controller in both cases, but the second case seems to encounter something GitHub didn't like in the redirected request. I assume it then redirects to the home page and never replies to my app because of this unknown problem in my original request.
Thanks,
David
Ivan from GitHub was a great help in finding the answer to my question. I had assumed the problem was some detail with using Faraday or OAuth, but it turns out the problem was a basic assumption that proved wrong. Hopefully this will help others that run into a similar misunderstanding.
I had assumed that a user of my app that wanted to connect to GitHub (or another OAuth service) would issue something like a "connect" request to my app. My app would then generate the OAuth authorization request to GitHub, handle any redirects, and eventually wind up presenting the Authorize App page to the user for acceptance.
Turns out I just needed to make the "connect" request actually a link to directly make the authorization request to GitHub. My app then only has to worry about handling the callback, which it already did. Easier and works in all cases now.
Turns out the wrong approach worked when not logged in due to it being the simple case. It failed when logged in because I wasn't handling session state that normally the browser would provide.
A more careful read of the OAuth RFC cleared up my confusion about where requests and responses are handled for the User Agent and for the Client.
I'm building an API with Sinatra (using Angular for the client side and want others to have access to API) and have it also be an OAuth provider. I am wondering what the best route to take (work off existing gems or roll own solution off Warden or something).
Have used devise and doorkeeper for authentication and oauth before with Rails, wondering what best solution for Sinatra is.
Ideally I don't want the views or be able to extend/mod the actions of an existing solution, as I'm interacting with it purely as an API.
I just recently did the same thing using the following answer from S/O
What is a very simple authentication scheme for Sinatra/Rack
It implies a user model, but instead of using that, I just set a user and admin password in my config file. Then I had a login form that just took a password. When the user enters that password, I checked it against the one in settings and set the session['user'] to :admin or :user according to whichever it matched (or nil if none). Then on each of my routes, I called auth: :user or auth: :admin accordingly.
APIs normally accept your login request and send you an authentication token which you need to pass back in each call. This is very similar to cookie based sessions where your browser automatically passes back the cookie which is acquired on initial website visit.
From what I've seen in Sinatra's docs, you could make a session-based authentication system like this:
enable :session
disable :show_exceptions
use Rack::Session::Pool,
key: 'session_id'
post '/login' do
user = User.login_success(params)
halt 401 if user.nil?
session[:user] = user
200
end
get '/fun' do
user = session[:user]
halt 401 if user.nil?
halt 403 if !user.has_permission_for '/fun'
"fun was had"
end
Now all you need to do in your client is to pass back the cookie token returned in response to initial visit when requesting an API function. This can be done with any web client library that supports cookie stores (such as libcurl) or by inserting the session cookie into the request header manually. Rack::Minitest functionality also supports cookies, so you can test your API with minitest.
See Sinatra API Authentication.
Quick summary:
Sinatra has no built-in auth.
It's best to build auth yourself (see the link).
There are gems available, but you probably won't need them for something as simple as an API.
I am right now developing web APIs with Ruby on Rails. When the Rails app receives POST request without any csrf token, the following error message shall happen. Because the app has no views.
WARNING: Can't verify CSRF token authenticity
So my question is how can I escape csrf token check safely in this case?
Thank you very much in advance.
You can do this by adding
skip_before_filter :verify_authenticity_token
to your controller. This way all incoming requests to the controller skips the :verify_authenticity_token filter.
For rails 4 it should be
skip_before_action :verify_authenticity_token, only: [:one_or_two_actions_here]
Note that you should avoid skipping verify_authenticity_token on all actions of your controller, instead use the option only to skip only where you have to. See the docs
I have an app without models or any database connectivity.
I have a method defined in the ApplicationController called "api_call" which does all api calls within the app. This method uses the ruby session to store things about the user, such as authentication info, access token info, and user info. In the session I store an Authentication hash for sending to the api for security when the user is logged.
Two things:
I'd like to put all api calls (the api_call method) in models (that don't use db or validation), but the problem is I don't have access session.
If I use a module, the module in the model also doesn't have access to the session.
If I create a model class without using ActiveRecord, should I use class methods rather than object methods?
How about passing the "authentication hash" to the API model's constructor?
class Api
def initialize auth
#auth = auth
end
end
class FooController < ApplicationController
def index
api = Api.new session[:auth]
end
end
Also, if you haven't see Pratik Naik's article about this, it's pretty funny.