Detecting 1st connection after register in grails - grails

In my grails application I want to know when it is the first login after a user successfully register.
I'm using the spring security core plugin.
What is the best way to perform this ?

Unless you're automatically logging the user in after registering, or know that the user will manually login right after a registration, you'll probably have to persist something like "lastLoginDate" with each user. Then just check if that value is empty (which is their first time logging in), otherwise just update the login date each time they login.
You can put this code in one of the events that is fired after a successful login.
UPDATED based on comments
grails.plugins.springsecurity.onInteractiveAuthenticationSuccessEvent = { e, appCtx ->
// fired after successful authentication
// and AFTER user info provided to SpringSecurityService
// to get currentUser, you can use the following
def springSecurityService = appCtx.getBean("springSecurityService")
def user = springSecurityService.currentUser
...
}
or
grails.plugins.springsecurity.onAuthenticationSuccessEvent = { e, appCtx ->
// fired after successful authentication
// and BEFORE user info provided to SpringSecurityService
// (e.g. springSecurityService.currentUser == null)
}
More info can be found on the SpringSecurity documentation under events.

Related

How to tie OAuth authentication with Spring Security

I have a Grails 2.5.3 app that currently uses spring security plugin for authentication. Users login using a username/pwd.
I have updated the app now to support OAuth authentication (Using ScribeJava). Users can click a link that redirects them to OAuth providers page and upon successfully entering the credentials they are redirected back to my application. However, I have not been able to tie this functionality with spring security plugin so that when the users are redirected back to my app (after successful login from OAuth), I can actually see that they are logged in and continue to use all my spring security goodies like <sec:ifLoggedIn>.
Does anyone know of a way to do this or have an example I can take a look at?
Here is how I authenticate a user using OAuth:
//called when user clicks "login using oauth"
def authenticate() {
OAuthService service = new ServiceBuilder()
.apiKey(grailsApplication.config.my.sso.clientid)
.apiSecret(grailsApplication.config.my.sso.clientsecret)
.build(MyApi.instance());
String url = service.getAuthorizationUrl();
return redirect(url: url)
}
//called when oauth provider redirects to my application
def authorization_code() {
def code = params.code
OAuthService service = new ServiceBuilder()
.apiKey(grailsApplication.config.my.sso.clientid)
.apiSecret(grailsApplication.config.my.sso.clientsecret)
.build(MyApi.instance());
println code
OAuth2AccessToken accessToken = service.getAccessToken(code);
String userProfileUrl = grailsApplication.config.my.sso.authdomain+"/userinfo"
final OAuthRequest request = new OAuthRequest(Verb.GET, userProfileUrl);
service.signRequest(accessToken, request);
final Response response = service.execute(request);
println(response.getCode());
println(response.getBody());
render (text: code)
}
Whenever you authenticate via OAuth, the remote server return you a unique id (some numeric value) each time.
You can use that id to verify the user in your end and authenticate the user using springsecurity.reauthenticate() method.
Steps to do that :
When user connect (authenticate first time) with service provider.
Service provider send you that unique id. Save that unique id in
user table.
And when user login via that service provider. Again service provider
sends that unique id. Check if that unique id exists in your system,
and if user exists with that unique id then use
springsecurity.reauthenticate(userInstance) method to authenticate the user. And now you can use spring security features.
check out link: http://www.jellyfishtechnologies.com/grails-2-2-0-integration-with-facebook-using-grails-oauth-plugin/
Assuming you got the user details from Oauth provider you just need to
set the security context of that particular user
Just get the user details by parsing the JSON like
def oauthResponse = JSON.parse(response?.getBody())
Map data = [
id : oauthResponse.id,
email : oauthResponse.email,
name : oauthResponse.name,
first_name : oauthResponse.given_name,
last_name : oauthResponse.family_name,
gender : oauthResponse.gender,
link : oauthResponse.link
]
Well in our case we used the email id as the user name.
So when we get the user data just check if user is already registered with system or not like below
//load the user details service bean
def userDetailsService
//check if user is already registered on our system
User user = User.findByEmail(data?.email)
if (user) {
//If user exists load his context
userDetails = userDetailsService.loadUserByUsername(data?.email)
} else {
//create the new user
//Assign the role to it
//load his context as below
userDetails = userDetailsService.loadUserByUsername(data?.email)
}
After user registered successfully we just need to load his context like below
def password
//setting spring security context
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, password == null ? userDetails.getPassword() : password, userDetails.getAuthorities()))
Once spring security context is loaded you can redirect user to your landing page.
Now oauth user will be access resources like the any other user with same role.

