Grails 3 and Spring Security: return 401 when user is not logged in - authentication

In my Grails 3.2.9 web-app I'm using Spring Security plugin to manage user session. This is the depencency:
compile "org.grails.plugins:spring-security-core:3.1.1"
The natural (years-long) evolution of the app brought to have basically all actions in all controllers, mostly secured using #Secured annotations, to return a JSON, with something like
return map as JSON // grails.converters.JSON
That means that all actions are basically acting like APIs.
But since they're secured, when user is not logged, a redirect is performed to /login/auth (login page), which is something you wouldn't expect. This is why I'm searching for a way to return 401 unauthorized status instead of letting Spring Security perform a redirect.
So far I've looked into pessimistic lockdown, and searches across the web also lead me to Spring Security Core REST plugin, but both ways don't seem to adapt to my case (to me at last, but maybe I'm missing something).
So any suggestion is welcome, thanks in advance!

Register the following in resources.groovy
authenticationEntryPoint(Http401AuthenticationEntryPoint, 'Bearer')

I do not have experience in Grails but perhaps what you are looking for can be implemented by providing a different implementation of org.springframework.security.web.AuthenticationEntryPoint in your Spring security configuration. By default for form authentication Spring uses org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint which performs redirect to the given login page. On the other hand org.springframework.security.web.authentication.HttpStatusEntryPoint just returns the desired status.
In our project entry point is set in the old fashioned way through XML configuration:
<http pattern="/**" auto-config="false" entry-point-ref="yourAuthenticationEntryPoint">

Related

Google Sign-in and Spring Security

I am ashamed to admit that I burned four full days trying to get Spring Security 3.1 to play nicely with Google Sign-in in a standard JSF web application. Both are awesome frameworks in their own right but they seemed incompatible. I finally got it to work in some fashion but strongly suspect that I have missed some fundamental concept and am not doing it the best way.
I am writing an app that our helpdesk uses to track system testing during maintenance activities when our systems are down and cannot host the app, so it is hosted externally. Our Active Directory and IdP are down during this activity so I cannot use our normal authentication systems. Google Sign-in is a perfect solution for this.
Google Sign-in works great in the browser using Google Javascript libraries and some simple code. The browser communicates with Google to determine if the user is already signed in, and if not, opens a separate window where the user can submit credentials and authenticate. Then a small bit of Javascript can send a concise, ephemeral id_token returned from Google to the server which the server can use to verify the authentication independently with Google. That part was easy. The beauty is that if the user is already signed into Gmail or some other Google app, authentication has already happened and Google does not challenge the user again.
Spring Security works great on the server side to protect specified resources and authenticate a user with a username and password. However, in this case, we never see the username or password - the credentials are protected by secure communication between the browser and Google. All we know is whether or not the user is authenticated. We can get the Google username, but Spring Security expects credentials that it can use to authenticate, to a database, in-memory user base, or any other system. It is not, to my knowledge, compatible with another system that simply provides yea-or-nay authentication in the browser.
I found many good examples online that use Spring Boot with EnableOAuth2Sso (e.g. here) but surprisingly few that use Spring Security in a standard app server which does not support EnableOAuth2Sso, and those few did not show any solution I could discern.
Here is how I've done it. I followed Google's simple directions here to provide authentication in the browser. I added this code to the onSignIn() method to send the id_token to the server.
var xhr = new XMLHttpRequest(); // Trigger an authentication for Spring security
xhr.open("POST", "/<my app context>/j_spring_security_check", true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var params = "profileID=" + profile.getId() + "&fullname=" + profile.getName() + "&email=" + profile.getEmail() + "&id_token=" + googleUser.getAuthResponse().id_token
+ "&j_username=" + profile.getEmail() + "&j_password=" + id_token;
xhr.send(params);
window.location.replace("/<my app context>/index.xhtml");
Unfortunately the Spring Authentication object, when passed to the AuthenticationProvider that I provided, did not contain anything but the j_username and j_password parameters as Authentication.getPrincipal() and Authentication.getCredentials(), but this is all I really needed. This is a bit of an abuse of those parameters since I have set them to email and id_token, not username and password.
I wanted to pass the user's full name and email, which Google provides in Javascript as googleUser.getName() and googleUser.getEmail(), to the backend as well. Since Spring Security does not accommodate anything but the username/password, and I was using Primefaces/JSF, I used Primefaces RemoteCommand to call a method on the backing bean with this information. This also feels a little clumsy.
In addition, I had to use window.location.replace() (in code above) because Spring Security did not redirect to my index.xhtml page as expected when I set this in the context with:
<security:form-login login-page='/login.xhtml' authentication-failure-url="/login.xhtml?error=true" default-target-url="/index.html" always-use-default-target="true" />
I have no idea why this does not work.
However, the app does now behave as I want in that it authenticates the user and the authenticated user can access the resources specified in Spring Security, and I wanted to share this in case anyone is doing a similar thing. Can anyone suggest a cleaner/better way? Thanks in advance.

How do I get Basic Authentication, GlassFish, REST, and a single page application to all work together with my own login form?

I'm using Glassfish 4 as a server with an AngularJS app as a client. Glassfish is exposing a REST API via JAX-RS (Jersey). I'm using Basic Authentication over an HTTPS connection. I have my own login form and am setting the Authorization header in my REST requests via JavaScript. My issue is that if I use normal web.xml based permissions (<auth-constraint> inside <security-constraint>), the responses come back with 401 with a WWW-Authenticate header (if the credentials are bad). This forces the browser to do the Basic Authentication dialog instead of my own and it appears there is no viable cross browser work around available on the browser side to stop it. So I need to somehow suppress the 401/WWW-Authenticate response.
I stopped using the web.xml based permissions, because it seems it is the Servlet level that is doing the 401 stuff. I was able to get Jersey authentication working with a filter and turning on the "RolesAllowedDynamicFeature" feature (in a matter similar to Glassfish #RolesAllowed with custom SecurityContext). That seems to work great and returns 403 for bad credentials (and thus no browser dialog). However, when I call my EJB's, they do not see the custom security context and the user I have set, so I get permission exceptions. If it matters: the EJB's are in a jar, the Jersey stuff is in a war, and both of them and bundled together in an ear. From what I can gather the only way to have the EJB's properly process credentials is to use the web.xml stuff.
I seemed to have painted myself into a corner and do not see how to make this work. Perhaps I can back out and return to using web.xml based permissions and somehow filter the servlet responses to not return 401/WWW-Authenticate? If so I could not find out how to do that. Or is there some way I can set EJB's security context? Or something else entirely? I wouldn't think using AngularJS with GlassFish and a REST API and Basic Authentication would be very unique, how does anyone do this?
Since posting this question I have found info on implementing a Servlet filter and using that to try to change the 401 response to a different status code. However, the filter never gains control if you have <auth-constraint> in your web.xml and the request is not authorized, so that did not help me. I still could not prevent the 401 responses.
But now I think I finally found the answer. I removed the <auth-constraint> tag from web.xml. I changed the Servlet filter to now extract the AUTHENTICATION_HEADER on its own and decode it (via javax.xml.bind.DatatypeConverter). Next I call HttpServletRequest.login (on the request object) with the decoded username and password. I catch the ServletException if the username/password combination is bad and use HttpServletResponse.sendError to send SC_FORBIDDEN. If I have a good login I call doFilter to continue on with processing of the request, and call HttpServletRequest.logout when it returns.
If I do this in combination with RolesAllowedDynamicFeature and annotations on the Jersey routines everything seems to work including calls to EJB's with their own security annotations.
Side note: before settling on HttpServletRequest.login I had tried to use HttpServletRequest.authenticate followed by checking the returned boolean, but when you gain control in that case the response has already been committed to be 401 and you cannot change it. I even tried passing a HttpServletResponseWrapper to authenticate to stop the commit from happening, but authenticate seems to get the response object through some other means, it seems to ignore the one you pass it (I even tried passed null and it didn't even notice).

Grails using Google authentication with the Spring Security plugin

Has anybody managed to successfully combine Google authentication with Burt Beckwith's awesome Grails-based Spring Security plugin recently? I wanted to go down that path with Grails 2.4.3, and after some fooling around (and recompiling the donbeave version of the plugin at https://github.com/donbeave/grails-spring-security-oauth-google) I was able to find a combination of references that would compile and run together. I ended up adding the following lines to my BuildConfig.groovy:
compile ':spring-security-core:2.0-RC4'
compile ":spring-security-oauth:2.1.0-RC4"
compile ':spring-security-oauth-google:0.3.1'
I found, however, that the changes created by the initialization command “grails s2-init-oauth” don’t give me all the modifications that I need in order to move forward. I ended up adding a block to my config.groovy that looked like this:
oauth {
providers {
google {
api = org.grails.plugin.springsecurity.oauth.GoogleApi20
key = 'MY KEY'
secret = 'MY SECRET'
successUri = '/oauth/google/success'
failureUri = '/oauth/google/error'
callback = "${baseURL}/oauth/google/callback"
scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
}
}
}
These config definitions specify a callback in my code (referred to above as ./oauth/google/callback) which didn’t exist. After I brought in a controller from the recommended example (https://github.com/bagage/grails-google-authentification-example), substituted "/springSecurityOAuth/onSuccess" for "/oauth/google/callback", (and registered by redirect URL through the Google Developers Console) I found that my onSuccess method was indeed being called, but the data structures referenced in the controller were wrong, and it seemed as if I would need to largely rewrite the controller logic in order to get everything working. I have to assume that other people want to accomplish Google-based authentication in the same way that I do. Is there an complete operational example somewhere? Or can someone tell me where I’ve gone wrong in my attempt to utilize the standard plug-ins? Thanks for any assistance.
You need to use spring security oauth plugin also. Please refer here https://github.com/cazacugmihai/grails-spring-security-oauth,
When you click on button, it hits the authenticate action inside Oauth controller which gets
authentication()
url of the google. After successful authentication, it hits callback() action Of Oauth controller which then redirects to onSuccess() action of SpringSecurityOauthController which then saves the info to OAuthId domain and finally redirects to the successUri given in config.

Jhipster authentication header

We are starting project based on the great jhipster work.
While we would like to keep the option to be session based (with a login page), we also have the need for enalble clients pass user info in the header of each request,
(e.g. Authenticate: username:password) to have it session less like without login, as regular users do.
Do we have this implemented OOTB? If not, what is the best approach for having that?
Thanks ahead for any comment & answer.
JHipster supports both "session-based" (with a login page, and the Spring Security token is stored in the HTTP Session), and OAuth2 (which is stateless, and the token are stored in the database).
The OAuth2 mechanism also uses a login page, but I guess you could make it work without it if needed -> isn't this the best solution for you?
We also have another stateless mecanism in the pipe, see https://github.com/jhipster/generator-jhipster/issues/892

How to make the login/ page in Grails Spring Security 2.0 the inital screen?

I am migrating from grails 2.2.2 to grails 2.3.4 to avoid a bug in 2.2.2 where the text value in the spring security property messages where not displaying, but I am running into all sorts of issues. Stuff that worked before, now it does not.
PROBLEM
When I run the grails app, the initial default page is index.gsp which is standard functionality but after installing and configuring the spring security core, spring security ldap, and spring securiy ui plugins I would like to make the /login/auth my default page.
In the previous release, I had it done via the UrlMappings.groovy config file by simply commenting, replacing or deleting this line
"/"(view:"/index")
for this one
"/"(view:"/login/auth")
My Config.groovy is set so that if the authentication is successfull to take me to the home page
grails.plugin.springsecurity.userLookup.userDomainClassName = 'security.Person'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'security.PersonAuthority'
grails.plugin.springsecurity.authority.className = 'security.Authority'
grails.plugin.springsecurity.requestMap.className = 'security.Requestmap'
grails.plugin.springsecurity.securityConfigType = 'Requestmap'
grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/home/'
and my Requestmap entries in the Bootstrap (if they are of any importance for this issue are as follows):
for (String url in [
'/', '/index', '/index.gsp', '/**/favicon.ico',
'/**/js/**', '/**/css/**', '/**/images/**',
'/login', '/login.*', '/login/*',
'/logout', '/logout.*', '/logout/*']) {
new Requestmap(url: url, configAttribute: 'permitAll').save()
}
new Requestmap(url: '/home/*', configAttribute: 'IS_AUTHENTICATED_FULLY').save()
It turns that when i do that... Eureka the login/auth comes as soon as the application is started but when I put the correct authentication credentials it does not seem to authenticate, it does does a 'slight little flicker' and it shows itself again.
However, If I delete this line
"/"(view:"/login/auth")
and put this one back in
"/"(view:"/index")
and then when I restart the application I manually to login/auth and put the correct credentials then it correctly takes me to the home page.
QUESTIONS
Did I miss any config setting anywhere that would make the login/auth the first page (but also allowing me to authenticate)?
I am not sure if this should be a separate posted question, but now by design the login page it's part of the plugin, before it was generated by and part of my code and I could style at my will. Do I have to copy paste the LoginController and the Auth.gsp in my app in order to customize the view or is there a better preferred way?
Thanks in advance.
The authentication mechanism in Spring Security works by keeping track of a referral URL when the login page is shown. And then redirecting to this page on a successful login. If you want the login page to be the first page people see just make the root view require authentication. I'm assuming most, if not all, of your application requires authentication. If this is the case, you don't need to make the login page the root view. Assuming everything under /home/* is locked down then Spring Security will detect that and immediately redirect to the login page when any of the secured pages are requested.
Long story short, you're making it harder than it needs to be.
As to your second question, I do believe you just need to create your own versions of those files in your app to customize them.
Hey I'm not pretty sure about your problem but you can try making the default login url /login/auth by
grails.plugin.springsecurity.auth.loginFormUrl = '/login/auth'