How to generate an API Key and Secret in Ruby on Rails? - ruby-on-rails

E.g. API_key: 4faa86aa5848207502000002 and API_secret 7375d7d1e89d3d602b184432fbcf3c09c7cb30676f19af9ac57d228be401.
Should I use SecureRandom?
Thanks!

ActiveSupport::SecureRandom would work for the actual generation, but you should also consider a way to invalidate and reset the token on different events.
Since you're using Devise, take a look at the Token Auth Strategy. You could write a similar strategy with two tokens (API Key and API Secret, respectively). You need to write both the strategy and the Model, but in both cases the Token Auth example gets you pretty far.
As a starting point (from the Token Auth example), your model should declare both required parameters.
module Devise
module Models
module APIKeyAuthenticatable
...
def self.required_fields(klass)
[:api_key, :api_secret]
end
def reset_keys
self.api_key = self.class.api_key
self.api_secret = self.class.api_secret
end
You might also want to read Custom authentication strategy for devise. If you're looking to provide a more full-featured API auth solution atop devise devise_oauth2_providable looks pretty good.

I have tried authlogic_api. It was fairly easy to implement.

Related

Rails authentication across apps/servers

I've been developing my rails apps whilst keeping them as modular as possible. I'm trying to implement different parts underneath as services.
Say an example of Facebook:
a) A MainApp that allows the user to have a wall, posts, etc.
b) A PhotoApp that stores photos, allows the user to see his photos, etc. This is a standalone app that will have a REST API that can be used by MainApp as well.
I was thinking of using OAuth as a Single Sign On solution (as in this tutorial http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on/) where each app will be authorized via OAuth and will get access to the current user session based on the cookie.
First question: Is this a viable solution?
Second question: I want to be able to call the PhotoApp API from the MainApp server (not from the user's browser). How would authentication work in this situation?
Third question: How would this work if say I had a service that used node.js?
Yes, SSO using OAuth is a viable solution, but it's not the simplest one. When building anything new, OAuth 2.0 is the way to go. The OAuth standards cover a lot of ground.
The primary advantage of OAuth is that it allows users to give 3rd party apps access to their account without disclosing their password to the 3rd party. If you are not seriously providing such interoperability, then OAuth is probably overkill.
Given the complexity, I offer a different pair of solutions:
For Single Sign On
The trick is to share the session ID cookie between hosts within your domain & to use a shared session store (like ActiveRecordStore or a cache-based store.)
Every Rails app has a "secret" that is used to sign cookies. In newer Rails apps this is located in /config/initializers/secret_token.rb. Set the same secret token in each application.
Then, configure the session to allow access from all subdomains:
AppName::Application.config.session_store :active_record_store, :key => '_app_name_session', :domain => :all
For Internal API calls
Use a good shared secret to authenticate over HTTPS connections. Pass the secret in the "Authorization" header value.
You can use the shared secret easily with other architectures (like node.js). Just make sure you always use HTTPS, otherwise the shared secret could be sniffed on the network.
You could look at a Service Oriented Architecture solution as proposed by Jeremy Green at Octolabs during the 2014 RailsConf.
The blog post with all the resources (repos, demos, etc.) is located here: http://www.octolabs.com/so-auth
And the video that explains everything is here: http://www.youtube.com/watch?v=L1B_HpCW8bs
This centralized SSO is no simple task but Jeremy has done an excellent job talking about Service Oriented Architecture and sharing exactly how you might put this system together.
I recently had a similar problem of wanting to share session data between Rails and an Erlang app. My solution was to write a Rack::Session::Abstract::ID class that stored sessions in Redis as hash vaules. It doesn't call Marshal.dump on String types. This allows non-ruby applications to use some of the session values if they have the session_id.
require 'rack/session/abstract/id'
class MaybeMarshalRedisSession < Rack::Session::Abstract::ID
def initialize(app, options = {})
#redis = options.delete(:redis) || Redis.current
#expiry = options[:expire_after] ||= (60 * 60 * 24)
#prefix = options[:key] || 'rack.session'
#session_key = "#{#prefix}:%s"
super
end
def get_session(env, sid)
sid ||= generate_sid
session = #redis.hgetall(#session_key % sid)
session.each_pair do |key, value|
session[key] = begin
Marshal.load(value)
rescue TypeError
value
end
end
[sid, session]
end
def set_session(env, sid, session, options={})
#redis.multi do
session.each_pair do |key, value|
# keep string values bare so other languages can read them
value = value.is_a?(String) ? value : Marshal.dump(value)
#redis.hset(#session_key % sid, key, value)
end
#redis.expire(#session_key % sid, #expiry)
end
sid
end
def destroy_session(env, sid, option={})
#redis.del(#session_key % sid)
generate_sid unless options[:drop]
end
end
You can use this from rails with:
MyApp::Application.config.session_store MaybeMarshalRedisSession
From Rack with:
use MaybeMarshalRedisSession
And from elsewhere with:
redis.hgetall("rack.session:#{session_id}")
If you want to call PhotoApp from your MainApp or Node.js you can make a HTTP request that includes your user's session cookie.

Digest authentication in Devise

I'm using Rails 3 and Devise for authentication. I have a proper working devise for the website and basic authentication for API (json handler). How do I enable the digest authentication?
Their Wiki is telling me to add
def http_authenticate
authenticate_or_request_with_http_digest do |user_name, password|
user_name == "foo" && password == "bar"
end
warden.custom_failure! if performed?
end
Where do I add it to and how do I make user_name/password match?
That wiki entry sure assumes a lot.
My best guess is you need to add it to the appropriate controller (or the Application controller if you want it for everything).
And then add a :before_filter :http_authenticate!
You could also try tracking down the person who wrote that wiki page and asking them.
Note. This relies on Warden to perform your authentication - Devise only handles accounts.
One of the reasons this stuff isn't documented so well is most people use a sophisticated authentication management system (eg. OmniAuth), and something else for permissions/authorization eg. DeclarativeAuthorization or CanCan if you prefer something more light weight.
HTTPBasic (and I assume Digest) tends not to play nicely with these.

Prevent Authlogic from establishing a session/cookie for non-HTML requests

I'm using Authlogic and Rails 3. On top of the regular browser-based user experience (logging in via form and whatnot), I'd like to implement an API.
Authlogic seems to support single access tokens that don't persist by default. I supply them by adding a GET argument as in:
/users.xml?user_credentails=my_single_access_token
Question: Is there any way I can have Authlogic accept the API key via HTTP Basic Auth? Highrise does something just like this, allowing for:
curl -u 605b32dd:X http://sample.highrisehq.com/people/1.xml
The same with Freshbooks:
curl -u insert_token_here:X https://sample.freshbooks.com/api/2.1/xml-in -d '[xml body here]'
How I would go about imitating this functionality? I can't even figure out where the input data (postdata from forms, HTTP basic, API token) are taken in. I've boiled it down to a call to UserSessions.find with no arguments, but I lose track of it after there.
Any help would be much appreciated!
Related question: I'd also like to disable session persistence (make it so that no cookie is stored) if HTTP basic is used. Any help on this too would be appreciated!
If you're implementing an API, you could consider building a separate Rack application that is then mounted at '/api/1.0/...' and shares your models.
That way you are not tying yourself into having your API directly related to your public routes, which could be difficult to construct for the API user.
A good approach would be to create a simple Sinatra application that exposes just the methods that you want, and to then create a separate authentication strategy:
require 'sinatra'
require 'active_support' # all the Rails stuff
require 'lib/user' # your User class
require 'sinatra/respond_to' # gem install sinatra-respond_to
Sinatra::Application.register Sinatra::RespondTo
use Rack::Auth::Basic, "API", do |username, password|
User.find_by_login(username).valid_password?(password)
end
get '/api/1.0/posts' do
#posts = Post.recent # assuming you have a Post model...
respond_to do |wants|
wants.xml { #posts.to_xml }
wants.to_json { #posts.to_json }
end
end
get '/api/1.0/users/:id' do
#user = User.find_by_login(params[:id])
# Careful here - don't release personal details!
respond_to do |wants|
wants.xml { #user.to_xml }
wants.to_json { #user.to_json }
end
end
Versioning your API with a '1.0' (or similar) in the path means that if you change your models you can create a new version of your API without breaking your users' existing code.
Using this you should be able to allow users to authenticate with HTTP Basic in the form:
curl -u steven:password http://example.com/api/1.0/users/steven.xml
curl -u steven:password http://example.com/api/1.0/users/steven.json
curl -u steven:password http://example.com/api/1.0/posts.xml
To get this running, save it as 'api.rb', and either run it as a Rack Middleware, or create a 'config.ru' file like so:
require 'api'
run Sinatra::Application
And then from that directory:
rackup
Disclaimer: I'm not a 100% this is possible in the way your describing without hacking up Authlogic's core functionality.
The first issue your going to have is that authlogic prevents the use of SSO tokens for authentication unless the request is ATOM or RSS to override this you need to pass a config paramater see here: http://rdoc.info/github/binarylogic/authlogic/master/Authlogic/Session/Params/Config
To the core issue: I don't see any 'easy' way to handle this functionality, however what you could do for something like curl is pass the user token as a paramater (using the -G option) just like you would when visiting the url.
cURL Documentation: http://curl.haxx.se/docs/manpage.html
Forgive me if I misunderstand your question, but I think the answer is a simple "no." You're mixing two metaphors here. If you want a secure API key, use the single access token; if you want to use http basic access authentication, you need a different base64 glyph -- and http basic auth isn't particularly secure (unless used over https, which isn't generally practical).
In more detail:
Per the wikipedia, http basic authentication is intended to provide a username and password in a simple, standard, but fairly insecure base64 encoded glyph.
To use basic auth, then I believe you want to generate the glyph via a simple
Base64.encode64("#{user.name}:#{password}")
...and I'd probably do this by having the user type their password, since you can't derive the password from the crypted_password that authlogic stores in your database.
But the upshot is that this is a very different beast from the single_access_token, and the two can't be mixed.

Using devise "rememberable" without cookies

I have a working Rails site that uses devise to manage users. For session management, I am using devise's rememberable strategy, which stores and retrieves encrypted authentication information from a user's cookie.
I'm implementing a multi-photo upload widget that uses flash. Flash does not support sending cookies along with requests. This is a problem with multiple multi-upload flash+javascript libraries, so fixing this shortcoming is probably not feasible.
So my question is: can I successfully authenticate to devise/rememberable without using cookies? And if so, how?
More details
Devise/rememberable depends on the value of remember_token within the cookie. If I could fool Rails into thinking that the value was supplied as a cookie (e.g. request.cookies['remember_token'] = '...'), my problem would be solved. Devise/rememberable would find the correct value there, unpack it, and successfully authenticate. However, the request.cookies hash is apparently read-only. Writing to the hash is silently ignored. Example (debug console from an incoming POST request):
>> request.cookies['remember_token'] = 'a string'
=> "a string"
>> request.cookies['remember_token']
=> nil
>> request.cookies
=> {}
I'm using (or trying to use) the FancyUpload v3 widget.
How about overriding Devise slightly?
Based on Devise 1.2.rc something like this should work:
module Devise
module Strategies
class Rememberable
def remember_cookie
# your code to get the hashed value from the request
end
end
end
end
Alternatively, you could add a new (subclassed) strategy:
module Devise
module Strategies
class RememberableParameter < Rememberable
def remember_cookie
# your code to get the hashed value from the request
end
end
end
end
Warden::Strategies.add(:rememberable_parameter, Devise::Strategies::Rememberable)
Or, look into Token Authenticatable:
Token Authenticatable: signs in a user based on an authentication token (also known as
"single access token"). The token can be given both through query string or
HTTP Basic Authentication
There's more about it here:
https://github.com/plataformatec/devise/blob/master/lib/devise/models/token_authenticatable.rb
Good luck!

How to deal with authentication for a Ruby API wrapper?

I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/

Resources