Manually logout another user in grails

In my application I need to programmatically logout a user, not the current one. I was able to expire its session but this does not force a logout for that specific user. So my question is, how can I force a logout on the user's next request? This is my code now:
sessionRegistry.getAllPrincipals().each {
principal = it
sessionRegistry.getAllSessions(it, false).each {
if (principal.username == userSec.username) {
it.expireNow()
}
}
}
I have this in my resources.groovy:
sessionRegistry(SessionRegistryImpl)
sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
maximumSessions = -1
}
concurrentSessionFilter(ConcurrentSessionFilter){
sessionRegistry = sessionRegistry
expiredUrl = '/login/concurrentSession'
}
Using the session mechanismus it is not possible. You have to use a storage medium where you can keep the users. This medium can be either an in-RAM singleton, like ConcurrentHashMap instance, or a DB, depending on your clustering architecture.
Then upon each call to springSecurityService.currentUser (e.g. in Spring Security core plugin), you have to check if the sessionRegistry contains that user, and if not, invalidate the session or the user

Grails spring security save login time in user domain

Using this link if we register a call back in grails, how to access springSecurityService in plain groovy/java class, so that we can get the current user domain class and save the login time?
Update:
I have done this using the below:
appCtx.springSecurityService.currentUser.id
If you are using the callback closures you can get the information from the AuthenticationSuccessEvent.
grails.plugin.springsecurity.onAuthenticationSuccessEvent = { e, appCtx ->
// handle AuthenticationSuccessEvent
println "User id ${e.authentication.principal.id} was authenticated"
}

grails with spring-security: manually log out selected users

As a application administrator I would like to be able to log off any user, for example, after setting the flag "enabled = false" to the selected user. Is it possible in spring-security?
I should add that my application allows the use of "remember Me" for users.
I'm using:
grails 2.2.1
plugin spring-security-core 1.2.7.3
Settings spring-security-core (config.groovy):
grails.plugins.springsecurity.useHttpSessionEventP ublisher = true
grails.plugins.springsecurity.useSessionFixationPr evention = true
grails.plugins.springsecurity.userLookup.userDomai nClassName = 'com.app.User'
grails.plugins.springsecurity.userLookup.authority JoinClassName = 'com.app.UserRole'
grails.plugins.springsecurity.authority.className = 'com.app.Role'
grails.plugins.springsecurity.userLookup.usernameP ropertyName = 'email'
grails.plugins.springsecurity.securityConfigType = "Requestmap"
grails.plugins.springsecurity.rejectIfNoRule = true
grails.plugins.springsecurity.requestMap.className ='com.app.Requestmap'
grails.plugins.springsecurity.requestMap.urlField= 'url'
grails.plugins.springsecurity.requestMap.configAtt ributeField='configAttribute'
grails.plugins.springsecurity.rememberMe.cookieNam e = 'remember_me'
grails.plugins.springsecurity.cacheUsers = false
grails.plugins.springsecurity.rememberMe.tokenVali ditySeconds=604800
Has anyone had a similar problem may be?
thank you in advance :)
Setting the UserDetails.enabled flag to false should cause a DisabledException to be thrown on the user's next secure request. This can either send the user to the default authfail handler, or you can configure an exception handler in your UrlMappings to send the user to a custom controller or action.
If you're not using the enabled flag anywhere else in your application, you can direct the DisabledException to an action which clears the authentication session (and rememberMe), then resets the enabled flag.
Another possible way would be to create a custom filter and inject it into the spring security filter chain in your Bootstrap.
Both the url exception mapping and the filter configuration are described in the Spring Security Plugin Documentation
Thanks Codelark, I'm reading about filters and try to use them :)
Unfortunately, when I set enabled = false for the selected user application does not redirect it to "/login /authfail".
Best of all, the changes (enabled = false) are visible to the user profile but the lack of the desired effect.
I would add, I tried to set "expireNow" a user session:
def expireSession(User user) {
def orginalUser = springSecurityService?.principal.username
springSecurityService?.reauthenticate(user?.email) //modified user np: test#app.com
springSecurityService?.reauthenticate(orginalUser) //admin np: admin#app.com
sessionRegistry.getAllPrincipals()?.each { princ ->
sessionRegistry.getAllSessions(princ, false);
if(princ?.username?.equals(user?.email)) { //killing sessions only for user (test#app.com)
sessionRegistry.getAllSessions(princ, false)?.each { sess ->
sess.expireNow()
}
}//if
}//each
}//expireSession
Namely sessionRegistry really gets active sessions for each user, but by calling:
sess.expireNow()
The result is that calling expireSession (user) for the same user again, the session is no longer visible. Which is understandable because it has expired.
But in spite of expired user session. He may continue to work in the application. The application does not log you off

