AuthenticationProvider UsernamePasswordAuthenticationToken returns Ljava.lang.String - grails

in our grails 2.4.4 project we want to use the Spring Security Core Plugin which we declare in BuildConfig.groovy this way:
compile ':spring-security-core:2.0-RC4'
For authentication against a REST service we implement a custom AuthenticationProvider:
class CustomAuthenticationProvider implements AuthenticationProvider {
def springSecurityService
Authentication authenticate(Authentication authentication) {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String username = auth.getPrincipal()
String password = auth.getCredentials()
println "username:" + username
println "password:" + password
The CustomAuthenticationProvider.authenticate is invoked when entering a username and password in the Login form.
Unfortunately when accessing the form values via auth.getPrincipal() or auth.getCredentials() the returned value is always a String containing a value like this [Ljava.lang.String;#6ecd6d25
When debugging the application it seems that the values for the UsernamePasswordAuthenticationToken are not filled correctly from the request.
Does anybody know whats going wrong here?

Related

SpringSecurity UserDetailsService REST Client

I'm using SpringBoot 2.4.7 and I'm trying to implement jdbc Authentication. The problem is that I can't reach the backend via http. I have read that with following configuration:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
....
I can reach a default login page at my context application address. But I would like to call a POST login endpoint with username and password parameters.
How can I do this?
If you are trying to receive the user credentials via a REST Endpoint and manually authenticate the user you can do this way:
#RestController
#RequestMapping("/login")
public class LoginController {
private final AuthenticationManager authenticationManager;
// constructor injecting authenticationManager
#PostMapping
public void login(#RequestBody UserCredentials credentials) {
UsernamePasswordAuthenticationToken token
= new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
Authentication auth = this.authenticationManager.authenticate(token);
if (auth != null) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
throw new SomeException();
}
}
This way, the Filters will take care of the rest of the authentication steps for you. The Spring Security documentation can be researched for more details.
If you want to use the endpoint generated with the default login page, you can follow the steps from the documentation to make your own request:
The form should perform a post to /login
The form will need to
include a CSRF Token which is automatically included by Thymeleaf.
The form should specify the username in a parameter named username
The form should specify the password in a parameter named password
If the HTTP parameter error is found, it indicates the user failed to
provide a valid username / password
If the HTTP parameter logout is
found, it indicates the user has logged out successfully

Spring security implementation with AWS congnito authentication on front end

I separate my application into 2 parts:
Front end : Vue js and connected with AWS congnito for login feature (email/pw or google social login).
Back end : Spring boot Restful. User information stored in database (a unique id from congnito as primary key.)
My flow of authentication
User redirected to congnito and login. congnito will return a unique id and JWT.
Front end passes the unique id and JWT to back end controller.
backend validate JWT and return user information from DB
My question is:
Is this a bad practice to authenticate on front end and pass data to back end for spring security? If so, may I have any suggestion to change my implementation flow?
To call AuthenticationProvider.authenticate, a Authentication consist username (in my case, the unique id from cognito) and password is needed (UsernamePasswordAuthenticationToken). Are there any implementation to set only username? or it is fine to set password as empty string?
// controller
public String login(HttpServletRequest req, String cognitoId, String jwt) {
// check JWT with AWS
if(!AwsJwtChecker(cognitoId, jwt))
return createErrorResponseJson("invalid jwt");
UsernamePasswordAuthenticationToken authReq
= new UsernamePasswordAuthenticationToken(cognitoId, "");
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);
MyUser user = userRepository.selectUserByCognitoId(cognitoId);
return createLoginSuccessResponse(user);
}
// web config
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String cognitoId = authentication.getName();
// check user exist in db or not
MyUser user = userRepository.selectUserByCognitoId(cognitoId);
if (user != null) {
return new UsernamePasswordAuthenticationToken(username, "", user.getRoles());
} else {
throw new BadCredentialsException("Authentication failed");
}
}
#Override
public boolean supports(Class<?>aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
Is this a bad practice to authenticate on front end and pass data to back end for spring security? If so, may I have any suggestion to change my implementation flow?
No, in fact it's best practice. JWT is exactly for that purpose: You can store information about the user and because of the signature of the token, you can be certain, that the information is trustworthy.
You don't describe what you are saving in the database, but from my perspective, you are mixing two authentication methods. While it's not forbidden, it might be unnecessary. Have you analysed your token with jwt.io? There are many information about the user within the token and more can be added.
Cognito is limited in some ways, like number of groups, but for a basic application it might be enough. It has a great API to manage users from within your application, like adding groups or settings properties.
You don't describe what you do with the information that is returned with 3). Vue can too use the information stored in the jwt to display a username or something like that. You can decode the token with the jwt-decode library, eg, and get an object with all information.
To call AuthenticationProvider.authenticate...
Having said that, my answer to your second question is: You don't need the whole authentication part in you login method.
// controller
public String login(HttpServletRequest req, String cognitoId, String jwt) {
// check JWT with AWS
if(!AwsJwtChecker(cognitoId, jwt))
return createErrorResponseJson("invalid jwt");
return userRepository.selectUserByCognitoId(cognitoId);
}
This should be completely enough, since you already validate the token. No need to authenticate the user again. When spring security is set up correctly, the jwt will be set in the SecurityContext automatically.
The problem I see with your implementation is that anyone could send a valid jwt and a random cognitoId and receive user information from the database. So it would be better to parse the jwt and use something from within the jwt, like username, as identifier in the database. The token can't be manipulated, otherwise the validation fails.
public String login(String jwt) {
// check JWT with AWS
if(!AwsJwtChecker(jwt))
return createErrorResponseJson("invalid jwt");
String identifier = getIdentifier(jwt);
return userRepository.selectUserByIdentifier(identifier);
}

Primefaces PostConstruct and SessionHolder

I have a problem in integrating Spring Security with JSF
I'm trying to access to the Authentication Object in a #PostConstruct method and retrieve the user login
and after I want to retrieve the user's object from database the problem is that I can't get the Authentication object because it's not constructed before login
#PostConstruct
public void init(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//here I have the exception because auth is null
String name = auth.getName();
lecteur =(Lecteur)userService.getUserByLogin(name);
}
How to solve this problem ?

Spring Security: Using BCryptPasswordEncoder inside custom authentication provide

I have hashed my password like this:
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);
Now how will i authenticate login username and password using custom spring authentication
public Authentication authenticate(Authentication authentication) throws AuthenticationException{
}
Inside this how can i use BCryptPasswordEncoder to match if login password is correct or not
The core of your authenticate method will probably look something like:
String rawPassword = (String)authentication.getCredentials();
if (!passwordEncoder.matches(rawPassword, hashedPassword)) {
throw new BadCredentialsException("Wrong password");
}

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).

Resources