RoR: Devise / OAuth2 Conflicts? - ruby-on-rails

As a RoR newbie, I'm using the rails-stripe-membership-saas code (https://github.com/RailsApps/rails-stripe-membership-saas) for my application's base (using Devise for authentication) and attempting to integrate into it the ruby-box gem (https://github.com/attachmentsme/ruby-box) for access to the Box API.
I'm running into what I believe to be conflicts between the Devise session and the subsequent ruby-box (OAuth2) session. My user model contains:
# :omniauthable
devise :database_authenticatable, :registrerable, :recoverable, :rememberable, :trackable, :validatable
After a user logs in, I'm redirecting them to Box to allow them to grant access to my application. This is handled in my application_controller and currently works as such,
def after_sign_in_path_for(resource)
case current_user.roles.first.name
when 'silver'
require 'ruby-box'
session = RubyBox::Session.new({client_id: '###',
client_secret: '###'})
authorize_url = session.authorize_url('https://myurl.com/auth/box')
end
end
Once the user grants access to my app, they are redirected to my Box controller (auth/box), where I'm attempting to get the access token from Box based on the code they provided in the redirect,
class BoxController < ApplicationController
def new
#token = session.get_access_token('code-returned-to-redirect_url')
end
end
This is when I run into the issue, getting the error:
undefined method 'get_access_token' for #<Rack::Session::Abstract::SessionHash:0x0000003b4cf00>
I can only assume that in calling "session" its not able to distinguish between the current user session and the Box session? How can I correct for this?
Thanks for your time and assistance.

I'm not particularly familiar with ruby-box, but it appears that their Session class is confusingly named. The Rails session object, accessible from controllers, is a way of managing persistent state across requests for a user -- a typical use of the word "session." But a ruby box session is nothing of the sort; it appears to just be a plain old ruby object with an API for making oauth authorization requests to ruby box.
The key is that there is no persistence of any RubyBox::Session object between requests. So when you redirect the user after sign in, the local variable session you created in after_sign_in_path_for is no longer available. So when you refer to session in your BoxController, you're getting an actual session object, not a RubyBox::Session.
The workflow that you're attempting isn't designed for an Authorization Code oauth grant type (the kind where a user of your application explicitly authorizes access to some protected resource they own, and you exchange an authorization code for an access token). It appears that it's designed for the Client Credentials authorization grant. That is, you're just getting a token based on your client key and client secret, where the authorization to access protected resources is implicit after you've authenticated your client.
Edited to add: if you want to authenticate your users via Box, you should have a look at omniauth-box instead, which will help you easily implement the authorization code oauth flow and will play nicely with devise.
So it appears that the documentation you're following isn't designed for the use case you have in mind. But as for the sessions, yeah, the session helper in a Rails controller refers to the users's session data that is persistent across requests, not a RubyBox::Session object.

Related

What is the proper way to sign in as a user in an rspec request spec, without devise?

I have a Rails application which has some User authentication which is built without Devise (or any gem for that matter). It uses the typical session[:user_id] to track the current user.
My understanding of the current state of controller tests is that the Rspec team and Rails teams both recommend against using them. This is fine, but I'm not seeing how to actually sign in as a user from within a request spec. I've done it with Devise with no issue, but Devise uses Warden and such.
I've tried to access the session from within the test but the level of abstraction within request specs seems to prevent access to it.
How can I sign in a user from within a request spec?
You can change the session before the request:
#request.session['user_id'] = '1'
Or add anything else that you require on the session to validate your user.
Or you could create a helper method that actually performs the request needed to login, which is what #dhh recommends.

Use Devise Session to Authenticate Doorkeeper API for Rails/Ember app

I'm developing a Rails app along with a corresponding API and contemplating introducing Ember for some particularly dynamic front end components. I'm using Devise for authentication and Doorkeeper to secure API endpoints and manage OAuth tokens.
I don't want to replace the login piece with Ember so the Ember app will likely be initialized once the user logs in on the primary "logged in index" page. I'd like the Ember app to use the public API rather than rendering JSON from my rails-centric controllers, partly for simplicity and partly to force me to keep the API up to date.
Since the user is already logged in, I don't think it makes sense to do the OAuth dance and get a token. Instead I'd like the API to allow requests from clients that have been logged in by Devise (presence of session / cookie). Effectively, you should be able to visit /api/v1/resources.json in a browser once logged in to the app and receive a JSON response. Currently its a 401 Unauthorized.
Does this seem like a reasonable approach? If so, does anyone have experience doing this?
For anyone interested in this in the future, the answer was pretty straightforward:
module Api
module V0
class ApiController < ActionController::Base
before_action :doorkeeper_authorize!, unless: :user_signed_in?
end
end
end
The key part being unless: :user_signed_in?, which is provided by Devise

Devise with user logged in using multiple scopes logs all but one out when using token_authenticateable

I'm using Devise with multiple scopes (in this case, a user scope and an admin scope) and admins are able to 'become' a user using the approach on the Devise wiki. This works well, except that I have one particular page that requires the use of an auth token that causes a problem with a session logged in under both a user and admin scope. The page generates a POST to a controller that requires a user to be logged in using the user auth token. The POST succeeds, but afterwards, the admin scope has been signed out. (Meaning that admin_signed_in? returns false.) Other pages that execute POSTs to the same controller without requiring the auth token work as expected without logging out the admin scope.
I suspect that something is going on with token_authenticatable where the authentication of any scopes other than the one associated with that specific token are logged out. I've searched for references in the devise gem source to both the devise sign_out and warden logout methods that could be invoked as part of the token_authenticatable functionality and wasn't able to find anything.
This is happening with Devise 1.3.4. Any help is appreciated.
In case anyone else is looking for a solution to this, I found that the before_filter/after_filter approach I described in the comment to my question seems to work fine. I think that a better, more general solution to this would be to make a change to the devise gem and underlying calls to warden, but didn't have time to make those changes for this particular problem yet.

rails authentication for an API

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.

Authenticate on an 'access code' using Devise in Rails 3

I am working on a rails project and it has been recommended that I use Devise for my authentication and user session management.
I have two user types who need user/password authentication and another user type which I only need to authenticate with an 'access_code'. They are different models with no inheritance.
What would be the best way of doing this in Devise? Is there a way to let all these different authentication types work side by side?
I have looked at allowing users to sign in using a username or email address but how would I go about doing it using only one field? No password involved.
Use the Token Authentication module without the Database one. There's an example in the Devise Wiki.
These tokens, unlike the ones you find on password recovery emails for example, are permanent and stored on the database. They behave by default like service API keys, which means they do not keep the user in session and need to be supplied on every request.
To make them really sign users in:
# If true, authentication through token does not store user in session and needs
# to be supplied on each request. Useful if you are using the token as API token.
config.stateless_token = false

Resources