Spring Security force logout when password change - spring

I have 2 user role ADMIN and USER. ADMIN can change USER's password. I want to force logout when user's password changed by ADMIN. I can save changed password and use them when next login. But I want to force logout to them.
UserDetails userDetails = userDetailsService.loadUserByUsername(vendor.getUsername());
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setAuthenticated(false);
This is not working.

First you need to configure the session configuration under your security configuration as follows
#Override
protected void configure(HttpSecurity http) throws Exception {
// this enables ConcurrentSessionFilter to allow us to read all sessions by using getAllPrincipals
http
.sessionManagement().maximumSessions(10)
.sessionRegistry(sessionRegistry())
.expiredUrl("/login?expire");
// Rest of the configuration
}
This enables you to call sessionRegistry.getAllSessions to manage the list of active session and expire them. SessionRegistry is autowired FYI.
List<Object> principals = sessionRegistry.getAllPrincipals();
for (Object principal: principals) {
// Check for the principal you want to expire here
List<SessionInformation> sessionInformations = sessionRegistry.getAllSessions(principal);
for (SessionInformation sessionInformation : sessionInformations) {
sessionInformation.expireNow();;
}
}

Related

Spring Boot 2 + Oauth2: How to have separate logins for regular users and admins?

So, I am using Spring Boot and Security for a while now. So far I only had one "kind" of user which were simply given roles USER_ROLE or ADMIN_ROLE in order to secure my REST endpoints.
However, I am now at a point where I realize: I only have one login. That is the default /oauth/token endpoint which, eventually, loads a user from my database and adds the authorities accordingly:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
username = username.trim();
AppUserEntity appUserEntity = this.appUserRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found."));
List<GrantedAuthority> authorities = new ArrayList<>();
Collection<AppUserRoleEntity> roles = appUserEntity.getRoles();
for (AppUserRoleEntity appUserRoleEntity : roles) {
RoleEntity roleEntity = appUserRoleEntity.getRole();
authorities.add(new SimpleGrantedAuthority(roleEntity.getRoleType().toString()));
}
return new AppUserDetails(
appUserEntity.getId(),
appUserEntity.getEmail(),
appUserEntity.getPassword(),
authorities,
appUserEntity.getActivated()
);
}
The problem with this is, that there is no distinction between users. I do not know here which login (on my website) the user was using. A login will always work, even if a normal user uses the admin-login mask.
What I seek is a way to have different registration and login endpoints for admin and regular users. How would I do that?
I have seen this tutorial and also this one but they do not use OAuth2.
What are my options here and/or what is the Spring Boot way to do this?
Authentication and Authorization are two things. With OAuth, you are doing only the authentication part. Meaning, it checks whether the user has a valid username and a password. It is the application's responsibility to allow or deny access to certain areas of the application based on the grant/role assigned to the authenticated user.
You authenticate users with oauth/token endpoint and pass the token to the application with every request. Then in application's security configurations, you restrict admin area to only users who are in ADMIN_ROLE.
Please check the following section of a sample spring security configuration.
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() //User must have a valid token
.antMatchers("/admin/**").hasRole("ADMIN"); //User must be of ADMIN_ROLE
}
So, any user having a valid username and a password cannot access (login is a bit vague word) admin area of your application.
In the above configuration, if a non-admin user try to access /admin/employee after authenticating through oauth/token, it will throw 403 error. In other terms, that user is not allowed to login to that area of the application.

How to customize the behavior of session scoped bean by current user is Spring MVC

Consider following scenario: Spring Security authenticates login data against custom UserDetailsServiceimplementation as follows
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
UserProfile profile = users.find(name);
if (profile.getUsername().equals("admin"))
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new User(profile.getUsername(), profile.getPassword(), authorities);
}
If authentication succeeds, I want to create unique session scoped service in controller, with customized behavior by valid UserProfile object state. I guess best way to do that is to declare the session bean manually in configuration file and somehow autowire UserProfile or session owner to it's constructor, but how that's possible, when UserProfile is not even an managed object?
In this case, I want server to create service for authenticated user, which maintains SSH connection to remote host with credentials stored in UserProfile
Also, how to restrict a creation of such service just to post login? Is there way to achieve this kind of behavior, or is it actually bad architecture?
You can use the SecurityContextHolder to access the authenticated user for the current request. I think the best approach is to create a singleton Service with a method like this:
public UserDetails getCurrentUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
return (UserDetails) principal;
} else {
//handle not authenticated users
return null;
}
}
Now you can autowire and use the service in your controllers.

Spring Boot - displaying the OTP page, restrict access to other pages using their URLs (after successful authentication on the login page)

