Describe the bug On our systems we see a NullPointerException for cached static resources with spring-security-web-5.3.3:

java.lang.NullPointerException: null
    at org.springframework.security.web.context.HttpSessionSecurityContextRepository.isTransientAuthentication(HttpSessionSecurityContextRepository.java:444) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.context.HttpSessionSecurityContextRepository.access$300(HttpSessionSecurityContextRepository.java:82) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper.createNewSessionIfAllowed(HttpSessionSecurityContextRepository.java:389) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper.saveContext(HttpSessionSecurityContextRepository.java:363) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper.onResponseCommitted(SaveContextOnUpdateOrErrorResponseWrapper.java:85) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.doOnResponseCommitted(OnCommittedResponseWrapper.java:252) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.checkContentLength(OnCommittedResponseWrapper.java:242) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.util.OnCommittedResponseWrapper.access$200(OnCommittedResponseWrapper.java:34) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.write(OnCommittedResponseWrapper.java:644) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.write(OnCommittedResponseWrapper.java:645) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.util.FastByteArrayOutputStream.writeTo(FastByteArrayOutputStream.java:249) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.web.util.ContentCachingResponseWrapper.copyBodyToResponse(ContentCachingResponseWrapper.java:215) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.web.util.ContentCachingResponseWrapper.copyBodyToResponse(ContentCachingResponseWrapper.java:199) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.web.filter.ShallowEtagHeaderFilter.updateResponse(ShallowEtagHeaderFilter.java:132) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.web.filter.ShallowEtagHeaderFilter.doFilterInternal(ShallowEtagHeaderFilter.java:109) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.7.RELEASE.jar:5.2.7.RELEASE]
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1618) ~[jetty-servlet-9.4.28.v20200408.jar:9.4.28.v20200408]
    at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:124) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    [...]

This seems to happen because the authentication returned by SecurityContextHolder.getContext() is null.

To Reproduce Unknown, because the error does not happen often.

Expected behavior I am not sure if the authentication may be null at all, but instead of a NullPointerException resulting in an "Internal Server Error" I would expect a plain "Unauthorized".

Comment From: jzheaux

@stovocor, I agree that a NullPointerException in this case is not desirable.

Since isTransientAuthentication cannot determine whether or not the authentication is transient in this case, I think it's reasonable to have it return false.

Comment From: marcusdacoregio

@stovocor, thank you for pointing this out.

I was looking into the code and the authentication is checked for null before the method is called:

if (authentication == null || trustResolver.isAnonymous(authentication)) { // authentication is being checked for null
    if (logger.isDebugEnabled()) {
        logger.debug("SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.");
    }

    if (httpSession != null && authBeforeExecution != null) {
        // SEC-1587 A non-anonymous context may still be in the session
        // SEC-1735 remove if the contextBeforeExecution was not anonymous
        httpSession.removeAttribute(springSecurityContextKey);
    }
    return;
}

if (httpSession == null) {
    httpSession = createNewSessionIfAllowed(context); // the method that is throwing NPE call
}

So in the scenario where the authentication is null it shouldreturn from this method and not reach the createNewSessionIfAllowed.

It seems to be a concurrency issue where the authentication is getting removed after the authentication == null test and before the createNewSessionIfAllowed method call.

To narrow the problem down, could you provide a sample that replicates the behavior?

Comment From: windmueller

To narrow the problem down, could you provide a sample that replicates the behavior?

I am very sorry, but as I mentioned in the original bugreport, I am not able to reproduce this problem and it occurs rarely.

Comment From: marcusdacoregio

@stovocor I see, thanks for the feedback. We are planning to work on a more general solution that would address more scenarios. @jzheaux do we have already an issue open for that?

Comment From: jzheaux

Yes, https://github.com/spring-projects/spring-security/issues/9634

Comment From: marcusdacoregio

Hi @stovocor, we have now a proposed PR #9813 to address this problem.

Comment From: marcusdacoregio

Hello @stovocor, the PR is now merged and is aimed at the milestone 5.6.0-M1. Thanks for the report and feel free to discuss here if you want anything else.