How do I make Spring Security return a 500 instead of a 403 when a InternalAuthenticationServiceException is thrown - spring-boot

I am using Spring Security to handle auth on my RESTful-ish webservice.
The goal is to create a /login endpoint for which the user provides a username/password and which returns a JWT. I'm loosely following this guide: https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/
I've got the happy-path working; when a user provides a valid user/pass, a valid JWT is returned to them.
The problem is my error case. If my UserService.loadUserByUsername method (which is called by Spring Security in order to validate the user/pass) throws an IOException, I want Spring to return a 500 error. Instead, Spring returns a 403. I stepped through the internal Spring classes a bit, and they clearly differentiate between a AuthenticationException (which is thrown when auth fails) and a InternalAuthenticationServiceException (which is thrown when auth is unable to be completed due to an internal error). I want a 500 error returned when an InternalAuthenticationServiceException is encountered. How to I configure Spring to do this? Will I need to implement my own AuthenticationFailureHandler?

As Thomas Andolf said, it might have been best for me to simply implement an OAuth2 flow instead of a custom authentication solution. However, if you want to implement a custom authentication solution, and if you want to return specific HTTP error codes for specific errors, then you can write a custom AuthenticationFailureHandler to accomplish this. I'm not sure if this is a good solution, but it is a solution. Something like this:
class CustomAuthenticationFilter(authManager: AuthenticationManager) : AbstractAuthenticationProcessingFilter(AntPathRequestMatcher("/login", "POST")) {
init{
this.authenticationManager = authManager;
this.setAuthenticationSuccessHandler(CustomAuthenticationSuccessHandler())
this.setAuthenticationFailureHandler(CustomAuthenticationFailureHandler())
}
#Throws(AuthenticationException::class, PreAuthenticatedCredentialsNotFoundException::class)
override fun attemptAuthentication(req: HttpServletRequest, res: HttpServletResponse): Authentication {
// insert code to parse the request into a username and password
return authenticationManager.authenticate(
UsernamePasswordAuthenticationToken(
username,
password,
ArrayList())
)
}
}
class CustomAuthenticationSuccessHandler: AuthenticationSuccessHandler{
override fun onAuthenticationSuccess(request: HttpServletRequest?, response: HttpServletResponse?, authentication: Authentication?) {
// these next three lines simply verify that none of the inputs are null; this is Kotlin syntax.
request!!
response!!
authentication!!
val username = (authentication.principal as User).getUsername();
val expiration = Date(System.currentTimeMillis() + EXPIRATION_DURATION_MILLIS)
// insert code to create a JWT and write it to the response
// no need to return anything
}
}
class CustomAuthenticationFailureHandler : AuthenticationFailureHandler{
override fun onAuthenticationFailure(request: HttpServletRequest?, response: HttpServletResponse?, exception: AuthenticationException?) {
// these next two lines simply verify that none of the inputs are null; this is Kotlin syntax.
request!!
response!!
when (exception) {
is PreAuthenticatedCredentialsNotFoundException -> {
response.status = 400;
}
is AuthenticationServiceException -> {
response.status = 500;
}
else -> {
response.status = 401;
// consider adding a WWW-Authenticate header as well
}
}
}
}

Related

Adding basic auth headers to all the requests in spring boot

I am new to Spring boot application development.
I need to add the basic auth headers to all the api requests in spring boot.
Can any one share the valid documentation of how I proceed
It depends on what kind of auth u require
for something like self auth token it would look something like
public String controllerFunction(#RequestHeader("Auth-header") String authToken){
if (authToken == null) {
log.error("Self token authentication failed");
throw new Exception(TOKEN_NOT_FOUND);
}
if (!"auth_password".equals(authToken)) {
log.error("Self token authentication failed");
throw new Exception(AUTH_FAILED);
}
log.info("Self token authentication successful");
}
If it's unique to individual users u will have to fetch the "auth_password" from your database for that particular user and validate it
To use it in globally you can build annotations like this
#Before("#annotation(tokenValidation)")
public void beforeAdvice(TokenValidation tokenValidation) {
String authToken = request.getHeader("Auth-header");
if (authToken == null) {
log.error("Self token authentication failed");
throw new Exception(TOKEN_NOT_FOUND);
}
if (!"auth_password".equals(authToken)) {
log.error("Self token authentication failed");
throw new Exception(AUTH_FAILED);
}
log.info("Self token authentication successful");
}
U might have to look up how to implement the annotations in spring boot but this is a basic concept.
and in the controllers, u just have to do
#tokenValidation
public String controllerFunction(String authToken){
//your code;
}