I have created a Spring Boot web application, where after successful login, I have to send the user to the OTP page.
My problem is: When the user comes to the OTP page he can bypass it changing the URL, so he can access any page (i.e. big security risk) because the user has already authenticated from the login page.
How can I restict URL changes on the OTP page as it happens on the login page (using Spring boot-security), so the user can only get in if she/he is authenticated by OTP.
A common approach is that on successful authentication - i.e. credentials entered on login screen are verified - the user is given limited access to the application. This limited access only allows access to the OTP page(s). Once the OTP has been verified, the user is given the full set of authorisation roles to which they're entitled.
A blog outlining this approach is available here.
Create an AuthenticationSuccessHandler
If the user requires a one-time password, strip their authorities, and give them a new one, say ROLE_OTP. ROLE_OTP can only use the OTP URL and not anything else.
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication sourceAuthentication
) throws IOException, ServletException {
UserDetails sourceUser = (UserDetails) sourceAuthentication.getPrincipal();
List<GrantedAuthority> targetAuthorities = new ArrayList<GrantedAuthority>( Arrays.asList( new SimpleGrantedAuthority("ROLE_OTP") ) );
UserDetails targetUser = new User( sourceUser.getUsername() , "", targetAuthorities);
UsernamePasswordAuthenticationToken targetAuthentication = new UsernamePasswordAuthenticationToken(targetUser, null, targetAuthorities);
targetAuthentication.setDetails( sourceAuthentication.getDetails() );
SecurityContextHolder.getContext().setAuthentication(targetAuthentication);
response.sendRedirect("/otp-url");
}
If they pass the OTP, reload their real roles with loadUserByUsername()
Authentication sourceAuthentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails sourceUser = (UserDetails) sourceAuthentication.getPrincipal();
UserDetails targetUser = userDetailsManager.loadUserByUsername(sourceUser.getUsername());
UsernamePasswordAuthenticationToken targetAuthentication = new UsernamePasswordAuthenticationToken(targetUser, null, targetUser.getAuthorities());
targetAuthentication.setDetails( sourceAuthentication.getDetails() );
SecurityContextHolder.getContext().setAuthentication(targetAuthentication);

Injecting Logged in User in Spring

Hi I want my user to be logged in via URL which is secured by spring. URL will contan username as well as password. I tried doing it by sending username and password via controller to customAuthenticationManager and then checked in CustomAuthentication Provider and returned UsernamePasswordAuthenticationToken. when I check isauthenticated flag it shows true but when I try to access a secured page it redirects me to the login page. Where am I going wrong ?
Its not the best way to do it but try this:
public void login(HttpServletRequest request, String userName, String password)
{
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password);
// Authenticate the user
Authentication authentication = authenticationManager.authenticate(authRequest);
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
// Create a new session and add the security context.
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}

Howto integrate spring-social authentication with spring-security?

I've got a little webapp secured by spring-security using a username/password combo on a sql-db as credentials.
I now want to add facebook/twitter authentication with spring-social. Using the examples I am able to store the users credentials in my db. I'm now working on authenticating the user against his current session on my app using the following piece of code:
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
User user = userService.getUserById(Long.parseLong(userId));
user.setPassword(this.passwordEncoder.encodePassword(user.getAccessToken(), this.salt));
this.userService.store(user);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getDisplayName(), user.getAccessToken());
HttpServletRequest req = request.getNativeRequest(HttpServletRequest.class); // generate session if one doesn't exist
token.setDetails(new WebAuthenticationDetails(req));
Authentication authenticatedUser = this.authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
return "/user/dashboard";
}
The authentication works, I am not getting any BadCredential-exceptions. But after being redirected to /user/dashboard I am thrown back to the login.
I am out of ideas, a similar piece of code for authenticating the session is working after a classical signup.
Does anyone have any ideas why this happens or how to debug this?
Thanks very much in advance!
Hendrik
I have similar code that works for me, and also adds "remember me" support:
// lookup by id, which in my case is the login
User user = userService.findByLogin(userId);
// code to populate the user's roles
List<GrantedAuthority> authorities = ...;
// create new instance of my UserDetails implementation
UserDetailsImpl springSecurityUser = new UserDetailsImpl(user, authorities);
// create new Authentication using UserDetails instance, password, and roles
Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser, user.getPassword(), authorities);
// set the Authentication in the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
// optional: remember-me support (must #Autowire in TokenBasedRememberMeServices)
tokenBasedRememberMeServices.onLoginSuccess(
(HttpServletRequest) request.getNativeRequest(),
(HttpServletResponse) request.getNativeResponse(),
authentication);

Resources