Using devise_token_auth library for both webapp and api - ruby-on-rails

I'm using devise_token_auth(https://github.com/lynndylanhurley/devise_token_auth) for authenticating mobile devices and I also need to use devise for my web app.
The problem is that a user can sign up through devise_token_auth but if the user signs in and tries to call certain function in a controller which contains "before_action :authenticate_user!", I get an error saying Authorized Users Only.
I wonder if there's a way to use devise and devise_token_auth together.
code:
class RestrictedController < ApplicationController
before_action :authenticate_user!
def stuff
head :ok
end
end

First, you should know that devise is no longer managing user's session. This why it is advised to use devise_token_auth.
Whenever you specify before_action :authenticate_user! on a Controller, all actions(like RestrictedController#stuff) would require the user to be signed in. devise_token_auth expect to receive in the query header 4 params listed below.
Here is what you need to do:
Sign in using the route provided by devise_token_auth.
If sign in succeeds, you'll receive in the header an access-token, a client, a token-type and a uid.
Whenever you'd like to run a controller action where user has to be signed in, specify, in the query headers, the access-token, the client, the token-type and the uid

Related

omniauth - session empty after auth token returned

I'm hoping someone can help me figure out what's going wrong with an auth I'm adding into an app. The app itself uses normal authentication (username/password) using devise. Once a user is logged in, they are supposed to be able to connect to their email provider using OAuth (currently working on Microsoft365) which is what I'm working on right now.
I'm using the omniauth-oauth2 gem to implement the authentication, and as far as I can tell everything is working - the user is presented the MS login page, a POST request is returned with the token. All looks good, however when the callback comes in, the original user session is completely empty, and when I then redirect the user back to another page, they are kicked back to the login screen.
My callback is really simple at the moment
def auth_callback
# data = request.env['omniauth.auth']
if has_permission?
flash[:notice] = 'YAY!'
redirect_to root_url
else
flash[:alert] = "Could not authenticate with your mailbox"
redirect_to new_mailbox_path
end
end
I'm not doing anything with the data, and I know in a normal authentication system this is where I'd be getting/creating the user and signing in - but the user should already be signed in. And since they weren't created by oauth, I can't really use the MS user ID to find them anyway.
The only solution I can think of, is to send the user ID in the state when requesting auth from MS, so that when it is returned I can match that up and the "sign in" the user again but that feels wrong.
I am already bypassing CSRF so it shouldn't be that wiping it out
skip_before_action :verify_authenticity_token, only: %i[auth_callback]
skip_before_action :authenticate_user!, only: %i[auth_callback]
Is there anything else that would cause the session to be lost like this?

How does Rail's DEVISE gem store temp data

I am using Rails 5 and Devise.
I have a form which can be accessed by anyone. The POST action URL however can only be accessed by signed in users.
before_action :authenticate_user!
So if a user is not signed in Devise automatically redirects to the login form, where the user can login or even create a new account. After doing that Devise automatically redirects to the POST URL and the posted data is still available.
So my questions is where in the code of the GEM is Devise handling and storing the redirect URL and POST data.

Rails: Persist params through an Unauthorized request?

This Rails project has an API side to send data to the iOS version of the app. It uses Devise::Lockable to lock user accounts when they fail login 3 times.
This is a strange edge case, but maybe not that uncommon: The situation begins with being already logged in to the desktop version. If I then go fail login in the iOS app 3 times, but then click the reset password link in my email from the desktop version, because I'm already logged in, the request for the edit_password_url gets unauthorized (because the account is locked) and I get redirected to the login page.
What are some ways I could have it still go to the edit_password_url while keeping the params (specifically the password reset token)? I'm thinking either:
Skip authenticating the password edit page (seems bad, but I still haven't see how to do this)
Create some series of filters that check requests then redirect back
to the edit_password_url ... but how do I keep that reset password
token?
Alternatively, I could somehow force sign out of the user in the main
app somewhere in the process of that user failing login through the API. But these are namespaced and under different controllers and I haven't found a way to force a sign out that way.
There are several actions in devise where it redirects if there's an active session. I never liked this behavior and usually override it. If I hit a link, it's because I want to perform that action, after all.
For example, visiting the sign-in link, you might want to sign in as a different user, so you could override that action to sign out if it's hit directly, rather than redirecting. So for my example you would subclass Devise::SessionsController
class SessionsController < Devise::SessionsController
skip_before_filter :require_no_authentication, only: [:new]
def new
if warden.authenticated?(resource_name)
sign_out
end
super
end
private
def sign_out
# From Devise::SessionsController#destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
end
end
and override it in routes:
devise_for :users, controllers: {sessions: 'sessions'}

Rails Devise OmniAuth Facebook Login from iOS

I've been searching for a solid solution to this problem, and came across this SO question which kind of matches my predicament, but not exactly.
Currently I have my iPhone application authenticating with my Rails API via Basic Auth. It's just your simple, run-of-the-mill devise auth package. I then followed the instructions to set up omniauth-facebook for devise and got that working on the browser side.
The part I can't figure out how to do is how to send the token received on the iPhone side (via the Facebook iOS SDK) to the server. I want the server to check the users table to see if that facebook user has signed up, and create an account for him if he hasn't. Then, I was thinking the server would generate a random password and send it back to the client device so that I could keep my same basic authentication strategy. Is this the proper way to implement single sign on for a web app and iPhone app? How would one go about modifying the server side packages to support authentication via a token sent from the phone?
You may want to take a look here:
Open Source: Announcing devise-iOS For Simplified Auth
It looks like a relatively painless way to work with Rails / Devise and iOS. I definitely plan on using it in my next project.
Have you looked into making your app an Oauth2 provider?
I haven't done this myself, but after some digging it looks like opro and doorkeeper are two possible solutions to the problem.
https://github.com/opro/opro
https://github.com/doorkeeper-gem/doorkeeper
looks like opro works pretty well with devise:
#inside initializers/opro.rb
Opro.setup do |config|
config.auth_strategy = :devise
end
Definitely interested to see how this turns out for you
I think you have the right plan. We've done exactly this in our app and Web service.
The apps use a REST API, basic authentication over HTTPS, a server-generated password, and all of this is implemented without Devise. There's a method in the controller that all the API controllers inherit from, that is a before_action for all the API methods, and it calls 'authenticate_or_request_with_http_basic'
class ApiController < ActionController::Base
before_action :authenticate_api
def authenticate_api
authenticate_or_request_with_http_basic do |username, password|
# check server-generated password
end
end
end
So that handles most requests.
We also have a API controller action to register from the device once to get that server-generated password:
class UsersController < ApiController
skip_before_action :authenticate_api, only: [:register_fb]
def register_fb
graph = Koala::Facebook::API.new(params.require("access_token"))
profile = graph.get_object("me?fields=email,first_name,last_name")
# then go on to look up user if already exists, or create
# ... return server-generated password
end
end
The Web app, however, all controllers inherit from WebappController and use devise.
Thus we have two passwords on the User object (one for web, one for mobile) and a facebook ID as well as our own User id which is the one we use for authentication.

What is the correct way to use Devise gem authentication with Mobile (Rails)

I am using Devise gem for web authentication in my Application.
Now i am about to write a mobile app for my Application which includes Sign in / Sign up process.
What is the correct way i should use to sign in a user and authenticate each call made by the user from the mobile app?
Which of the below strategy is correct? (i am not sure which method to follow to be more secure)
Note : You can view the above image in http://i.stack.imgur.com/I13uT.png (will be more clear)
FYI : I am using Titanium to develop mobile app and my backend server runs Rails app
Model #1 isn't secure, you aren't passing any sort of authentication on subsequent requests to validate that the user is still who they say they are.
What I'm presuming you really want to know is, what's the best way to verify the user is who they say they are, after logging in. I've answered this previously, Exposing Rails/Devise Authentication to iOS application and the same answer applies here.
Using token authentication in Devise will match model #2, and is also the most secure since you exchange the username/password for a token rather than having to store their username and password and reuse it with every request.
I'm not sure how #1 is secure at all since none of the subsequent requests are signed in any way. If someone knew the file structure of your app they could just access it that way, right?
With Devise, you can set an attribute on your User model to allow users to be authenticated via token:
class User < ActiveRecord::Base
devise :token_authenticatable
# there are other details and options on this, but this is the relevant piece
end
On each controller you can also verify that the user is authenticated by including before_filter :authenticate_user! at the beginning:
class PostsController < ActionController::Base
before_filter :authenticate_user!
end
When making requests from the mobile app, include the auth_token in the request so that the Rails app can authenticate before responding.
Beyond authentication, you may also be interested in something like CanCan to handle authorization as well.

Resources