Computer scienceBackendSpring BootSpring SecuritySpring Security internals

AuthenticationEntryPoint

8 minutes read

When you visit a web page or click a link on a page, your browser sends an HTTP request to the server. The server then needs to check if the resource request is private or protected since you have not been authenticated at the moment. If the resource happens to be private or protected, the server sends back a response stating that you need to be authenticated. This response could be an HTTP response like a 401 code or a redirect to a login page. Now, you are asked to log in with your user credentials, which might be retrieved by the browser through a BASIC authentication dialogue box, a cookie, a certificate, and so on. Next, an HTTP POST containing the form you filled, or an HTTP HEADER holding your authentication details, is sent to the server. The server then checks the validity of the credentials received. If they are valid and you're authorized to access the protected resource, you will get a successful response, if not, you would receive a 403 error code indicating "forbidden". Spring security has an interface, the AuthenticationEntryPoint, and several implementing classes responsible for beginning this authentication process.

AuthenticationEntryPoint

The AuthenticationEntryPoint is used to set applications to perform the necessary actions when an unauthenticated user tries to request a secure (private/protected) HTTP resource. It can send an HTTP response seeking the user's credentials (username, password, access token) via one of its implementations. The AuthenticationEntryPoint uses the commence() method, which accepts three parameters: an HttpServletRequest object, an HttpServletResponse object, and an AuthenticationException object. You can override this method in the implementing classes to perform appropriate actions, such as a redirect to a login page or responding with a WWW-Authenticate header.

interface AuthenticationEntryPoint {

    @Throws(IOException::class, ServletException::class) // Not required in Kotlin, but added for Java interoperability
    fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authException: AuthenticationException
    )
}

ExceptionTranslationFilter

The ExceptionTranslationFilter class uses the AuthenticationEntryPoint to start an authentication process. It catches any AccessDeniedException and AuthenticationException thrown within the filter chain by using the filterChain.doFilter(request, response) method. It takes 2 parameters; an HttpServletRequest and an HttpServletResponse objects.

fun doFilter(
    httpServletRequest: HttpServletRequest,
    httpServletResponse: HttpServletResponse,
    filterChain: FilterChain
) {
    try {
        filterChain.doFilter(httpServletRequest, httpServletResponse)
    } catch (exception: Exception) {
        when (exception) {
            is AuthenticationException, is AccessDeniedException -> {
                if (!isAuthenticated || exception is AuthenticationException) {
                    startAuthentication(httpServletRequest, httpServletResponse, filterChain, exception)
                } else {
                    accessDeniedHandler.handle(httpServletRequest, httpServletResponse, exception)
                }
            }
            else -> throw exception
        }
    }
}
protected fun startAuthentication(
    httpServletRequest: HttpServletRequest,
    httpServletResponse: HttpServletResponse,
    filterChain: FilterChain,
    authenticationException: AuthenticationException
) {
    SecurityContextHolder.getContext().authentication = null
    requestCache.saveRequest(httpServletRequest, httpServletResponse)
    authenticationEntryPoint.commence(
        httpServletRequest,
        httpServletResponse,
        authenticationException
    )
}

When the ExceptionTranslationFilter detects an AuthenticationException, it starts the authenticationEntryPoint. If it detects an AccessDeniedException, it will either trigger the authenticationEntryPoint if the user is anonymous or delegate it to the AccessDeniedHandler if the user is not anonymous. By default, the filter uses the AccessDeniedHandlerImpl. If neither an AccessDeniedException or an AuthenticationException is thrown, then ExceptionTranslationFilter does nothing.

Authentication Entry Point

Common Implementations

There are some implementations of the AuthenticationEntryPoint interface that the ExceptionTranslationFilter uses to kickstart authentication.

1. BasicAuthenticationEntryPoint

This EntryPoint is used by the ExceptionTranslationFilter to initiate authentication via the BasicAuthenticationFilter. If you want to authenticate a user using BASIC authentication, you either need to close the browser or send an unauthorized (401) header during logout. This response is an HTTP 401 response. You can trigger an unauthorized header by calling the commence() method, which tells the browser that its credentials are no longer valid, prompting the user to log in again.

class BasicAuthenticationEntryPoint : AuthenticationEntryPoint, InitializingBean {
    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authenticationException: AuthenticationException
    ) {
        response.addHeader("WWW-Authenticate", "Basic realm=$realmName")
        val unauthorized = HttpStatus.UNAUTHORIZED
        response.sendError(unauthorized.value(), unauthorized.reasonPhrase)
    }
}

2. LoginUrlAuthenticationEntryPoint

This EntryPoint is used by the ExceptionTranslationFilter to initiate a form login authentication through the UsernamePasswordAuthenticationFilter. The LoginUrlAuthenticationEntryPoint class has a loginFormUrl property holding the location of the login form which is used to build a redirect URL to the login page.

class LoginUrlAuthenticationEntryPoint : AuthenticationEntryPoint, InitializingBean {
    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authenticationException: AuthenticationException
    ) {
        response.sendRedirect(loginUrl) // redirects the user to a login page
    }
}

3. Http403ForbiddenEntryPoint

This EntryPoint assumes that the user has already been identified using some external authentication process, and a secure context has already been established before the security-enforcement filter is invoked. This class will only be called when a user is rejected by the AbstractPreAuthenticatedProcessingFilter, resulting in a null authentication. The commence method() will always return a HttpServletResponse.SC_FORBIDDEN (403 error code) to the client. This class, unlike other authentication providers, is not mainly responsible for initiating authentication.

class Http403ForbiddenEntryPoint : AuthenticationEntryPoint {

    companion object {
        private val logger = LogFactory.getLog(Http403ForbiddenEntryPoint::class.java)
    }

    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authenticationException: AuthenticationException
    ) {
        logger.debug("A Pre-authenticated entry point has been initiated, access was denied")
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied")
    }
}

Custom Implementation

In Spring security, you may want to customize a response for a failed authentication. This could be necessary when you want to send a specific message to your user for the failed authentication. You can change the headers and the body to include your custom message. Through this custom implementation, you can ensure that no sensitive data is revealed or sent to your user. You can create your custom class, such as CustomAuthenticationEntryPoint, that implements the AuthenticationEntryPoint interface and overrides the commence() method with a custom message sent to the user. This method accepts these parameters: HttpServletRequest requestObject, which resulted in an AuthenticationException; HttpServletResponse responseObject, which will ask the user to begin authentication; and AuthenticationException authExObject responsible for the failed authentication.

class CustomAuthenticationEntryPoint : AuthenticationEntryPoint {

    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authException: AuthenticationException
    ) {
        response.addHeader("message", "Unauthorized to access this page")
        response.sendError(HttpStatus.UNAUTHORIZED.value())
    }
}

Conclusion

  • Authentication is how a user's identity is established when they try to access private or protected resources.

  • A common way of authenticating users is by asking for their credentials, usually username and password.

  • Once authentication is established, authorization can be performed.

  • AuthenticationEntryPoint is an interface used in Spring Security that sends an HTTP response asking for the user's credentials when an unauthenticated user tries to access private or protected resources.

  • When this occurs, the appropriate AuthenticationException or AccessDeniedException is thrown.

  • There are several built-in implementations of this interface.

  • In Spring Security, you can create your own custom implementation of this interface. This allows you to send specific or custom messages to your users.

  • You can also configure multiple AuthenticationEntryPoint for your application depending on what you need.

7 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo