How to make register and login work correctly in spring security, problems with authentication - spring

I'm trying to make authorization in my project. Registration works great and I get access and refresh tokens, but login throws an exception.
Problem is that in registration I create UsernamePasswordAuthenticationToken with UserEntity and password:
#PostMapping("/register")
public ResponseEntity register(#RequestBody UserSignUpModel userSignUpModel) {
UserEntity user = customUserDetailsService.createUser(userSignUpModel);
Authentication authentication = UsernamePasswordAuthenticationToken.authenticated(user, user.getPassword(), Collections.EMPTY_LIST);
return ResponseEntity.ok(tokenGenerator.createToken(authentication));
}
And in login I create UsernamePasswordAuthenticationToken with email and password so that the daoAuthenticationProvider can process it:
#PostMapping("/login")
public ResponseEntity login(#RequestBody UserSignInModel userSignInModel) {
Authentication authentication = daoAuthenticationProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(userSignInModel.getEmail(), userSignInModel.getPassword()));
return ResponseEntity.ok(tokenGenerator.createToken(authentication));
}
So I receive different authentication objects and can't pass the instanceoff check in TokenGenerator:
public TokenModel createToken(Authentication authentication) {
if (!(authentication.getPrincipal() instanceof UserEntity user)) {
throw new BadCredentialsException(
MessageFormat.format("principal {0} is not of UserEntity or UserDetails type", authentication.getPrincipal().getClass())
);
}
TokenModel tokenModel = new TokenModel();
tokenModel.setUserId(user.getUser_id());
tokenModel.setAccessToken(createAccessToken(authentication));
String refreshToken;
if (authentication.getCredentials() instanceof Jwt jwt) {
Instant now = Instant.now();
Instant expiresAt = jwt.getExpiresAt();
Duration duration = Duration.between(now, expiresAt);
long secondsUntilExpired = duration.toSeconds();
if (secondsUntilExpired < 15) {
refreshToken = createRefreshToken(authentication);
} else {
refreshToken = jwt.getTokenValue();
}
} else {
refreshToken = createRefreshToken(authentication);
}
tokenModel.setRefreshToken(refreshToken);
return tokenModel;
}
Should I write custom authentication provider? If so, do I need to download an entity from the repository and check passwords in it?

Related

How to test a reset password request with spring boot and postman?

I'm currently working on a reset password for users and I had a problem testing the method. This is the code below:
#PostMapping("/user/resetPassword")
public GenericResponse resetPassword(HttpServletRequest request, #RequestBody String userEmail) {
System.out.println("Here we are in the method of the reset *********");
User user = userRepository.findByEmail1(userEmail);
System.out.println ( user.getId());
System.out.println("This is my user"+user);
if (user == null) {
throw new UserNotFoundException();
}
String token = UUID.randomUUID().toString();
userService.createPasswordResetTokenForUser(user, token);
mailSender.send(constructResetTokenEmail(getAppUrl(request), request.getLocale(), token, user));
return new GenericResponse(messages.getMessage("message.resetPasswordEmail", null, request.getLocale()));
}
The problem is in the findByEmail it is not returning the user from the email that I am inserting in the JSON in the Postman payload:
User findByEmail(String email);
the result of the execution is UserNotFoundException.

How to return a JWTtoken when users registers for the first time in springboot

I pretty new to Springboot with jwt, I need to generate a jwt token when users sign up for the first time and use the token to allow redirect to another page instead of returning user registered successfully message. How can I achieve this? I am able to generate a token when users signin and use the token for authentication. any help will be appreciated.
how am generating on signin method
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(item -> item.getAuthority())
.collect(Collectors.toList());
return ResponseEntity.ok(new JwtResponse(jwt,
userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail(),
roles));
}
my signup method.
#PostMapping("/signup")
public ResponseEntity<?> registerUser(#Valid #RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest.getUsername())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Error: Username is already taken!"));
}
if (userRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity
.badRequest()
.body(new MessageResponse("Error: Email is already in use!"));
}
// Create new user's account
User user = new User(signUpRequest.getUsername(), signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()),signUpRequest.getFirstname(),
signUpRequest.getSurname(),signUpRequest.getTelephoneno(),
signUpRequest.getWhatsappno());
Set<String> strRoles = signUpRequest.getRole();
Set<Role> roles = new HashSet<>();
if (strRoles == null) {
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
} else {
strRoles.forEach(role -> {
switch (role) {
case "admin":
Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(adminRole);
break;
case "mod":
Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(modRole);
break;
default:
Role userRole = roleRepository.findByName(ERole.ROLE_USER)
.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
roles.add(userRole);
}
});
}
user.setRoles(roles);
userRepository.save(user);
return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
//return ResponseEntity.ok(jwt);
}
how can I generate a token when users sign instead of return ResponseEntity.ok(new MessageResponse("User registered successfully!"));

How to modify spring SecurityContextHolder.getContext().getAuthentication() object after successful login?