Spring Security:Why check if authentication token is set in authorization filter?

I'm trying to understand spring security. I came across following piece of code
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
// Reads the JWT from the Authorization header, and then uses JWT to validate the token
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
// new arraylist means authorities
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
What is the need of
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
Authorization will be done after authentication right ? In that case header will be set anyway.What am I missing?
And also what is chain.doFilter() doing exactly? It is used to proceed and hit the servlet eventually right? If user isn't authenticated why proceed with request?If it is to proceed to authentication filter then how come authorization filter invoked before authentication filter?
In spring security a filter chain is basically a linked list. So as you do a request, it will hit the first filter, that filter will then call the next chain in the link, then the next, the next, etc.
// Get a specific header (im guessing authorization header)
String header = req.getHeader(HEADER_STRING);
// If no such header was found, or the found header does not include a specific prefix
// (im guessing the string Baerer) which means that if this is not a
// authentication header containing a baerer token, then call the next filter in
// the chain.. The next filter will have the same call, etc.
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
If a request is sent with no auth header and we proceed into the application, down the filter chain as we hit the actually endpoint, it will probably have an anotation saying that you need a specific role, which the user wont have, since it hasnt authenticated itself. And the result will be a 401 UNAUTHORIZED returned from the security framework.
If it on the other hand it finds an auth header with a bearer prefix it will try to extract out the token from the header, verify its integrity that no one has tampered with it, decode it, and get the subject field from the token. Using the subject it will create a UsernamePasswordAuthenticationToken and set this as the authenticated token in the security context. Basically setting the subject as the authenticated user.
If there is no subject in the token, filter will return null (which is sloppy coding and might crash the application, a 401 exception should be thrown).
Several bad things with this code:
String header = req.getHeader(HEADER_STRING); is called twice
default user case is to try to log in the user, should be the other way around, default case should be to throw a 401 unauthorised and all logic should try to prevent that.
if a authorization header containing the string bearer but no token is sent the parsing function will return a null value, and the filter will call setAuthentication with the value null which will probably result in a crash.
if a authorization header containing the string bearer the token does not contain a subject value the parsing function will return a null value, and the filter will call setAuthentication with the value null which will probably result in a crash.
Spring already includes support for Nimbus jwt library to encode and decode JWT's there is no need to pull in another library java-jwt as done in the example above.
Worth mentioning that from Spring 5 there is already a fully implemented JWT solution in spring security that only needs to be customised. So this entire custom filter is redundant.

Using a request header value in #PreAuthorize

Is it possible to use a request header value in #PreAuthorize?
In my app, all requests have a custom header included which I need to use in conjunction with the user role to determine whether or not they should be allowed to access the controller.
It's ok if someone manually specifies a header as that won't be a security issue, as ultimately the role will control this. But I will need to use it to cut down on checking for that manually in each controller method.
Thank you,
Matt
1 - This may be the fastest method if you will only use it in a few places.
#GetMapping(value = "/private-api-method")
#PreAuthorize("#request.getHeader('header-name') == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
return ResponseEntity.ok("OK!");
}
OR
#GetMapping(value = "/private-api-method")
#PreAuthorize("#header == 'localhost:8080'")
public ResponseEntity<String> privateApiMethod(#RequestHeader("header-name") String header) {
return ResponseEntity.ok("OK!");
}
2 - This may be the best method if you will use it in many places. (In the SecurityServise, you can add multiple different methods of checking.)
#GetMapping(value = "/private-api-method")
#PreAuthorize("#securityService.checkHeader(#request)")
public ResponseEntity<String> privateApiMethod(HttpServletRequest request) {
return ResponseEntity.ok("OK!");
}
3 - You can choose a special method for yourself
A Custom Security Expression with Spring Security
Since you intend to check for a particular header/cookie/request-attribute for every controller methods, you should opt for a Filter as this would be a standard and you can have a guarantee for it be executed for each and every method and that too only once by extending from OncePerRequestFilter
Having said that, there would be 2 way you can achieve this:
By extending AbstractAuthenticationProcessingFilter or OncePerRequestFilter
For this you may refer the spring-security jwt token validation flow which all would advocate for:
Add method security at your desired controller method as #PreAuthorize("hasAuthority('USER_ROLE')")
Intercept the request before UsernamePasswordAuthenticationFilter, extract the Authentication header or cookies from the request and validate the token value for claims.
public class CustomHeaderAuthFilter extends AbstractAuthenticationProcessingFilter{
#Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response){
// Get all the headers from request, throw exception if your header not found
Enumeration<String> reqHeaders = request.getHeaderNames();
Assert.notNull(reqHeaders, "No headers found. Abort operation!");
Collections.list(reqHeaders)
.stream()
.filter(header_ -> header_.equals("TARGET_HEADER_NAME"))
.findAny().ifPresent(header_ -> {
// header found, would go for success-andler
});
// Here it means request has no target header
SecurityContextHolder.clearContext();
failureHandler.onAuthenticationFailure(request, response, new CustomException(""));
}
}
Going by this way, you need to register your filter with WebSecurityConfigurerAdapter and you may also provide your AuthenticationProvider if you extend from AbstractAuthenticationProcessingFilter.
By accessing HTTP Headers in rest controllers using #RequestHeader as dm-tr has mentioned.
Maybe you can try this
#PreAuthorize("hasAuthority('ROLE_SOMETHING')")
#RequestMapping("PATH")
public void checkIt(#RequestHeader("header-name") String header) {
if (null != header /* && header meets certain condition*/) {
// stuff
} else throw new ResponseStatusException(HttpStatus.FORBIDDEN); // PERMISSION NOT GRANTED, 403 ERROR
}

