Ruby ways to authenticate using headers? - ruby-on-rails

I am designing an API system in Ruby-on-Rails, and I want to be able to log queries and authenticate users.
However, I do not have a traditional login system, I want to use an APIkey and a signature that users can submit in the HTTP headers in the request. (Similar to how Amazon's services work)
Instead of requesting /users/12345/photos/create I want to be able to request /photos/create and submit a header that says X-APIKey: 12345 and then validate the request with a signature.
Are there any gems that can be adapted to do that? Or better yet, any gems that do this without adaptation?
Or do you feel that it would be wiser to just have them send the API key in each request using the POST/GET vars?

You probably use an authentication library already. It probably has a way to override the way it checks for authentication. Most likely, the method is named authenticated?. Refer to the documentation for the library you are using, though.
I would not have looked for an existing gem, but implemented it myself; doing so shouldn't be too hard. Here's an example boilerplate implementation:
class ApplicationController < ActionController::Base
def authenticated?
current_user.is_a?(User)
end
def current_user
user_from_session || user_from_api_key
end
def user_from_session
# ... use session[:user] or whatever.
end
def user_from_api_key
User.find_by_api_key(request.headers["X-APIKey"])
end
end
That's as specific as I can get, since you don't say anything about your current authentication setup.

Related

How to validate that a user owns the requested resource through Rails API when using devise_token_auth

I am building an API-only (for now) Rails app to serve as the back end for an Android app I'm building. I was previously using Firebase but wanted to do more processing on application data and I didn't want to bloat the mobile client with all the logic, so I am moving away from Firebase's real-time database and backing the application data using Rails. I was also using Firebase's authentication which is very straightforward. But it seems less complex for the overall system to keep all of this functionality in one place, so I'd like to perform auth and user management in the Rails app as well.
I have installed devise_token_auth (seen here) and finished the basic configuration. The /auth route works correctly if params are provided and creates a user. sign_in and sign_out both successfully create sessions and return some header information. The important parts returned are client, access-token, and uid, which I need to use in future calls to the API. I believe these are invalidated and replaced with each subsequent call. At this part of the flow is where I'm not sure how to proceed. I don't understand how the data in these headers is associated with the user who signed in and how I can validate that they own a resource they request. To summarize the question another way:
How can I sign a user into the API and then validate which user is making subsequent API calls?
My Android app is a task manager, so I need to be able to validate for example that if user 1 requests task 3, that they own that resource. I'm also unsure how to direct index calls using the API. That is, when the index endpoint is hit (/tasks), how can I identify from the data in the API headers which user's tasks I should retrieve.
I haven't been able to find any solid tutorials or samples using devise_token_auth so I'm having trouble stitching together the interaction between the pieces I have now. I know this is a meaty question - thanks in advance for any guidance.
How can I [...] validate which user is making subsequent API calls?
With the current_user method. This is a built-in feature to the devise_token_auth gem.
I need to be able to validate for example that if user 1 requests task 3, that they own that resource
There are many different approaches you could take. You could just write some custom logic in each controller action, using the current_user method (and return 403 forbidden if necessary).
Or, you could use a popular "framework" solution for this such as CanCanCan or Pundit. I, and probably most of the modern community, would recommend Pundit.
I highly advise you to read that library's README, as it's extremely helpful. But for the example above, you could write something like this:
class TasksController
def show
task = Task.find(params[:id])
authorize(task) # !!!
render task
end
end
# app/policies/task_policy.rb
class TaskPolicy
def show?
record.user == user
end
end
(Note that by default, the "user" in Pundit policies calls the method: current_user. This is all explained in the project's README.)
when the index endpoint is hit (/tasks), how can I identify from the data in the API headers which user's tasks I should retrieve
Again, this is all handled as part of Pundit's standard features. You just need to define a TaskPolicy::Scope and call policy_scope(Task) in the controller - as explained here.

How to implement API authentication for Rails app with Authlogic

I have a Rails app with an iOS mobile client.
So far, I've had the iPhone client send HTTP requests to the normal URLs. I want to replace this with a proper API. I'm on Rails 3, and I'm using Authlogic for authentication.
I have watched the railscasts on versioned apis and securing APIs. But since I'm already using authlogic for authentication, I think reimplementing token creation would be unnecessary?
I created the API just as Ryan Bates suggests in this episode with a controller under app/controllers/api/v1/. I have corresponding views with RABL in views/api/v1.
My controller is
module Api
module V1
class RecordsController < ApplicationController
respond_to :json
def index
status = RecordStatus.where("name = ?", "processed").first
#records = current_user.records.where("record_status_id = ?", status.id)
end
def show
#record = Record.find(params[:id])
end
end
end
end
Basically, I've read a lot on the different options to implement (including a bunch of answers on SO) and I'm just really stumped as to what's the best way for me to implement authentication, securely. Should I go to oauth? Can I do it with authlogic? Which option would make it easy to use from the iOS side? Which option is easiest to implement?
Any guidance would be helpful.
Perhaps you could use the single access token stuff that's in authlogic already?
http://rubydoc.info/gems/authlogic/Authlogic/Session/Params

Setting HTTP Headers for Rails form_for

I am currently working on an avatar app powered by Rails where users can upload avatars for their user profile.
I would like to use a custom HTTP header to block public upload requests and only allow requests from my apps. How would I go about doing this with Ruby on Rails?
I am uploading the avatars using AJAX so this may be a bit harder. Also I would prefer not to show the header in the public HTML code otherwise it defeats the object of adding it!
If you add
protect_from_forgery
to your application controller, it will block all NON Get requests from 3rd party links. It will add a hidden input value to each form with an authentication token that will be used to check all data that is sent to the servers.
Further reading
http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
Rails 3.1 - CSRF ignored?
You could implement a custom HTTP header (say X-Foobar-Validity-Status: valid) and check it in a before_filter.
class YourController < ApplicationController
before_filter :check_header
def check_header
unless request.headers['X-Foobar-Validity-Status'] == "valid"
render json: {"error" => "You are an evil attacker. Go away"}
end
end
end
However, I would consider this a bad idea.
Attackers can read the packet dump of your HTTP requests and add the headers, even with jQuery. See the jQuery.ajax headers option.
Instead of using a proprietary header, I would use User-Agent for this purpose.
Instead, I would sugest using the protect_from_forgery mechanism of rails. It makes your life easier and is more secure. Just fetch the authenticy token by a http request in your app and then send it back with your request. This should keep intruders out.

Implementing API for a Rails app

I am building an API for my rails app i am trying to reuse same controllers for both API and app logic. The requests without authenticating a user is fine but how to implement if some actions need an authentication. I am using authlogic, to edit people need some specific permissions which i am checking in the before_filter. If i implement an API using http basic authentication how do i differentiate ?
I think there are two parts to the answer. Firstly you need to set up http basic auth with authlogic. The details for doing that are contained in this SO answer: Rails: Basic Authentication with Authlogic
Then you need to differentiate between API calls and normal browser calls in your controller. Assuming your API is XML or JSON, you can do that with something like this:
class ThingsController < ApplicationController
before_filter :authorize
def authorize
if params[:format] == 'json' || params[:format] == 'xml'
require_http_auth_user # http basic auth for API access
else
require_user # normal authlogic authentication
end
end
end
It may be worthwhile separating into two separate controllers and including the common functionality via a mixin. That way you can auth the controllers separately.

How to create password protected RSS feed in rails

Creating RSS feed in rails is easy. I need a simple way to password protect the RSS feed. I am thinking http basic authentication.
I googled but could not find any article about creating password protected RSS.
I have this in my ApplicationController
def xml_authorize
if request.format == Mime::XML
authenticate_or_request_with_http_basic do |username, password|
username == 'foo' && password == 'bar'
end
end
end
Then I just apply a before_filter :xml_authorize to the actions that I want to password protect for XML requests only, but still want to serve normally with html.
Here's where I got the inspiration from.
Just use whatever auth system you use on your regular controllers. If the user is logged, and session is alive he will be able to download the feed.
How is HTTP authentication any different on RSS feeds than other controller actions? (This is not necessarily a rhetorical question - is it actually different?)
Have you tried just using the Rails HTTP Basic Authentication methods?
Feeds are meant to be fetched in regular intervals without user interaction.
RSS-Feeds can be consumed by something different than a browser. For example,
I have a phone where I can create a widget for the start screen from a rss-feed-link. Great function. Unfortunately authentication does not work for this widget.
And there is no user interface to put in username and password. So the authentication need to be part of the url, with all the bad security implications...
On the other hand you don't need a session to answer a feed-request.
So the solution is a create a special key for the user, and store it in the user table.
Then use it when you display the link to the rss-feed. In the feed-method, you use this key to retrieve the user, but you don't create a session. This way, even when a hacker somehow got a valid key, the hacker can only view the rss-feed, but not access the rest of your application.
If you already use some library for authentication, there may already some solution implemented for this. In Authlogic, is is the class SingleAccessToken, and you need to add a column 'single_access_token' of type string to your user table. Then authlogic creates some cryptic key when are saving the user record. You than add this key as the GET-Parameter 'user_credentials' to the url of the private rss-feed
Like knoopx said, If you use an authentication system like authlogic you should be able to specify the authentication type in the controller. You can then specify http basic authenication. Then you can, if you choose, include the authentication in the URL for the RSS Feed.
e.g:
http://username:password#example.com/rss
(sorry to break the URI up like that, but I don't have enough reputation points to post multiple links :( )

Resources