Access UserDetails Object inside AuthenticationFailureHandler Spring Security 4.x - spring-security

I’m migrating an application from Spring Security 3 to 4. The applications utilizes both form based username/password and smart card authentication. It also has a custom AuthenticationFailureHandler that redirects the user to a particular page based on what type of error occurred and the roles associated to the user. For example a locked exception should be handled differently for internal vs external users.
In Spring Security 3 the org.springframework.security.authentication.AccountStatusUserDetailsChecker used to place the UserDetails object into the extraInformation property of the AuthenticationException it threw. The custom AuthenticationFailureHandler we have utilized that to extract the roles from the user who failed authentication so it could handle the different user types appropriately:
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
User user = (User)exception.getExtraInformation();
if(exception instanceof LockedException){
if(user.hasAnyRole(Role.INTERNAL)){
//Send to internal user page
}else{
//Send to external user page
}
}
}
However now with Spring Security 4, I’m not sure the best way to get the information about the user into the failure handler so I can appropriately route the request. What would be considered the ‘best practice’ approach for this?
One approach is to extract the relevant information from the request (Either the form based username or the X509 cert) and then look up the roles using a database query but since that is already handled earlier in the Spring Security chain it seems like duplicating that effort in the failure handler isn’t the best approach.
Thanks

Related

spring security: what's the best practice for include value in permission?

In spring security, or RBAC, the Authority is described as a string, such as "download-file" means user can download file. If I need to limit user maximum daily download times and assign different values to different user, means the Authority contains dynamic values, how can I do this in spring security?
As you are alluding to there is a difference between authorities (i.e. roles) and permissions. Authorities tend to broadly apply for an application and have no state while permissions tend to be on specific objects and contain state.
This seems more like a domain problem than a permissions problem. Putting the logic into security feels a bit like having a form that must contain a valid email and checking the email format in security. I'd consider moving the logic outside of the security code.
If you really want to do this with Spring Security, I'd use a custom Bean that performs the check:
#Component
public class Download {
public boolean isAlowedForUser(Authentication authentication) {
// ...
return result;
}
public boolean isAllowedForCurrentUser() {
return isAllowedForUser(SecurityContextHolder.getContext().getAuthentiation());
}
}
Then you can autowire the Bean into your code and check the permission by invoking the code. If you prefer, you can also integrate into Spring Security's method security to perform the checks. To enable it you need to specify #EnableGlobalMethodSecurity(prePostEnabled = true) at the top of one of your configuration classes. Then you can use something like this on a Spring managed Bean:
#PreAuthorize("#download.isAllowedForCurrentUser()")
public void downloadFile(String fileName) {
Please refer this link
Spring Boot : Custom Role - Permission Authorization using SpEL
You can add new permission , like "DOWNLOAD_FILE" and authenticate if the current user has that permission using -
#PreAuthorize("hasPermission('DOWNLOAD_FILE')")
You can also limit access for Roles as well
#PreAuthorize("hasRole('ADMIN') and hasPermission('DOWNLOAD_FILE')")

how do I add a 'groups' custom claim to id_token issued by Cognito?

I want to use a custom claim on an open id connect id_token to indicate the groups that the user belongs to.
I have this working with Okta and Spring Security 5. With Okta there was a simple way to set-up a custom claim and associate it with a user's groups.
Now I want to do the same with Cognito.
How do I use Spring Securities group/role authorities in conjunction with Cognito?
I have open id connect login working with Spring Security 5, Webflux & Cognito but now I want to add some role based, group membership coarse-grained entitlements.
I have added a custom attribute to my user-pool but can't see how to populate it.
thanks
Just want to add an implementation example here.
I use my custom JwtAuthenticationConverter to extract cognito:groups and bind them to Spring Security Authorities.
Stack is the same as yours (Webflux, Spring 5 [boot]). Here is the converter class:
#Component
public class GrantedAuthoritiesExtractor extends JwtAuthenticationConverter {
#Override
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
val authorities = (Collection<String>) jwt.getClaims().get("cognito:groups");
if (authorities == null) {
return emptyList();
}
return authorities.stream()
.map(SimpleGrantedAuthority::new)
.collect(toList());
}
}
And here is the related part in my SecurityConfiguration:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
GrantedAuthoritiesExtractor extractor) {
http
// ...
// Other configurations
// ...
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new ReactiveJwtAuthenticationConverterAdapter(extractor));
return http.build();
}
Also one point that should be mentioned is that #EnableReactiveMethodSecurity annotation should be placed above SecurityConfiguration class to enable functionality of #PreAuthorize annotations across the application.
I have created another user pool and confirm that adding a custom attribute to the Cognito user pool with the name of 'groups', that is mutable, has resulted in the id_token returned from an openid scope call having a cognito:groups custom claim containing strings representing the groups that the user is allocated to. If the user isn't allocated to any groups then the claim doesn't appear on the token. I suspect this is why I believed it to not be initially working. I couldn't find any explicit documentation that said this his how it works thought. Hope this helps someone, cheers.

Manually setting authenticated user on a websocket session using spring security

I'm developing a Spring boot application using STOMP messaging over a websocket implementing an RPC pattern which exposes public (i.e.: without any need to be authenticated) and private methods.
Two public methods exists to register and authenticate the user, so I need to manually (programmatically) handle the user login, method accessed via a custom #MessageMapping operation.
The question is: how do I authenticate the user of the websocket session?
To make a similar thing using a normal MVC web application I would use a code similar to this:
#MessageMapping("/auth")
public Object auth() {
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_TEST");
SecurityContextHolder.getContext().setAuthentication(new PreAuthenticatedAuthenticationToken(myname, "dummy", authorities));
}
but this seems not work in a websocket environment because when the user a method like this:
#MessageMapping("/myendpoint")
public Object myEndpoint(Param params, Principal principal) {...}
I get the error:
org.springframework.messaging.simp.annotation.support.MissingSessionUserException: No "user" header in message
So the question is: how do I manually authenticate the user in the auth() method so that in myEndpoint() the Principal parameter is correctly resolved?

