I am now exploring that authentication of users in microservice. For that I am created my authentication service - checkUserAuthentication. Also providing Microservice also. this is already deployed in cloud.
Now I am creating new service with specific business logic. In this service , need to authenticate and check authorization of user to access this end-point by using authenticationProvider from spring security.
For this I am reading and exploring the following tutorials,
https://dzone.com/articles/spring-security-custom
http://roshanonjava.blogspot.in/2017/04/spring-security-custom-authentication.html
http://javasampleapproach.com/spring-framework/spring-security/spring-security-customize-authentication-provider
http://www.baeldung.com/spring-security-authentication-provider
In here they are implements AuthenticationProvider in class CustomAuthenticationProvider.
and in method they are receiving username and password is like following,
public Authentication authenticate(Authentication authentication) throws
AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
Optional<User> optionalUser = users.stream().filter(u -> u.index(name,
password)).findFirst();
if (!optionalUser.isPresent()) {
logger.error("Authentication failed for user = " + name);
throw new BadCredentialsException("Authentication failed for user = " + name);
}
// find out the exited users
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(optionalUser.get().role));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(name, password,
grantedAuthorities);
logger.info("Succesful Authentication with user = " + name);
return auth;
}
These are codes from documentation. Instead of this method, I need to do in different way. Here I am adding my requirements:
My requirement: I need to receive username and password from API Request.And For checking this username and password, I need to call my deployed APIs checkUserAuthentication and checkUserAuthorization.
My doubts on this:
Can I directly call these API within "public Authentication authenticate(Authentication authentication)" method ?
How I receive username and password from the received request ?
Why we are using UsernamePasswordAuthenticationToken ? , If we are sending JWT token instead of username and password, then which class will use for providing reply?
Since I only started with Spring Security, I am new to security world.
Can I directly call these API within "public Authentication authenticate(Authentication authentication)" method ?
Yes.
How I receive username and password from the received request ?
Same as they are doing in authenticate method.
Why we are using UsernamePasswordAuthenticationToken ? , If we are sending JWT token instead of username and passowrd, then which class
will use for providing reply?
UsernamePasswordAuthenticationToken is used internally by spring security. This
comes into the picture when you create a session in spring. it contains the user information (eg. email etc.) and authorities (role).For example, when you receive a JWT token in your application, you will validate the JWT token (signature etc. ) and upon successfull validation of JWT, you will create an object of UsernamePasswordAuthenticationToken and spring will save it in session. For each incoming request, spring will call boolean isAuthenticated() method on this object to find if user can access the required resource.
Now when you have got all your answers, my recommendation is to go with Oauth2 for your boot microservices. there are plenty of example how to implement it and customize it for your requirement. (Basically, you have to implement your Authorization server which will authenticate the user with your service checkUserAuthentication and generate the accesstoken. Each consumer of your microservice needs to send this accesstoken which they have got from Authorization server and you need to validate it in your microservice. So your microservice will act as Resource Server).
Hope it will help.
Related
The issue I have is after the user is authenticated meaning user has signed in, I understand from the client side to logout a user, I delete the token from the local storage but the issue I have is how do I invalidate the token or logout from the serverside.
My intial approach was to make the logout API permit all in my SecurityFilterChain but when I try to grab the authenticated user from SecurityContextHolder after the user had signed in I was getting anonymousUser.
My second/current approach is I instead authorized LOGOUT API which means to access the API, a token has to passed in the header. Then I can then set SecurityContextHolder.getContext().setAuthentication(authentication == false); and also clearContext(). With this approach I am able to get the logged in user but my questions are:
Is this the right logic to implement a log out?
I understand a token cannot be invalidated because it is STATELESS. But is there a way to get around this? Because even after setting Authentication to false in SecurityContextHolder
and clearing security context SecurityContextHolder.clearContext(); when I try accessing Authenticated API i.e CRUD operations, I am still able to use the token.
Here is my login and logout methods in my RestController Class
logout
#PostMapping(path = "/logout", headers = "Authorization")
#ResponseStatus(OK)
public ResponseEntity<?> logout() {
LOGGER.info("Trying to Logout ");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
LOGGER.info("Username {} ", username);
authentication.setAuthenticated(false);
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder.clearContext();
return ResponseEntity.ok().body("Successfully logged out");
}
login
#PostMapping(path = "/login", consumes = "application/json", produces = "application/json")
#ResponseStatus(OK)
public ResponseEntity<?> login(#Valid #RequestBody UserDTO userDTO) {
Authentication authentication;
LOGGER.info("Authenticating {}", userDTO.getUsername());
var authenticationToken = confirmUser(userDTO); // returns a UsernamePasswordAuthenticationToken
try {
authentication = authenticationManager.authenticate(authenticationToken); // Authenticate user password token
SecurityContextHolder.getContext().setAuthentication(authentication); // Set the security context to the logged user
} catch (AuthenticationException e) {
LOGGER.error("Stack trace {}", e.getMessage());
SecurityContextHolder.getContext().setAuthentication(null);
throw new InvalidPasswordException("Wrong username or password");
}
LOGGER.info("{} has signed in", userDTO.getUsername());
return ResponseEntity.ok()
.header( AUTHORIZATION, tokenService.generateToken(authentication) )
.build();
}
I might recommend a different approach, but let's start with your question.
Expiring Access Tokens
To expire a resource server token, you will need to add some kind of state.
This usually comes in the form of some kind of list of valid tokens. If the token isn't in the list, then the token is not valid.
A common way to achieve this is to rely on the authorization server. Many authorization servers ship with an endpoint that you can hit to see if a token is still valid.
Modeling Things Differently
That said, it might be worth considering if you should be thinking about the access token differently. The access token does not represent a user's authenticated session. It represents the user granting access to the client to operate on the user's behalf.
So after the user logs out, it still makes quite a bit of sense for the client to have a valid access token so that the user doesn't have to reauthorize the client every time they log in.
I'm trying to create a restful web application with Springboot and Gradle. I use JWT for authentication. When a user logged the backend creates a user token for the user. this token he uses whenever he opens another page to retrieve his data.
What I'm trying to do is making a signup page, but the problem is that I cant send information to my backend without a Bearer token. How do I send a post method to create a user without authentication on this one single POST operation?
To generate a token for a user that exists
#PostMapping(value = "${jwt.get.token.uri}")
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtTokenRequest authenticationRequest)
throws AuthenticationException {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtTokenResponse(token));
}
What I want to call to create a user in my database:
(want to do this POST without authorization)
#PostMapping(value = "/signup")
public ResponseEntity<User> createUser(#RequestBody User user){
HBMUserService HBMuserService = new HBMUserService();
User createdUser = HBMuserService.saveUser(user);
return new ResponseEntity<>(createdUser, HttpStatus.OK);
}
My application properties:
jwt.signing.key.secret=SecretKey
jwt.get.token.uri=/authenticate
jwt.refresh.token.uri=/refresh
jwt.http.request.header=Authorization
jwt.token.expiration.in.seconds=604800
I Found out where my JWT Ignores my Auth path for login and added my signup path there as well.
.antMatchers(
HttpMethod.POST,
authenticationPath,
"/signup"
)
I have a setup of spring boot OAuth for AuthServer and it is resposible for serving a number of few resource server for authentication using spring-security-jwt.
My problem is while authenticating I need to load the roles of a user but specific to the clientId.
eg: If user1 have roles ROLE_A, ROLE_B for client1 and ROLE_C, ROLE_D for client2, then when the user logins either using client1 or client2 he is able to see all the four roles ie. ROLE_A, ROLE_B, ROLE_C, ROLE_D because I am getting roles based on username.
If I need to have a role based on the client then I need clientId.
FYI,
I am using the authorization code flow for authentication.
I have seen similar question but that is based on password grant but I am trying on authorization code flow and that solution doesn't work for me.
Password grant question link
Below is my code where I need clientId
MyAuthenticationProvider.java
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
String userName = ((String) authentication.getPrincipal()).toLowerCase();
String password = (String) authentication.getCredentials();
String clientId = ? // how to get it
....
}
}
MyUserDetailsService.java
#Override
public UserDetails loadUserByUsername(String username) {
String clientId = ? // how to get it
....
}
}
You probably need to see OAuth2Authentication in Spring-security. When your client is authenticated by oauth2, then your "authentication" is actually instance of OAuth2Authentication that eventually implements Authentication.
If you see the implementation of OAuth2Authentication, it's done as below;
public Object getPrincipal() {
return this.userAuthentication == null ? this.storedRequest.getClientId() : this.userAuthentication
.getPrincipal();
}
so if request included "clientId', then you should be able to get clientId by calling getPrincipal() and typecasting to String as long as your request didn't include user authentication.
For your 2nd case, username is actually considered as clientId. You need to call in-memory, RDBMS, or whatever implementation that has clientId stored and returns ClientDetails. You'll be able to have some idea by looking into Spring security's ClientDetailsUserDetailsService class.
Since I didn't get any appropriate solution for my question, I am posting the solution that I used after digging source code and research.
MyJwtAccessTokenConverter.java (Extend JwtAccessTokenConverter and implement enhance method)
public class OAuthServerJwtAccessTokenConverter extends JwtAccessTokenConverter {
....
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String clientId = authentication.getOAuth2Request().getClientId();
// principal can be string or UserDetails based on whether we are generating access token or refreshing access token
Object principal = authentication.getUserAuthentication().getPrincipal();
....
}
....
}
Info:
In enhance method, we will get clientId from authentication.getOAuth2Request() and userDetails/user_name from authentication.getUserAuthentication().
Along with JwtAccessTokenConverter, AuthenticationProvider and UserDetailsService are required for authentication in generating access token step and refresh token step respectively.
get authorization header from request then parse from base64 to get the client-id.
something like this:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes())
.getRequest();
String authHeader = request
.getHeader("Authorization");
I have few Spring-boot controller classes to expose few rest web-services. Whenever some user tries to access any of those services, I need to invoke an web-service to check whether the user (user id will be passed as RequestHeader) is authorized or not. If not authorised, need to display an error page (freemarker template) to the user.
I don't want to write a method which will invoke the authentication webservice and call that from each controller methods and throw an exception and redirect the user to the access denied error page using #ControllerAdvice as here I have to call the method from all controller methods.
I'm not sure whether I can use WebSecurityConfigurerAdapter/AuthenticationManagerBuilder to call the webservice and do the validation.
I'm looking for some solution where I would write an interceptor and spring-boot will invoke the webservice before calling the controller classes and will be able to redirect to the error page, if validation fails.
As a recommendation, take a few minutes for reading about Spring Security (https://projects.spring.io/spring-security/), you must configure it and probably you will spend more time than expected, anyway you have so much more profits than make security by ourself.
Benefits are things like:
#PreAuthorize("hasRole('ROLE_USER')")
On every place you can get the user logged through the SecurityContext with something like:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
The way SpringSecurity authenticate users is with JWT (JsonWebToken) this is a really nice way because you can pass and retrieve all information you want:
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("customInfo", "some_stuff_here");
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
And you can forget every possible problem (bad authentication, phishing, xss or csrf..) because it works with public/private key and secrets, so anyone can create a token.
I have 2 questions about spring jwt token?
The first one is related to the additional informations of the JWT token:
- Is there any way to hide the additional informations from the oauth2 jwt token because they are in plain text and the same informations are duplicated in the JWT access token or payload
public class CustomTokenEnhancer extends JwtAccessTokenConverter {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
User user = (User) authentication.getPrincipal();
additionalInfo.put("organization", user.getOwnerId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
}
The second one concerns the mapping of my user permissions to access token scopes, in fact, when i add the scopes as additional informations, which represent for my case the different permissions for a given user, and when I want to test this in my WS by #PreAuthorize("hasRole('ROLE_USER') and #oauth2.hasScope('XXXXX')") annotation. It does not work because the checking is based on client scopes rather than user access token scopes? Is there a way, for using access token scopes (which represents my permissions user) rather than client scopes by using the #oauth2.hasScope('XXXXX') annotation? how can i do that?
thanks.