omniauth - session empty after auth token returned - ruby-on-rails

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?

Related

logout from application but can not able to logout from google account while using google authentication

I do login authentication with google in rails app,I had problem with when I logout it will redirect to my index page but can not able to logout from google account
I was doing first time login authentication,Please can any one help me
Thank you in advance
Here I write my code where i was wrong
def create
#user = Authentication.find_or_create_from_auth_hash(request.env["omniauth.auth"])
session[:user_id] = #user.id
redirect_to '/'
end
def destroy
session[:user_id] = nil
redirect_to '/login'
end
This is not how it works.
Consider doing this: Open your rails app -> Login with Google -> You'll see your rails app dashboard -> Now open a new tab and open google.com -> Logout from there -> Now switch back to your rails app and reload the page.
Even though you're logged out of Google, you're still signed in to your Rails app.
This means that your rails app is not exactly your google account. They both are independent. Your rails app(through Omniauth) just requests the Google data with an OAuth API, which will authorize the app and then sends the access token, and with this token, you get the google data(like email), etc.
However, if you still want to log out the user from the Google account, You can use alternatives like in this answer and multiple answers from this thread.

Rails: Migrating to Auth0 while retaining postgres records and associations

My project currently uses has_secure_password to handle users (similar to this railscast). Everything works fine but looking down the road, I feel that a switch to Auth0 will provide benefits and would like to make the transition now before too many users sign up.
Users are saved to a User model with several associations (checkins/comments/etc.) How can I link an Auth0 user to a User record on my database. I figured I could either lookup by email (since they will be unique) or Auth0 User Id (and add a new column to the user table) once they login. It seems like I could simply use the Auth0 login callback to sign the user in (using my current sessions controller) but how can I create users on my database when they are created in OmniAuth. Is there a callback for that as well?
In a perfect world I would like to do something like:
User signs up using Auth0 lock. Creating a new account on the Auth0 database.
Callback triggers a new record to be made on my database that contains all the information from Auth0 and has columns for associations.
User logs in and their user_id (from my database) is stored in sessions.
Changes the user makes to personal information (name/email/etc.) is changed on both databases.
Is what I want possible? Or should I stick with my current authentication?
There is no major difference between implementing Auth0 authorization and any other OAuth provider such as Facebook, Twitter, Github etc. The Auth0 quickstart guide is just kind of strange since it does not show how you would link credentials to a user model in the application. Rather they just shove the whole shebang into the session.
The only real change you need to do to your user model here is add a uid column which is used to store the Auth0 uid for the user.
Basically the OAuth flow with OmniAuth works like this:
The user clicks a link that redirects the user to the providers OAuth portal. (Or in the case of Auth0 its a fancy JS Widget)
The provider redirects back to your application and it goes to either the callback or failure method on your callbacks controller.
At this point the user may be a new or returning user. So you try to find the user in your application based on the uid from the auth hash or create a new user from the auth hash.
You sign in and redirect the user.
class Auth0Controller < ActionController::Base
# the signed up or logged in on auth0
# GET "/auth/auth0/callback"
def callback
auth_hash = request.env['omniauth.auth']
#user = User.find_or_intialize_by(uid: auth_hash[:uid])
if #user.new_record?
#user.name = auth_hash.name
#user.email = auth_hash.email # etc
#user.save!
end
reset_session
session[:user_id] = #user.id
redirect_to root_path, success: "Hello #{#user.name}"
end
# the user declined to give your app permissions
# GET "/auth/failure"
def failure
redirect_to root_path, alert: "Oh noes"
end
end
See:
https://auth0.com/docs/quickstart/webapp/rails
https://github.com/omniauth/omniauth
https://auth0.com/docs/connections/database/migrating

Using devise_token_auth library for both webapp and api

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

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: sign out logged in user on event

I'm using Rail3 with Devise gem. It does a great job when you need to lock user from signing in.
But it works just for new login attempts.
If he is already logged in - it won't sign him out immediately.
Here's is the typical use case:
Given admin user
when detects suspicious activity of certain user he locks it with malicious_user.lock('locking-reason')
% can config/initializers/session_store.rb
AppFoo::Application.config.session_store :cookie_store, :key => '_foo_session'
Given HTTP's statelessness, you can't immediately log out a user because you will need to wait until they make another request to your server. You could get around this via a push service I suppose, but that would be overkill.
My solution would be to add that person to a blacklist and then check if they're on the blacklist whenever they try to access a section intended for logged-on users only. This will render them unable to log on until you decide whether or not their activity is suspicious.
Example:
User is suspected of intolerable activity
Admin wants to check this out, so they temporarily add the user to the blacklist.
User clicks on an area of the page they were currently on when added to the blacklist.
Code checks for loggin status and blacklisted users.
Since the user is blacklisted, they are informed that they need to sign in to access the content
Once the user tries to sign in again you can inform them that their account has been temporarily disabled (or you can do this in the previous step).
perhaps the easiest way would be to redirect the user to the logout action when you lock them so:
malicious_user.lock('locking-reason')
redirect_to '/logout' and return
I'm not familiar with Devise so this may not be the best solution or even possible but it's how I would approach the problem
Use a before_filter in the ApplicationController that will do the following
before_filter :kick_out_blocked_user
protected
def kick_out_blocked_user
unless current_user.try(:active?)
redirect_to destroy_user_session_path
end
end

Resources