Failing authentication in MVC pipeline

Given that the FormsAuthentication module fires before a custom http module that handles the OnAuthenticateRequest, I'm curious if one can cancel or invalidate the forms authentication based on my own criteria.
Basically I have a process where the user logs in. After that they get a token. I get the token back after the forms authentication fires upon subsequent requests. What I want to do is then validate that the token hasn't expired against our back end server. If it's expired I need to do something so that they are forced to log back in. My thought was to do something in my OnAuthenticateRequest handler that would get picked up later in the pipeline and force a redirect back to login page or something. Is that possible?
In an ASP.NET MVC application in order to handle custom Authentication and Authorization people usually write custom Authorize attributes. They don't deal with any OnAuthenticateRequest events. That's old school. And by the way if you are going to be doing some custom token authentication why even care about Forms Authentication? Why not replace it?
So:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string token = GetTokenFromHttpContext(httpContext);
if (IsTokenValid(token))
{
// The user has provided a valid token => you need to set the User property
// Obviously here based on the token value you already know which is the
// associated user and potential roles, so you could do additional checks
var identity = new GenericIdentity("john.smith");
var user = new GenericPrincipal(identity, new string[0]);
httpContext.User = user;
return true;
}
// Notice that here we are never calling the base AuthorizeCore method
// but you could call it if needed
return false;
}
private string GetTokenFromHttpContext(HttpContextBase httpContext)
{
// TODO: you know what to do here: get the token from the current HTTP Context
// depending on how the client passed it in: HTTP request header, query string parameter, cookie, ...
throw new NotImplementedException();
}
private bool IsTokenValid(string token)
{
// TODO: You know what to do here: go validate the token
throw new NotImplementedException();
}
}
Now all that's left is to decorate your controllers/actions with this custom attribute instead of using the default one:
[MyAuthorize]
public ActionResult SomeAction()
{
// if you get that far you could use the this.User property
// to access the currently authenticated user
...
}
Is that possible?
This is definitely possible. You could even set your autehtication scheme to None so that forms module isn't there in the pipeline and have only your own module.
However, even if forms is there, your custom module can override the identity set for the current request. Note also that until the forms cookie is issued, forms module doesn't set the identity. This was quite common to use both forms module and the SessionAuthenticationModule - forms does the job of redirecting to the login page and the session auth module handles its own authentication cookie.
This means that you can safely mix the two: the forms module and your own custom module for a similar scenario.
Darin suggests another approach and this of course is valid too. An advantage of an authentication module (versus the authentication filter) is that the authentication module could support other ASP.NET subsystems (web forms / wcf / webapi).

SpringSecurity: how to specify remember me authentication?

I'm developing an app based on Grails and Vaadin 7. I managed to make them work with SpringSecurity for authentication and authorization, but I had to develop my own Service that calls the Spring Security authentication manager to make it work with Vaadin:
class SecurityService {
static transactional = true
def springSecurityService
def authenticationManager
void signIn(String username, String password) {
try {
def authentication = new UsernamePasswordAuthenticationToken(username, password)
SCH.context.authentication = authenticationManager.authenticate(authentication)
} catch (BadCredentialsException e) {
throw new SecurityException("Invalid username/password")
}
}
}
The problem is that now I need to implement a remember me authentication and I don't know from where to start.
How do I make the authenticationManager know that I want it to use remeberMeAuthentication? I can get a boolean value from a checkbox on the login View, but what do I do with it next?
Since your question is specific to the handling of checkbox value (remember me flag) coming from login page, the answer is that you have to call loginSuccess or loginFail method of RememberMeServices. The loginSuccess adds auto-login cookie in the response and loginFail removes that cookie.
But I guess above answer won't help you much unless you are sure that you have RememberMeServices configured in your app. Maybe following steps that configure RememberMeServices will help you do whole thing your way (or help you understand the out of the box functionality):
(1) Create a class (call it myRememberMeServices) that implements RememberMeServices and LogoutHandler.
(2) In autoLogin method, create an authentication object (UsernamePasswordAuthenticationToken) after parsing the cookie value.
(3) In loginFail method, cancel the cookie.
(4) In loginSuccess method, create an auto-login cookie. Add value that you would use in autoLogin method. Usually cookie value is encrypted.
(5) In logout method , cancel the cookie.
(6) Inject myRememberMeServices in following four places and call appropriate method:
(a) At the time of successful login (if checkbox value is set),
(b) At the time of failed login,
(c) On logout, and
(d) In filter that does autologin
It is worth noting that RememberMeAuthenticationFilter takes authenticationManager and RememberMeServices in its constructor.
Answer to your other question is that the authenticationManager doesn't need to know anything about remember me. It is the filter (or any class handling auto login) that needs to know about authenticationManager and RememberMeServices. (In other words, ask RememberMeServices for a token and pass it to authenticationManager to do auto login).
Spring Security's architecture is based on servlet filters. The sign-in mechanism you have implemented above is normally done by the UsernamePasswordAuthenticationFilter. Another filter called RememberMeAuthenticationFilter takes the responsibility for the remember-me functionality. The authenticationManager is not aware at all whether the remember-me feature is used by the application or not.
If you want to integrate Spring Security with another web-framework, first try to find out how the filters of the two frameworks can play together.

Resources