Grails Spring Security: Logging in with a target URL skips post authentication workflow

In my grails app I have customized the post authorization workflow by writing a custom auth success handler (in resources.groovy) as shown below.
authenticationSuccessHandler (MyAuthSuccessHandler) {
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
superAdminUrl = "/admin/processSuperAdminLogin"
adminUrl = "/admin/processAdminLogin"
userUrl = "/admin/processUserLogin"
}
As you can from the last three lines in the closure above, depending on the Role granted to the logging in User I am redirecting her to separate actions within the AdminController where a custom UserSessionBean is created and stored in the session.
It works fine for a regular login case which in my app is like so:
User comes to the app via either http://localhost:8080/my-app/ OR http://localhost:8080/my-app/login/auth
She enters her valid login id and password and proceeds.
The app internally accesses MyAuthSuccessHandler which redirects to AdminController considering the Role granted to this User.
The UserSessionBean is created and stored it in the session
User is taken to the app home page
I have also written a custom MyUserDetailsService by extending GormUserDetailsService which is correctly accessed in the above flow.
PROBLEM SCENARIO:
Consider a user directly accessing a protected resource (in this case the controller is secured with #Secured annotation) within the app.
User clicks http://localhost:8080/my-app/inbox/index
App redirects her to http://localhost:8080/my-app/login/auth
User enters her valid login id and password
User is taken to http://localhost:8080/my-app/inbox/index
The MyAuthSuccessHandler is skipped entirely in this process and hence my UserSessionBean is not created leading to errors upon further use in places where the UserSessionBean is accessed.
QUESTIONS:
In the problem scenario, does the app skip the MyAuthSuccessHandler because there is a target URL for it to redirect to upon login?
Can we force the process to always pass through MyAuthSuccessHandler even with the target URL present?
If the answer to 2 is no, is there an alternative as to how and where the UserSessionBean can still be created?
You can implement a customized eventListener to handle the post-login process, without disrupting the original user requested url.
In config.groovy, insert a config item:
grails.plugins.springsecurity.useSecurityEventListener = true
In you resources.groovy, add a bean like this:
import com.yourapp.auth.LoginEventListener
beans = {
loginEventListener(LoginEventListener)
}
And create a eventListener in src/groovy like this:
package com.yourapp.auth
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
import org.springframework.web.context.request.RequestContextHolder as RCH
class LoginEventListener implements
ApplicationListener<InteractiveAuthenticationSuccessEvent> {
//deal with successful login
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
User.withTransaction {
def user = User.findByUsername(event.authentication.principal.username)
def adminRole = Role.findByAuthority('ROLE_ADMIN')
def userRole = Role.findByAuthority('ROLE_USER')
def session = RCH.currentRequestAttributes().session //get httpSession
session.user = user
if(user.authorities.contains(adminRole)){
processAdminLogin()
}
else if(user.authorities.contains(userRole)){
processUserLogin()
}
}
}
private void processAdminLogin(){ //move admin/processAdminLogin here
.....
}
private void processUserLogin(){ //move admin/processUserLogin here
.....
}
}
Done.
1) Yes, because it is an "on-demand" log in.
2) Yes, you can set it to always use default. The spring security plugin has a setting for it "successHandler.alwaysUseDefault" change that to true it defaults to false.
Also if you need more details check out the spring docs look for the Setting a Default Post-Login Destination section.
3) If you want to still create the user session bean and then redirect to the original URL you have two options create the bean in an earlier filter or expose the needed data via a custom UserDetailsService. Personally I would go the route of a custom details service.

Resources