My two rails applications(app1, app2) are communicating using active resource.
app1 calls app2 create a user inside app2. app2 would create the user and would like app1 then redirect the user to app2's authenticated pages.
going from app1 to app2 would invariably ask the user to log in.
I was looking for a way to avoid this login step in app2, instead make the user log in during the first active resource call to create user, and somehow get the authentication token written.
Authentication is done using Devise. Is there anything built into Devise that support this?
Is passing around the authentication token the way to go?
You are trying to implement a form of Single Sign-On service (SSO) (sign in with app1, and be automatically authenticated with app2, app3...). It is unfortunately not a trivial task. You can probably make it work (maybe you already did), but instead of trying to reinvent the wheel, why not instead integrate an existing solution? Or even better, a standard protocol? It is actually relatively easy.
CAS server
RubyCAS is a Ruby server that implements Yale University's CAS (Central Authentication Service) protocol. I had great success with it.
The tricky part is getting it to work with your existing Devise authentication database. We faced the same problem, and after some code diving, I came up with the following, which works like a charm for us. This goes in your RubyCAS server config, by default /etc/rubycas-server/config.yml. Of course, adapt as necessary:
authenticator:
class: CASServer::Authenticators::SQLEncrypted
database:
adapter: sqlite3
database: /path/to/your/devise/production.sqlite3
user_table: users
username_column: email
password_column: encrypted_password
encrypt_function: 'require "bcrypt"; user.encrypted_password == ::BCrypt::Engine.hash_secret("#{#password}", ::BCrypt::Password.new(user.encrypted_password).salt)'
enter code here
That encrypt_function was pain to figure out... I am not too happy about embedding a require statement in there, but hey, it works. Any improvement would be welcome though.
CAS client(s)
For the client side (module that you will want to integrate into app2, app3...), a Rails plugin is provided by the RubyCAS-client gem.
You will need an initializer rubycas_client.rb, something like:
require 'casclient'
require 'casclient/frameworks/rails/filter'
CASClient::Frameworks::Rails::Filter.configure(
:cas_base_url => "https://cas.example.com/"
)
Finally, you can re-wire a few Devise calls to use CAS so your current code will work almost as-is:
# Mandatory authentication
def authenticate_user!
CASClient::Frameworks::Rails::Filter.filter(self)
end
# Optional authentication (not in Devise)
def authenticate_user
CASClient::Frameworks::Rails::GatewayFilter
end
def user_signed_in?
session[:cas_user].present?
end
Unfortunately there is no direct way to replace current_user, but you can try the suggestions below:
current_user with direct DB access
If your client apps have access to the backend users database, you could load the user data from there:
def current_user
return nil if session[:cas_user].nil?
return User.find_by_email(session[:cas_user])
end
But for a more extensible architecture, you may want to keep the apps separate from the backend. For the, you can try the following two methods.
current_user using CAS extra_attributes
Use the extra_attributes provided by the CAS protocol: basically, pass all the necessary user data as extra_attributes in the CAS token (add an extra_attributes key, listing the needed attributes, to your authenticator in config.yml), and rebuild a virtual user on the client side. The code would look something like this:
def current_user
return nil if session[:cas_user].nil?
email = session[:cas_user]
extra_attributes = session[:cas_extra_attributes]
user = VirtualUser.new(:email => email,
:name => extra_attributes[:name],
:mojo => extra_attributes[:mojo],
)
return user
end
The VirtualUser class definition is left as an exercise. Hint: using a tableless ActiveRecord (see Railscast #193) should let you write a drop-in replacement that should just work as-is with your existing code.
current_user using an XML API on the backend and an ActiveResource
Another possibility is to prepare an XML API on the users backend, then use an ActiveResource to retrieve your User model. In that case, assuming your XML API accepts an email parameter to filter the users list, the code would look like:
def current_user
return nil if session[:cas_user].nil?
email = session[:cas_user]
# Here User is an ActiveResource
return User.all(:params => {:email => email}).first
end
While this method requires an extra request, we found it to be the most flexible. Just be sure to secure your XML API or you may be opening a gapping security hole in your system. SSL, HTTP authentication, and since it is for internal use only, throw in IP restrictions for good measure.
Bonus: other frameworks can join the fun too!
Since CAS is a standard protocol, you get the added benefit of allowing apps using other technologies to use your Single Sign-On service. There are official clients for Java, PHP, .Net and Apache.
Let me know if this was of any help, and don't hesitate to ask if you have any question.
Related
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.
I am trying to implement user authentication using Devise for my Rails/iOS app. I am having trouble since I've mostly been a "user" of Devise and was using it for pure web apps so didn't need to bother so much with what goes on behind the scenes. Now that I have to build authentication for an API based app, it's entirely a different world.
I've read every single tutorial on the web that deals with this topic (most of them are outdated due to the fact that token_authenticatable has been deprecated) but still having trouble understanding what I need to do.
I also read the original GitHub gist talking about this issue and still don't understand what they are talking about.
I'm sure there are people out there just like me who've been just a "user" of Devise so don't really know what goes on behind the scenes.
Can anyone provide a concise solution to implementing an API based authentication system for a mobile app? I mean it can't be that complex, Devise used to be so awesome since all i needed to do was run rails generate, but this has been nightmare for me.
I am working on same things as you want,
for this you have to use token authentication rather than simple
Devise, add following gem in gemfile
# Use device for authentication
gem 'devise'
gem 'simple_token_authentication'
follow documention simple_token_authentication
Use Api like this
curl -v https://example.com/users/sign_in -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{"user": {"login": "7838712847" ,"password": "8489", "mobile_type": "ios", "mobile_key": "APA91bG6G4UvjeCWvb8mMAH1ZO3I-8FB3nkRPyCHnwZiXgd16HK18GgoV5n7gjJtZNs038iaFGutzdxnhes3WyaXEX52-xmOyvuEK8S1abBdpuhD9AD5bzLWeu-1Ow_yZRTVg3Nypz1z"}}'
I am using mobile number to login so customize gem according your need
please let me know if it is not working (mail me: er.mukeshsharma.rj21#gmail.com)
Here is an approach that works excellent for me, when using Devise for authentication in a Rails app. If tests for a token first (you can set the token by any iOS, Android, ... app) and falls back to the default authentication method for your web users.
Rails
Add your own token to the user model, by adding an :api_token string column and fill that with a unique value per user. Using a Digest::SHA1 of some user data (like id + email) is a good starting point, but you can (and should) go as crazy as you like when it comes to generating a unique token.
Create a method for authentication over that token. You can add it to your main ApplicationController for easy access (don't forget to put the method in your private section of the controller);
def authenticate_user_by_token
#api_token = request.headers['HTTP_AUTHORIZATION']
if #api_token.present? && #user = User.find_by_api_token(#api_token)
sign_in #user
return #user
else
return false
end
end
Next create a (private) method and chain this method to the devise before filter method you are using (like :authenticate_user! for example). Put it in the same controller as the method above for easy access;
def authenticate_by_token_or_devise!
return authenticate_user! unless authenticate_user_by_token
end
Now Replace your current before_filter call from :authenticate_user! to the newly created one; :authenticate_by_token_or_devise!, like so;
before_filter :authenticate_by_token_or_devise!
Or, starting from rails 4 (Rails 4: before_filter vs. before_action), use before_action;
before_action :authenticate_by_token_or_devise!
iOS
Now all you have to do is add that token to your iOS app. Depending on the framework that you use in your app, this might be different then the code below.
I use AFNetworking (https://github.com/AFNetworking/AFNetworking) in this example. This is how you set the Authorisation header token in your AFHTTPRequestOperationManager so it gets added to every request you make.
NSString *apiToken = #"your-token-here";
[[_manager requestSerializer] setValue:apiToken forHTTPHeaderField:#"Authorization"];
Optional
Additionally, you can create a before filter method that allows access to token-based authentication only (e.g. if you have a set of /api routes that you only want to be accessed using the token) like this;
def authenticate_user_by_token!
if !authenticate_user_by_token
render nothing: true, status: :unauthorized and return
end
end
Recently, we also had to set up token based authentication for our webapp (for API access) - and we also stumbled upon the fact that it has been removed from Devise.
We went with Simple Token Authentication which worked just beautifully.
When I recently implemented an API, I grudgingly followed a suggestion to use Warden, a rack-based authentication gem. My sense was that an authentication gem that required you to write your own authentication was broken. But this gem provides just the right level of control. My only complaint is that the gem don't handle POST parameters well. I was able to work around it, but that kind of concern should be (IMO) handled by the gem.
Having used it, I highly recommend this gem for any scenario requiring non-generic authentication. Rolling your own authentication strategies is a joy because (a) it's pretty simple and (b) you aren't bound by other devs' assumptions.
To help you get started, here is my config/initializers/warden.rb file.
You can use a combination of the devise gem and doorkeeper gem to support web and mobile authentication.
For example, I used devise for signing up users and handling forget password and email confirmation flow. For mobile clients, I used the doorkeeper gem as a oauth2 provider to protect my apis. There are many oauth2 grant flows supported by the doorkeeper gem and I suggest you can take a look at those.
Here's a link! to get started
I'm currently working on an application that in addition to the usual visual web application goop also will expose a few RESTful API services for use by outside applications. I am using Devise to manage user authentication but I'm struggling with how to "manually" authenticate a user given certain input.
The case I have is that I want a user of the API to log in w/o actually going to the visual log in screen etc. I want them to submit a username and password and then authenticate and sign them in in my API services.
I know that you can sign a user in using the sign_in method that Devise provides, but that seems to ignore authentication entirely. here's what I wanted to do explained in a bit more detail:
Assume a GET route called connect in the user controller. the controller is replacing entirely the Devise registrations controller, but not the session one. The URL to my service would be:
<server>/users/connect
and it would expect 'email', 'password' parameters in addition to some service specific and unimportant to my question goop.
What I want to know is how to implement something that is equivalent to the following pseudocode:
def connect
user = User.find_by_email(params[:email])
password = params[:password]
# here is the part I'm pseudo coding out
if user.is_valid_password(password)
...do my stuff...
end
render :json ...etc...
end
I have been unable to find a method in the Devise source to do this--it's so generalized in so many ways that I'm likely just missing it.
Anyone have any ideas? I'm hoping not to a) have to implement my own thing and b) not have to move away from Devise. It provides me with so much for the non-API services...
thanks!
I've left out th
Devise's token_authenticatable is the way to go for this. We've successfully used it many times to do api-based logins.
In config/initializers/devise.rb
config.token_authentication_key = :nameofyourapikeyhere
In user.rb:
devise … token_authenticatable, ...
In the above, you can name the api key anything and then have your route as /users/connect?apikey=whatever (using apikey as an example). In the database, it'll be authentication_token, but it'll work fine.
To clarify, if the user has an authentication_token and it's sent in the params (or it's alias- in the above example: apikey), they'll login.
I've received a token / secret from a service using OmniAuth and can store it for users, but I'm stuck as to how to actually use these to call a service.
The closest thing I've seen to this question is here but the way he's solved that there doesn't feel right. I feel like OmniAuth likely does this all for you if you know what you're doing.
Netflix has a pretty involved auth process, so I was hoping to skirt all of this by using OmniAuth to abstract me from all of this.
Given that I have a token and secret for a user, how to use these in calling a service like Netflix?
Many thanks :)
Hey, I'm the author of the OmniAuth gem. OmniAuth is meant to be used for the authentication process. In the case of OAuth providers like Netflix, this means exchanging a request token for an access token which is then used to pull user information from the API. These one-off calls are specifically designed for each provider and are not meant to be a generic API client for the given provider.
What you can do it use OmniAuth to obtain the credentials and then use another specific library for the site itself (such as ruby-netflix or anything else, I'm not sure what the best one is) to make calls. You can retrieve the access token and secret that is obtained in the authentication dance by accessing env['omniauth.auth']['credentials'], then use those to initialize the API client.
You can also use the OAuth library directly to make these calls, but I would strongly recommend just using an existing library, it will be much faster and easier. Does all of that make sense?
OmniAuth is all about authentication; you should probably look at another gem for making actual calls to the service. E.g., for Facebook, I use the OAuth2 gem and code like the following:
module Facebook
class Client < OAuth2::Client
# Return a new OAuth2::Client object specific to the app.
def initialize
super(
APP_CONFIG[:facebook][:api_key],
APP_CONFIG[:facebook][:app_secret],
:site => 'https://graph.facebook.com',
:parse_json => true
)
end
end
class Token < OAuth2::AccessToken
# Return a new OAuth2::AccessToken specific to the app
# and the user with the given token.
def initialize(token)
super(
Facebook::Client.new,
token
)
end
end
end
access_token = Facebook::Token.new(users_fb_token)
url = "https://graph.facebook.com/#{user_fb_id}/feed"
response = access_token.post(url, :message => "My update")
Note that there are gems for popular services, like Facebook and Twitter, that can manage the behind-the-scenes things like creating tokens, managing URLs, etc. For Netflix, you might check the following:
https://github.com/tiegz/ruby-netflix
https://github.com/rares/netflix
http://code.google.com/p/flix4r/
Also keep in mind that OmniAuth just returns the service data to you; you're free to store it and use it how you will (Devise has it's own pattern for OmniAuth that you might butt heads with if you try to go outside the lines). The other question you linked doesn't look too far fetched to me.
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 :( )