How to access the URL that Acegi has stored for after the login form in Grails - grails

I'm integrating Gigya with a web app running Acegi.
I have it working that the client side Gigya can authenticate an existing user and then skip the login form post and hit a controller method to inform the server securly that the user authentication has been performed by Gigya.
Using the following code in my controller I'm able to tell Acegi that the user has authenticated.
def user = com.playhardsports.football.web.admin.auth.User.find("from User where username=?", [UID])
def authorities = [new GrantedAuthorityImpl('ROLE_USER')] as GrantedAuthority[]
def userDetails = new org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserImpl(UID, fakePassword, true, true, true, true, authorities, user)
def authentication = new UsernamePasswordAuthenticationToken(userDetails, fakePassword, authorities)
SecurityContextHolder.context.authentication = authentication
The problem I'm having now is that I don't know where to redirect the user after the authentication.
A common scenario is that the user visits a protected page and Acegi redirects them to the login form. On my login form I also have the controls for Gigya to validate the user. Of course, meanwhile, the normal Acegi flow would be after login to redirect back to the original protected page.
So I'm looking for how to access that url, and if there was no url, because the person went straight to login, then how to find the default url that Acegi has configured.
Thanks.

You can access the SavedRequest from the session:
import org.springframework.security.ui.savedrequest.SavedRequest
import org.springframework.security.ui.AbstractProcessingFilter as APF
def savedRequest = session[APF.SPRING_SECURITY_SAVED_REQUEST_KEY]
String originalUrl = savedRequest?.fullRequestUrl ?: '/'

Related

How to configure post logout redirect uri in ADFS 2018?

The user is logged off and the ADFS default logout page shows up, how to redirect to another page after logout and how to configure ADFS post_logout_redirect_uri?
if you are using asp.net application then inside the WsFederationAuthenticationOptions you will have to set SignOutWreply variable like:
new WsFederationAuthenticationOptions
{
SignOutWreply = "URL",
}

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.

Does Spring Security gives any such API where I can pass username & password and get the Authentication Object?

Does Spring Security gives any such API where I can pass username & password and it will return either Authentication Object for successful authentication or AuthenticationCredentialsNotFoundException for unsuccessful authentication?
Let me elaborate my requirements:
Our application has a HTTP API(say, /createXXX.do) and the client is hitting this with username, password & other parameters.
Now I want to authenticate + authorize this access (coming from HTTP Hits to my application).
My planned design is like below:
a) I will not restrict access of my HTTP API context(i.e. /createXXX.do)
b) Once the request reached my doGet()/doPost(), I will retrieve the username & password from request and want to use some spring security API like below:
Authentication validateXXXXX(String username, String password)
throws AuthenticationCredentialsNotFoundException;
c) so that this above API internally push these username/password to the existing spring security chain and return me the Authentication Object for successful authentication or AuthenticationCredentialsNotFoundException for unsuccessful authentication.
d) For unsuccessful authentication, I will catch AuthenticationCredentialsNotFoundException and return the HttpServletResponse with AUTHENTICATION_ERROR code.
e) and for successful authetication, based on authiories from Authentication Object, I will allow or return the HttpServletResponse with AUTHORIZATION_ERROR code.
Can anyone know about such spring security API?
Any pointers/suggestion will be highly appreciated.
Thanks.
If you have just one authentication source (only LDAP or only DB) you can configure some implementation of org.springframework.security.authentication.AuthenticationProvider in your security context. Then you can use it:
User user = new User(login, password, true, true, true, true, new ArrayList<GrantedAuthority>());
Authentication auth = new UsernamePasswordAuthenticationToken(user, password,new ArrayList<GrantedAuthority>());
try {
auth = authenticationProvider.authenticate(auth);
} catch (BadCredentialsException e) {
throw new CustomBadCredentialsException(e.getMessage(), e);
}
// but your need to push authorization object manually
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);
It is "low level" manipulation. You can use element from Spring Security namespace. It can provide login controller, even login form for you (and it can handle this situation automatically).

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.

How to programmatically logout in acegi plugin in Grails?

Is there a similar logic for logout using this login code:
// login account
def autht = new AuthToken(username, password)
def authtoken = daoAuthenticationProvider.authenticate(autht)
SecurityContextHolder.context.authentication = authtoken
I checked LogoutController and this is the only logic for logout:
redirect(uri: '/j_spring_security_logout')
Any idea? Thanks
You would need to invalidate the session:
session.invalidate()
and remove the authentication:
SecurityContextHolder.clearContext()
You also need to remove their remember-me cookie if that's supported. Add a dependency injection for rememberMeServices ("def rememberMeServices") and call
rememberMeServices.logout request, response, null

Resources