I'm working on spring boot application where i've created a CustomUserDetails class by extending UserDetails as follows..
public class CustomUserDetails
extends org.springframework.security.core.userdetails.User {
private static final long serialVersionUID = 1L;
/**
* The extra field in the login form is for the tenant name
*/
private String tenant;
private Long userId;
private String firstName;
private String middleName;
private String lastName;
private String email;
private String role;
i need to modify tenant details in UserDetails object. For this i've checked following
How to update Spring Security UserDetails impls after successful login?
https://stackanswers.net/questions/how-to-immediately-enable-the-authority-after-update-user-authority-in-spring-security
https://dev.to/onlineinterview/user-account-loginregistration-feature-with-spring-boot--3fc3
And Controller is here where i'm updating authentication object:
#PreAuthorize("hasRole('SUPER_ADMIN')")
#GetMapping(path = "/useTenant/{tenantId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<ResponseDTO> useTenant(#PathVariable Long tenantId) {
HttpStatus status = HttpStatus.OK;
boolean error = false;
String message = languageMessageService.getMessage(MultiLanguageKey.SUCCESS);
// fetch master tenant by id
Optional<MasterTenant> optional = masterTenantService.findById(tenantId);
if (optional.isPresent()) {
CustomUserDetails customUserDetails = customUserDetailsService.getUserDetail();
//Changing Tenant ID
customUserDetails.setTenant(optional.get().getTenantId());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof UsernamePasswordAuthenticationToken) {
// Update Current user by changing tenant id in SecurityContextHolder
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
auth.setDetails(customUserDetails);
SecurityContextHolder.getContext().setAuthentication(auth);
}
} else {
error = false;
message = languageMessageService.getMessage(MultiLanguageKey.TENANT_NOT_FOUND);
}
return new ResponseEntity<>(new ResponseDTO(error, message), status);
}
My problem is that when i'm hitting another request to perform particular action, i didn't find tenant detail in CustomUserDetails object which is fetched from
SecurityContextHolder.getContext().getAuthentication()
Please let me know how can i update or modify UserDetails object of Authentication and save back so another request get updated CustomUserDetails.
The UserDetails should be set to the Principal of the UsernamePasswordAuthenticationToken rather than Details as suggested by the java docs :
The AuthenticationManager implementation will often return an
Authentication containing richer information as the principal for use
by the application. Many of the authentication providers will create a
UserDetails object as the principal.
Details in UsernamePasswordAuthenticationToken is normally stored user 's IP address or certificate serial number etc.
So change it to :
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof UsernamePasswordAuthenticationToken) {
// Update Current user by changing tenant id in SecurityContextHolder
UsernamePasswordAuthenticationToken currentAuth = (UsernamePasswordAuthenticationToken) authentication;
CustomUserDetails userDetail = currentAuth.getPrincipal();
customUserDetails.updateTenanet("blablalb");
UsernamePasswordAuthenticationToken updateAuth = new UsernamePasswordAuthenticationToken(userDetail ,
currentAuth.getCredentials(),
currentAuth.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(updateAuth);
}

Spring WEB MVC + produces = MediaType.IMAGE_JPEG_VALUE + #ResponseStatus(HttpStatus.FORBIDDEN) = HTTP status 406

I'm writing some code for user authorization. For users with 2 factored authorization enabled I'm writing code for 2fa secret update:
#RestController
public class CurrentUserController {
#PostMapping(value = "update-2fa-secret", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] update2FaSecret() {
UserEntity user = userRepository.findOne(currentUserId);
if (user.is2FaEnabled() != Boolean.TRUE)
throw new HttpForbiddenException("2fa disabled for current user");
String secret = createNewSecret();
user.setSecret2Fa(secret);
userRepository.save(user);
return createQRCode(secret, user.getEmail());
}
}
And Exception:
#ResponseStatus(HttpStatus.FORBIDDEN)
public class HttpForbiddenException extends RuntimeException {
............
}
And when Exception happens I get response from the server with 406 Http status and without body (content).
I don't understand why this happens and how to solve it. Can somebody explain it to me please?
I've solved this issue in the next way:
#RestController
public class CurrentUserController {
#PostMapping(value = "update-2fa-secret", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] update2FaSecret(HttpServletResponse response) {
UserEntity user = userRepository.findOne(currentUserId);
if (user.is2FaEnabled() != Boolean.TRUE) { //fix is here
response.setStatus(HttpStatus.FORBIDDEN.value()); //403
return new byte[0];
}
String secret = createNewSecret();
user.setSecret2Fa(secret);
userRepository.save(user);
return createQRCode(secret, user.getEmail());
}
}

How to use InetOrgPersonContextMapper class

I'm authenticated and authorise to Active Directory by Spring Security.
But can not retrive LDAP attributes, for example MAIL.
I trying use InetOrgPersonContextMapper for it...
#Bean
public InetOrgPersonContextMapper inetOrgPersonContextMapper(){
InetOrgPersonContextMapper contextMapper = new InetOrgPersonContextMapper();
return contextMapper;
}
#Bean
public LdapAuthenticationProvider ldapAuthenticationProvider(){
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator(),ldapAuthoritiesPopulator());
ldapAuthenticationProvider.setUserDetailsContextMapper(inetOrgPersonContextMapper());
return ldapAuthenticationProvider;
}
but when i trying retrive attributes in controller to i get ClassCastExeption
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
InetOrgPerson person = (InetOrgPerson)auth.getPrincipal();
Please tell me correct way for reitrive attributes.
I guess it's no better way, but it's working.
If anybody know how can do it better, please tell me.
#Bean
public UserDetailsContextMapper userDetailsContextMapper(){
return new LdapUserDetailsMapper(){
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
InetOrgPersonContextMapper personContextMapper = new InetOrgPersonContextMapper();
UserDetails cm = personContextMapper.mapUserFromContext(ctx,username,authorities);
String MAIL = ((InetOrgPerson)(personContextMapper.mapUserFromContext(ctx,username,authorities))).getMail();
String FullName = ((InetOrgPerson)(personContextMapper.mapUserFromContext(ctx,username,authorities))).getDisplayName();
System.out.println("MAIL: " + MAIL + " Full Name: " + FullName);
return cm;
}
};
}

Resources