I'm testing my website using Devise, and what I've found is if I intercept the redirect after a Devise Sign Out, while the "destroy" method is being called, I can still use the back button and log back into the system.
A bit more details:
1) I am using Devise with the following options: :database_authenticatable, :recoverable, :trackable, :validatable, :lockable, :timeoutable, :password_archivable, :maximum_attempts => 4, :unlock_strategy => :none. Also, I using the default settings for sign_out_all_scopes (true).
2) I am using Burp Proxy to intercept calls coming back to the browser from the server.
3) My Session Controller Destroy method is as follows:
def destroy
Rails.logger.info "RESETTING SESSION"
reset_session
Rails.logger.info "DESTROYING DEVISE SESSION"
super
Rails.logger.info "DONE WITH DEVISE"
end
The logs calls are just for me, also I have tried with and without the reset_session.
4) I log in as a user, then I click my logout call, I see that the Destroy method is called on the Web Server (looking at the logs), but I intercept the call to the browser.
5) Clicking the back button, I am can get back into the site and I can navigate it like I never was logged out.
It appears to me that despite calling the Warden's logout behind the scenes, it actually do anything unless the cookie is destroyed on the browser. Also looking a little closer, it doesn't appear that Devise or Warden does anything to the database on logout meaning that logging out yet not destroying the cookie would have no effect anyway.
I'm relatively new to Rails and very new to Devise, so am I just missing something?
EDIT: So after a brief conversation with Billy Chen it seems I'm not doing anything wrong, this is just how Devise/Warden works.
I'm curious how anyone is solving this problem for sites where they want to be assured that logging out really logs out a user? Throwing in a status on the User object that gets updated on login/logout would be easy enough, but I'm curious why there is no option to do that in the current design (even if it was just optional).
EDIT 2:
Solved the issue. Despite not having :rememberable turned on in the User model, the devise.rb file had "config.use_salt_as_remember_token = true", removing this fixed the problem. Not exactly sure why that configuration should be used if the model doesn't allow rememberable, but at least the problem is solved.
EDIT 3:
Apparently I'm a liar, it's not fixed I had just missed intercepting the response in burp proxy. Retesting show this is still an issue. Fixing it by tagging the User with a key and removing it on logout. A hack, but it should fix the issue.
The "destroy" is to destroy session, not user. So why do you expect database operation in this "destroy" action?
When an user log in, he fill out email and password in login page, aka "Session#new".
When he hit "log in" button, the request is sent to "Session#create". If login info is correct, session established. The server send a cookie containing session id which is hash including user id.
On every requests from this user thereafter, his browser send request together with this cookie. The server decodes this hash and load user id from database.
If he wants to log out, he'll hit "logout" button which will send "DELETE" request to "Session#destroy", then server will ask his browser to delete the key/id from that cookie.
Now the session is destroyed. This user can no more access protected resources for login users, because requests he send thereafter won't contain his valid user info.
That's basically how it works. You can check the details here:
http://guides.rubyonrails.org/security.html
Related
On backend in a controller, I want to log in a user. Then I want to render a view render(view: '/my-view') where the user will be authenticated already.
Scenario
A user is given a link.
He goes to this link.
Backend redirects the link to a controller.
Controller creates a temporary account for the user and authenticates him.
Controller renders a view and ???? somehow sends the session to frontend ????.
How can I send the session to the frontend?
Define front end ?
Backend redirects the link a controller. Controller creates a
temporary account for the user and authenticates him.
This is how I am doing it sockets does authentication, access that user's http session from backend and puts in there that they have logged in. I then send a socket trigger back to front end html to say all ok
at this point gsp gets response from sockets and says aha redirect to /site/hello
Controller renders a view and ???? somehow sends the session to frontend ????
This /site/hello now checks for specific session and well user is also now logged in too.. the session details was set by backend when user authenticated and not front end session
in gsp you can do
<g:set var="something" scope="session"/>
But I think what i have described is what you need to do
If you need helping user session details it is all quite easy i don't have it to hand
but from gsp when connecting to sockets i send '${session.id}' which then i look up and bind back to user .....
Also note --- there is catch here, when user is not authenticated they have primary session, when they authenticate through spring security they are actually given a new session id. This is due to security issues but I have got around that with checking session.username which i set upon login and this now matches '${params.encryptedUsername}' decrypted on backend..
Ahh it's rolling back.. there is a concurrent hashmap which contains username,session and from that When i get Decrypted.username I get hashMap which the value is user http session to which i poke and do things with ...
I can give you my code but then that is a lot of work above is the steps in one way of how you go about it
So to answer your question, this is under grails 3:
Enable Spring security session listener in application.groovy
grails.plugin.springsecurity.useSecurityEventListener = true
Add CustomSecurityEventListener.groovy class to your app, remove the loginCacheAttempt, unless you wish to use it refer to build.gradle for that stuff and the related service etc in that demo app.
This then calls SessionListener provided in that same folder and adds user with session id to the sessions synchronised map declared at the top of SessionListener
Now in my websockets when I register a user:
String sessionId = userSession.userProperties.get("sessionId") as String
def userHttpsession = SessionListener.sessions.find{it.key==sessionId}?.value
userHttpsession.username = username
userHttpsession.password = password
This is still pre-authentication and primary session
I send a trigger to tell sockets to refresh gsp page to another window.location.href
In that location controller action i authenticate session details and invalidate session details
registerService.authenticateUser(user, session.password)
This way of doing things appears to work fine without the complications, there is an encrypted user which is sent as part of initial socket transaction to ensure/verify session.user matches encrypted user (for logged in user)
It seems like the programmatic login takes care of the session too.
springSecurityService.reauthenticate(email, password)
I'm talking about interaction between IUserEnumerationPlugin and IAuthenticationPlugin.
Plone has a strange behaviour about the possibility to flag (activate) the IAuthenticationPlugin without flagging IUserEnumerationPlugin too.
You can add in ZMI -> Plone -> acl_users a simple "ZODB user manager"; add a dummy user in it; and activate only the Authentication flag.
You will get that the user authenticate correctly at login (welcome message is printed as well as userid in place of login tab) but the authentication cookie seems that didn't being set: browsing any other page brings you back to anonymous state.
Dunno if it is a bug, but I need to authenticate users without let them show in sharing page or in usergroup-userprefs searches as they are managed elsewhere.
I'm currently implementing an omniauth solution for an app that will initially be in an invite only mode. I can restrict the UI so that a person cannot see the registration screen from which omniauth could be activated unless they have a valid invitation code. That being said, if a user knew the url structure, they could try to initiate the omniauth process directly and I'm trying to figure out how to handle that. I can't lock down the authentication url because an already registered user would need to go through them and they would not have their invitation code after the initial registration. Ideally I'd like to pass the invitation code along in the omniauth request so that it would come back to the app upon success, but in looking I found this thread which said that is not possible.
http://groups.google.com/group/omniauth/browse_thread/thread/4d99d608...
Is this still true or is it now possible to do what I'm looking for? Is setting the value in the session still the preferred way or is there a better way to handle this when using OmniAuth?
Thanks in advance
Chris
Instead of passing the custom info along with the omniauth request, you can first store the info to somewhere (like session). And in the oauth callback, you check the invitation code of current session, if it's available, register the user.
Here's my scenario.
First, I log in as Alice:
http://localhost:3000/?auth_token=eMXBk8cuJMA1ETZfMIgB
Then, without logging out, I log in as Bob:
http://localhost:3000?auth_token=Z9Ui7Cw_xCnOmGWOEUEH
What happens is, after the second GET request, I'm still logged in as Alice, not Bob.
If I do a http://localhost:3000/users/sign_out in between the two auth_token logins, everything's OK.
Without the sign_out, Bob can't login using his token.
Is this a bug, or the way things should be due to some security issues I'm ignorant of?
Can this behavior be overriden through hooks?
I've run into this with restful_authentication and devise. Here is what I use to handle it (put in application_controller.rb) and call it where needed. Note that I use :ak for the auth token. Change to whatever you're using.
def switch_session(api_key_passed)
if api_key_passed != current_user.authentication_token
logger.info("******Switching session as token is different.*******")
user = User.find_by_authentication_token(api_key_passed)
sign_out(user)
if #api_login_enabled.present?
redirect_to(new_user_session_path(:ak => api_key_passed))
else
logger.info("***API Login Setting is Disabled.***")
end
end
end
Devise's token_authenticatable strategy is a login path. Sending a User's authentication_token to Devise will log in that user and set a session, just as logging in via the web would. It is not supposed to act as an API Key, which would be required to be sent on every request and knowledge of that request disappears once the server responds.
Take a look at this issue here for more information: https://github.com/plataformatec/devise/issues/300
#jschorr's answer will work if you wish to use it more like an API key, but you should be aware that the original issue will not actually persist the previous user's session between different clients, this is not a security issue of sessions leaking between clients, and this is exactly how the authors of Devise intended. Just as you would need to log out of your Significant Other's webmail account in order to check your own if they just checked their mail from the same computer, you would need to send a logout message to your Rails app before you can switch accounts.
You are missing a setting on devise.rb initializer:
# By default Devise will store the user in session. You can skip storage for
# :http_auth and :token_auth by adding those symbols to the array below.
# Notice that if you are skipping storage for all authentication paths, you
# may want to disable generating routes to Devise's sessions controller by
# passing :skip => :sessions to `devise_for` in your config/routes.rb
config.skip_session_storage = [:token_auth]
So no session is used when a user authenticates with an auth_token.
I have a problem with my authentication in CakePHP. Whatever I try, Cake will either tell me I'm de-authenticated after each request, either I'm still authenticated, even though I closed the browser before.
I'll explain in a few words how my authenticating system works. The user logs in, with either remember me checkbox checked or not. If it's checked, I will create a cookie so as the user is auto-logged in the next time he visits the site. Basically, when the user closes his browser, I need to delete all the session cookies Cake stores in the browser. This way, when the user comes back, he sees the login page if he didn't want to be remembered, or is automatically logged in from the remember me cookie if he chose to store it.
The problem is I played with both Session.timeout and Security.level from core.php, with no positive results. If I put the timeout to 0, Cake will de-auth me after each request and I'm not able to view any page so. If I put any other value for timeout, the user might close the browser and when he re-opens it, he could be still logged in, as the Cake session didn't expire.
In conclusion, how can I automatically delete the session cookie whenever the browser is closed?
Take a look at this article maybe can helps you, I use something similar for other purpose.
http://bakery.cakephp.org/articles/admad/2009/09/02/how-to-bend-cakephp-s-session-handling-to-your-needs
see: ini_set('session.cookie_lifetime', 0);