Get current logged in user from Spring when SessionCreationPolicy.STATELESS is used

I want to implement this example using Keyclock server with Spring Security 5.
I'm going to use OAuth2.0 authentication with JWT token. I'm interested how I can get the current logged in user into the Rest Endpoint?
I have configured Spring Security not to store user sessions using http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);.
One possible way is to use this code:
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
But I don't know is it going to work. Can someone give some advice for that case?
SecurityContextHolder, SecurityContext and Authentication Objects
By default, the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal’s request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.
SessionManagementConfigurer consist of isStateless() method which return true for stateless policy. Based on that http set the shared object with NullSecurityContextRepository and for request cache NullRequestCache. Hence no value will be available within HttpSessionSecurityContextRepository. So there might not be issue with invalid/wrong details for user with static method
Code:
if (stateless) {
http.setSharedObject(SecurityContextRepository.class,
new NullSecurityContextRepository());
}
if (stateless) {
http.setSharedObject(RequestCache.class, new NullRequestCache());
}
Code:
Method to get user details
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication()));
}
private static String extractPrincipal(Authentication authentication) {
if (authentication == null) {
return null;
} else if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
}
public static Optional<Authentication> getAuthenticatedCurrentUser() {
log.debug("Request to get authentication for current user");
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication());
}
sessionManagement
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
You might like to explore Methods with Spring Security to get current user details with SessionCreationPolicy.STATELESS
After the service validate the token, you can parse it, and put it into the securitycontext, it can contains various data, so you have to look after it what you need. For example, subject contains username etc...
SecurityContextHolder.getContext().setAuthentication(userAuthenticationObject);
The SecurityContextHolder's context maintain a ThreadLocal entry, so you can access it on the same thread as you write it in the question.
Note that if you use reactive (webflux) methodology, then you have to put it into the reactive context instead.

How to access the request body in SpringBoot's AccessDecisionVoter?

So we have a Authorisation server with which we create OAuth2 access token. All sub-systems verify the access token and may check the request path for permissions, however, in one of the sub-systems we need to look into the request body and extract the 'id' to check if the user has proper permission to submit the request. The request message is in JSON format and this is a POST request with client-provided id.
The id in the request is a process id and some users may not have right permission to some processes therefore we need the id to verify.
So while in AccessDecisionVoter, we only can get request URI but I can't get HttpServletRequest to read the message. (Note: We have a Request wrapper that allows us to read request body multiple times)
I tried to auto-wire HttpServletRequest, no luck. There is an error that no thread has been bound to the request
I was also thinking about implementing UserDetailService but again no luck as this is not being invoked by Spring boot. Remember that we are using a custom AuthorizationServerTokenServices and that is in a common library.
How do I get Http servlet request or the request body in AccessDecisionVoter?
You should be able to implement an AccessDecisionVoter<FilterInvocation> where you can get the request. Does this not work:
public class MyAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> {
#Override
public boolean supports(ConfigAttribute attribute) {
return false;
}
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
int result = ACCESS_ABSTAIN;
fi.getRequest() // this is the request
// decide the outcome and set result
return result;
}
}

Resources