When registering new users via Devise in Rails 5, I need to check if a cookie exists and if so store data from the cookie to a newly created user record. It also utilizes OmniAuth to create users from Facebook. I'm having trouble finding a way to tap into Devise's user creation and be able to access cookie information.
If I implement this directly in the RegistrationController after a user is created, I have to repeat it in the OmniAuth callbacks, which is not very DRY. Is there a better place or method to hook into this?
I came across the new_with_session method when building a new resource, but unfortunately that only gives access to the session, not cookies.
Related
I must build a Rails API, which responds to requests from an iPad app. I have, among other things, a Shop model, a User model and a Product model.
The iPad app works like that - once started, first the shop must log in with an ID and a password. This is done by the shop manager (probably in the morning, at the beginning of the working day or even just once, if they never log out). This happens on multiple iPads. Then, an iPad is given to a customer, who, within the “session” of the shop, logs in as a user. The point of this is, that a user can log in with the same credentials in different shops and depending on this, they can see different products in the iPad app.
So, within a Rails session I need to keep a current_user, but also a current_shop. The question is - how do I implement this?
I was thinking of the following - after the shop manager enters id and password, the API returns some token, which is persisted on the iPad. Then - when the user logs in, this token is sent along with their credentials, so that, at the moment of login I know in which shop the user is and know which products to return in the initial response after login. I also save the shop token in the user’s session.
I would first like to know if my general idea is correct. Also, I would like to know how would you implement it. I was thinking of using Devise for the user and hand-rolled authentication for the shop, but I must figure out how to integrate both.
Devise does allow you to use any model and multiple models in parallel. This allows you to use all the nifty helpers like current_user and authenticate_shop!.
What devise doesn't bring out of the box is an API authentication mechanism. That you can (and have to) implement yourself
Using tokens for each of the shop and user accounts seems straight forward. You can even use the same basic mechanism (maybe via HTTP-Header).
before_filter :authenticate_xxx_via_token
def authenticate_xxx_via_token
xxx_id = params[:xxx_id] || request.headers["X-XXX-ID"]
xxx_token = params[:auth_token] || request.headers["X-XXX-AUTH-TOKEN"]
xxx = xxx_id && Xxx.where(id:xxx_id).first
if xxx && Devise.secure_compare(xxx.authentication_token, xxx_token)
sign_in xxx, store: false
end
end
So do this once for each model and you can then protect your controllers/actions via authenticate_xxx! and use current_xxx.
Finally, don't forget to add SSL to your service or all of this is of little use.
I guess, you're integrating an API driven application, using devise will be of limited use to you. You could do it but I have a feeling it will cause more pain than it is worth.
Use Rails built'in has_secure_password - http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html
The simplest possible flow without session tokens would go like this:
store manager's username and password into the iPad app (you can send an api call to the host to verify it's OK) and the app stores those values in its process memory (not on the disk!)
in subsequent API calls you resend the manager's username and password alongside with the customer's username and password and you verify both in before_action of your base controller
A bit more secure solution would be to use a session token which supports multiple ipads:
store manager enters his username and password into the ipad app and ipad app sends a store authentication call, you verify the credentials and return a SecureRandom.base64 digest and you set Rails.cache shop_owner/#{digest} key to value of shop_id for later retrieval, on the app you are free to put the digest on the disk
in every subsequent request the digest is sent alongside the customer credentials, in the before_action you check the cache for shop_owner/#{digest} and retrieve the store ID from there
If a user starts out in my app unauthenticated and then logs in, I can have my Rails back-end return the user data, and create a User model in my Ember app.
However, what if a user starts out in the app authenticated? How can I use session to fetch the user's details from Rails?
I'm using the ember-simple-auth-devise authenticator.
I was able to use
this.get('session.user_email')
in my application route to find the authenticated user.
The better way to do it is to reopen the session object and add a property that references the authenticated user. See this example from the guides.
I have a JSF login page using form authentication. I login users by calling HttpServletRequest.login(username, password). Logging out is done by first calling ExternalContext.invalidateSession() and then calling HttpServletRequest.logout() for the current user.
My plan is to keep track of the logged in user in an application scoped list by adding to the list anytime a user logs in and removing from the list when a user logs out.
I have two concerns with this approach:
If a user that was already logged in tries to log in again without first logging out, I want to invalidate the existing session and do some cleanup. How do I access the session for a given logged in user? I could also use this functionality to forcefully logout some users.
If a session expires (e.g. timeout) I want to remove the user from the list of logged in users. How do I listen for a session expiration?
Maintain a Map<User, HttpSession> logins in application scope yourself. During login, check if logins.put(user, session) doesn't return null and then invalidate it.
Let the User implement HttpSessionBindingListener and implement valueUnbound() accordingly so that it does a logins.remove(this). Or, if you don't have control over User, then implement HttpSessionListener#sessionDestroyed() instead to perform the remove.
Unrelated to the concrete problem, calling HttpServletRequest#logout() is unnecessary if you already invalidate the session. The user is tied to the session anyway.
I am using devise with rest enabled. I have a form that requires user authentcation / registration. The authentication is implemented in fancybox light box.
I am able to register and signin using ajax calls. Sign in seems to return a session_id. How do I get the session object after registration. Or, more like is a session object created at registration?
If one is not created how would I bypass signin the first time after registration ?
There seems to be no straight forward way to do this. As of now the only way I can do this is by refreshing the page.
I am using Omniauth and Fb_graph gems in my app.
I wanted to perform two different actions on:
1) Sign up using facebook -> Using Omniauth I create an authentication and redirect to root_path
2) Finding facebook friends -> If an authentication exists, find friends using fb_graph. If it doesn't exist, create an authentication in omniauth and then redirect to fb_friends_path
How can I have different callbacks after authenticating using Omniauth? (In first case, I want to redirect to root_path and in second case I want redirect to fb_friends_path after creating an authentication if it does not exists.)
Thanks a lot!
Assuming you are using rails, setup a before filter called :auth_required that checks to see if that user has setup authorization in the past -- perhaps you are storing oauth tokens and you can check for it. Place :auth_required in front of the action that corresponds to fb_friends_path. If they don't have authorization setup, store the user's intent (their desired url) in the session.
In the callback you have setup for omniauth, if there is a stored intent redirect to it and remove it. Otherwise, assume they are signing up and send them to the root path.