JSON Web Token with Devise - ruby-on-rails

I hope this does not count as an opinionated question. I just need to be pointed in the right direction.
I am modifying the Devise gem to work purely with JSON. I have had no problems with the registration, confirmation, re-confirmation, locking so far.
However, while working with the sign in, I dug deeper and understand that the default Devise sign in strategy uses Warden as it has to do with sessions and Rack authentication.
I understand JWT contains all the information in itself and does not need sessions.
So if I strip the default Devise strategy of everything and simply return a JWT on success and errors on error, would that be the right approach?
Am I missing something?

In order to use JWT with devise, I recommend to not monkey patch devise and instead use a tool others can audit and test.
For this reason, I developed devise-jwt. It does zero monkey patching and leverages warden, which is the authentication library below devise. You can also read more about it in this post I wrote: A Secure JWT Authentication Implementation for Rack and Rails
Hope it helps

I wouldn't use devise_token_auth since it seems like too much hassle and ... you store tokens in db :/. Why would we want to do so if JWT is available.
I'd rather add a new strategy to Warden/Devise couple and let them work as they should.
Here's an example: https://medium.com/#goncalvesjoao/rails-devise-jwt-and-the-forgotten-warden-67cfcf8a0b73 . One thing to note: JWTWrapper doesn't really belong to app/helpers/ . You need to inject somewhere a call to JWTWrapper.encode({ user_id: current_user.id }) once your users successfully signs in with their email/password. Perhaps in the Devise SessionsController?
def create
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
yield resource if block_given?
render json: JWTWrapper.encode({user_id:current_user.id})
end
You might want to do this only for xhr or json (format) requests

You probably shouldn't be hacking your Devise gem source. I suggest to just use Devise Token Auth gem to handle tokens instead.
https://github.com/lynndylanhurley/devise_token_auth
It will generate and authenticate valid RFC 6750 Bearer Tokens.
According to their README.
Seamless integration with both the the venerable ng-token-auth module for angular.js and the outstanding jToker plugin for jQuery.
Oauth2 authentication using OmniAuth.
Email authentication using Devise, including:
User registration
Password reset
Account updates
Account deletion
Support for multiple user models.
It is secure.

Sorry for late answer, but I'm actually working on the same problem, and want to share my opinions on that.
First, I would emphasize not changing Davise sources, this will likely bring you to further problems, especially when Devise code changes.
On the other hand, as I have encountered devise-token-auth, it might not be viable for your needs, especially in distributed systems (SOA). Perhaps I'm wrong, but as I see devise-token-auth, you can't add Subjects to restrict user access solely on the token. If you don't need this feature, you really should try devise-token-auth.
If you want to store additional information in the token, you could try to authenticate against a regular devise or devise-token-auth and then encode your information using a JWT gem.
Example can be found here: https://www.sitepoint.com/introduction-to-using-jwt-in-rails/

Related

User Authentication into Devise from iOS

I am creating an application where user is going to sign in with username and password. At the back end and also for the website I am using ruby on rails where the authentication is handled by Devise. With the last edition of Devise they have depriciated the Authentication Token. I am lost in terms of how to authenticate from iOS ? Any suggestions ? How am I going to modify the gem files etc.
See this gist from Jose Valim Safe or Unsafe Tokens
Basically you will want to write your own auth token methods. You need to generate tokens and later compare them. You should read all of the comments, the discussion is pretty good.

Using the new Devise for authentication for a mobile app

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

How should I handle user validation through Rhodes (without RhoConnect/Sync)?

I have a site build on rails that provides XML version of all relevant pages. Additionally, it has HTTP authentication.
My plan to handle login for the mobile app is to post the username/password to the login page and, assuming I get a good response, I'll set global variables variables for user and password and make all requests to protected data with the validated user/pass in the header. Logout will just wipe wipe the user/password global variables.
Is the best way to handle this or am I making things more difficult than they need to be?
yes, although my advice is to use a gem like Devise or authLogic as rolling your own authentication is fraught with potential problems.
There are different ways you can include external libraries for Rhodes, including RubyGems and custom extensions. Please refer to the link below for information.
http://docs.rhomobile.com/rhodes/extensions

How to use omniauth to make authenticated calls to services?

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.

Rails, OAuth, and CSRF protection

I am using REST and OAuth to talk to a Rails app (from an iPhone app, but that should not be relevant). However, I am running into some issues with Rails' CSRF protection (via protects_from_forgery).
I understand that CSRF protection only kicks in for regular form submissions (i.e. Content-Type=application/x-www-form-urlencoded), so I would be fine if I was submitting JSON or XML data. Unfortunately, OAuth is currently limited to application/x-www-form-urlencoded requests. There's a draft spec that extends OAuth to non-form-urlencoded data, but this doesn't help me right now.
The way I see it, I have the following options:
Send the data as JSON, knowing that it would not be part of the OAuth signature and thus subject to man-in-the-middle attacks. Obviously not an attractive solution.
Create special Rails actions (e.g. UsersController#update_oauth) that internally delegate to the regular actions (e.g. UsersController#update). Then exclude these from the forgery protection (protects_from_forgery :only => [:update]). This should work and might be borderline acceptable for one or two actions, but obviously would be a very messy solution.
Override the Rails CSRF protection to ignore OAuth requests. I have not tried this, but it seems like it should be possible to change one of the hooks (perhaps the verify_authenticity_token filter) to consider OAuth requests successful.
Has anybody run into this before? Any recommendations? Or am I perhaps missing something basic?
I'll answer my own question. :)
I added the following method to our OAuth controller extensions. The only thing this adds on top of the default implementation is the oauth? check. This seems to do the trick and feels like a pretty clean solution.
def verify_authenticity_token
verified_request? || oauth? || raise(ActionController::InvalidAuthenticityToken)
end

Resources