Table of contents
- Introduction
- Goal
- Spring Stack
- Brief Intro to Spring Security
- Implementation
- What's next?
- Conclusion
- Source Code
- Reference
Introduction
In one of my project setups, the authentication process is offloaded to Keycloak and set up with an Open Policy Agent sidecar in a typical Kubernetes deployment. Any request that comes to the service, will be pre-authenticated, and all credentials/information are stored in the headers.
Since I'm using Spring Boot with Spring Security, I wanted to figure out if there's a way to integrate seamlessly within the application through Spring Security without a custom solution (i.e. writing custom filters). After some research, I figured that the most suitable way to do so is via Pre-Authentication Scenarios described in the documentation.
Goal
The goal is to be able to use the header(s) provided, construct and build into an Authentication object, specifically a PreAuthenticatedAuthenticationToken object, which can then use for all security-related needs.
In this tutorial, I explore how to make use of RequestHeaderAuthenticationFilter, an existing implementation provided by Spring Security, that relies on a header to identify and extract the username.
Spring Stack
At the time of writing, I am using the latest iteration of Spring Boot 2.7.13 with Spring Security 5.8.4 to ensure minimal changes are required when upgrading to Spring Boot 3.x and Spring Security 6.x in the future.
Brief Intro to Spring Security
Before I go any further, I want to briefly go through the architecture of Spring Security, specifically in this context.
When a client sends an HTTP request to the web server
It passes through several Servlet Filters, one of which is
DelegatingFilterProxy
(created by Spring) to bridge between Servlet Lifecycle and Spring ApplicationContextSpring Security creates a
FilterChainProxy
to supportSecurityFilterChain
where it can have multiple instances ofSecurityFilterChain
Each of the
SecurityFilterChain
can have one or moreFilter
registered, in this case, we are looking specifically atRequestHeaderAuthenticationFilter
which extendsAbstractPreAuthenticatedProcessingFilter
Implementation
Although Spring comes with some existing implementation, it doesn't quite fit my use case as I have a custom UserDetails
and I need to load my user permission from the database, hence I also need to write my custom AuthenticationUserDetailsService
and so on. Fortunately, Spring is super flexible and extensible, hence, I can easily extend and write my implementation as needed.
Without further ado, let's see how to implement this.
Initialize project
This project is generated from start.spring.io
Overwrite Spring Security version
Spring Boot 2.7.13 comes with Spring Security 5.7.9 by default, so I have to overwrite the version to use Spring Security 5.8.4.
In pom.xml
, add the following under properties
section
<properties>
<spring-security.version>5.8.4</spring-security.version>
</properties>
Start Application
This is an extracted portion of the logs when starting the application
2023-07-08 17:05:12.169 WARN 8820 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: 70e6538f-cc9f-4764-9959-cd1d2a6cd5b5
This generated password is for development use only. Your security configuration must be updated before running your application in production.
2023-07-08 17:05:12.500 INFO 8820 --- [ restartedMain] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@3f690985, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@458bfb19, org.springframework.security.web.context.SecurityContextPersistenceFilter@5e6593, org.springframework.security.web.header.HeaderWriterFilter@1cce17a1, org.springframework.security.web.csrf.CsrfFilter@238de12d, org.springframework.security.web.authentication.logout.LogoutFilter@36d43e9f, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3c373438, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@7fd33069, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@6c864ee5, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5a740d9c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@329f9df5, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@27623b68, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@66e2dd0b, org.springframework.security.web.session.SessionManagementFilter@692b3e18, org.springframework.security.web.access.ExceptionTranslationFilter@13ca9f33, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@1cd5e041]
It's quite difficult to see from the logs what filters are being applied, so let's see it from a different format.
Security filter chain: [
DisableEncodeUrlFilter
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
A total of 16 default filters
Two key things to learn from the log:
Default Generated Password
Default SecurityFilterChain
Without writing a single line of code, the application is protected by default through a series of sensible defaults (with best practices) provided out of the box by Spring Security. Isn't that awesome?
Read more of what's happening behind the scene in the documentation
Configure SecurityFilterChain
Create WebSecurityConfig
class to define our own SecurityFilterChain
and let's disable everything that is not required.
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable)
// we don't need to enable csrf, as 1) no view, 2) no session cookie authn
.csrf(AbstractHttpConfigurer::disable)
// disable logout
.logout(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
Exclude UserDetailsServiceAutoConfiguration
to prevent it from creating a default user and (generated) password via the application.yaml
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
Run the application again, and the updated SecurityFilterChain
looks like this now
Security filter chain: [
DisableEncodeUrlFilter
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
SessionManagementFilter
ExceptionTranslationFilter
]
Down to 8 filters (50% less!)
Start the application again, and the log that states the generated password is now gone (disable it via application.yaml
).
Configure custom UserDetails
It is very common to have a custom version of UserDetails
, and mine is no different.
public class PreAuthUserDetails implements UserDetails {
private final String username;
private final List<GrantedAuthority> authorities;
public PreAuthUserDetails(String username, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.authorities = new ArrayList<>(authorities);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return "N/A";
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Configure RequestHeaderAuthenticationFilter
As mentioned, I want to reuse the existing implementation as much as possible and overwrite it only when necessary. So let's create a RequestHeaderAuthenticationFilter
bean and configure it.
@Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() {
RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
requestHeaderAuthenticationFilter.setPrincipalRequestHeader("X-User");
requestHeaderAuthenticationFilter.setExceptionIfHeaderMissing(true);
return requestHeaderAuthenticationFilter;
}
What RequestHeaderAuthenticationFilter
does is extracting the principal
from X-User
header which will be used in AbstractPreAuthenticatedProcessingFilter#doAuthenticate
to create PreAuthenticatedAuthenticationToken
object.
While writing this blog, I realize that RequestHeaderAuthenticationFilter is not registered as part of the Spring Security Filter Chain but as a ServletFilter. So I wrote a separate post to explain why
Start the application now, and the following error will surface
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager must be set
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.28.jar:5.3.28]
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.afterPropertiesSet(AbstractPreAuthenticatedProcessingFilter.java:127) ~[spring-security-web-5.8.4.jar:5.8.4]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.28.jar:5.3.28]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.28.jar:5.3.28]
... 56 common frames omitted
This is because RequestHeaderAuthenticationFilter
extends AbstractPreAuthenticatedProcessingFilter
which requires an implementation of AuthenticationManager
and this brings us to the next point.
Register AuthenticationManager
An AuthenticationManager
must be provided to perform the authenticate
method. But before that, there are two key components (ProviderManager and AuthenticationProvider) we need to understand first.
AuthenticationManager is the API that defines how Spring Security’s Filters perform authentication.
ProviderManager
ProviderManager is the most commonly used implementation of AuthenticationManager
AuthenticationProvider
Multiple AuthenticationProviders can be injected into ProviderManager. Each AuthenticationProvider performs a specific type of authentication.
The explanation is taken directly from the documentation here, here and here.
In short, for RequestHeaderAuthenticationFilter
to perform doAuthenticate
. It requires the implementation of AuthenticationManager
which outsources the actual authentication through the AuthenticationProvider
, which relies on AuthenticationUserDetailsService
to create and return the UserDetails
object.
Since I am using a custom UserDetails
object, I will need to create a custom AuthenticationUserDetailsService
to construct our PreAuthUserDetails
object.
There is no need to create custom
PreAuthenticatedAuthenticationProvider
class since it has all I needed
Configure AuthenticationUserDetailsService
public class PreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
@Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws UsernameNotFoundException {
// hard-coded authority
List<GrantedAuthority> authorities = List.of(new SimpleGrantedAuthority("ADMIN"));
return this.buildUserDetails(token, authorities);
}
protected UserDetails buildUserDetails(PreAuthenticatedAuthenticationToken token, Collection<? extends GrantedAuthority> authorities) {
return new PreAuthUserDetails(token.getName(), authorities);
}
}
The nice thing about this is that it doesn't care how the user details object is constructed. It could be loaded from the database, hard-coded, or anything as long it returns the UserDetails
object.
For this, I will create a hard-coded authority first. In the later part of the blog, I will explore how to get this from the headers.
Update WebSecurityConfig
With all the necessary classes created, and configured. It is time to update the WebSecurityConfig
class to reflect the changes I've made thus far.
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfig {
@Bean // 1
public AuthenticationProvider authenticationProvider() {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new PreAuthUserDetailsService());
return provider;
}
@Bean // 2
public AuthenticationManager authenticationManager(AuthenticationProvider authenticationProvider) {
return new ProviderManager(authenticationProvider);
}
@Bean // 3
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
requestHeaderAuthenticationFilter.setPrincipalRequestHeader("X-User");
requestHeaderAuthenticationFilter.setExceptionIfHeaderMissing(true);
requestHeaderAuthenticationFilter.setAuthenticationManager(authenticationManager);
return requestHeaderAuthenticationFilter;
}
@Bean // 4
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.anonymous(AbstractHttpConfigurer::disable)
// we don't need to enable csrf, as 1) no view, 2) no session cookie authn
.csrf(AbstractHttpConfigurer::disable)
// disable logout
.logout(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}
Register
AuthenticationProvider
bean and settingPreAuthUserDetailsService
Register
AuthenticationManager
beanUpdate
RequestHeaderAuthenticationFilter
bean to setAuthenticationManager
No change
This is in line with what was drawn in the diagram above on the dependency between each class
At this stage, I have everything ready and the application should also start up just fine. Next, I will write some tests to verify if this is indeed working as intended.
Verification
Endpoint
Write a simple endpoint that returns UserDetails
.
@RestController
public class MeController {
@GetMapping("/me")
public UserDetails me(@AuthenticationPrincipal UserDetails userDetails) {
return userDetails;
}
}
@AuthenticationPrincipal
helps to resolve the current authenticated user
The controller exposes an HTTP GET API endpoint (/me
) that returns the authenticated user details.
Test Case
Then write a test to verify the interaction.
import org.springframework.http.MediaType;
@WebMvcTest(MeController.class)
@Import(WebSecurityConfig.class)
class MeControllerTests {
@Autowired
private MockMvc mockMvc;
@Test
void whenCallMe_shouldGetValidResponse2() throws Exception {
this.mockMvc
.perform(MockMvcRequestBuilders
.get("/me")
.header("X-User", "joseph"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("$.username").value("joseph"))
.andExpect(MockMvcResultMatchers.jsonPath("$.authorities").isArray())
.andExpect(MockMvcResultMatchers.jsonPath("$.authorities[0].authority").value("ADMIN"))
.andDo(MockMvcResultHandlers.print());
}
}
Note that for sliced tests such as
@WebMvcTest
, we need to import our security configuration since Spring Boot 2.7.x due to the deprecated WebSecurityConfigurerAdapter.To send the request via curl
curl localhost:8080/me -H "X-User: joseph"
The test does the following
Configure
mockMvc
to simulate an HTTP GET request to/me
withX-User
headerAssert the (JSON) response contains the username and authorities
Print out the log
Here's the log of the request after the test case is ran
MockHttpServletRequest:
HTTP Method = GET
Request URI = /me
Parameters = {}
Headers = [X-User:"joseph"]
Body = null
Session Attrs = {SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=PreAuthenticatedAuthenticationToken [Principal=PreAuthUserDetails(username=joseph, authorities=[ADMIN]), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[ADMIN]]]}
Handler:
Type = com.bwgjoseph.springsecuritycustompreauthenticationflow.MeController
Method = com.bwgjoseph.springsecuritycustompreauthenticationflow.MeController#me(UserDetails)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = application/json
Body = {"username":"joseph","authorities":[{"authority":"ADMIN"}],"enabled":true,"credentialsNonExpired":true,"accountNonExpired":true,"accountNonLocked":true,"password":"N/A"}
Forwarded URL = null
Redirected URL = null
Cookies = []
It works! Of course, it does, I've spent quite some time writing this blog post, ensuring that it works, so why wouldn't it? 🤣🤣
Configure AuthenticationDetailsSource
But.... is that all? Not quite. Remember that I hard-coded the authorities to ADMIN
in our existing implementation? Now, how can we get the value from the other header key? Knowing that RequestHeaderAuthenticationFilter
supports only getting one header value, which is the username/principal. So how do we extract the additional headers from the request headers, and use it?
For this, I need to create a custom implementation of AuthenticationDetailsSource
and WebAuthenticationDetails
and pass it to RequestHeaderAuthenticationFilter
. Before that, let's try to understand why I need to do so.
If we look at the current implementation of AbstractPreAuthenticatedProcessingFilter and zoom into the following code
PreAuthenticatedAuthenticationToken authenticationRequest = new PreAuthenticatedAuthenticationToken(principal, credentials);
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
We can learn that there is a property - details
that we can store any information as long as it is an Object
type through AuthenticationDetailsSource
. Knowing this, I can then use it to store the additional headers information through the custom implementation of AuthenticationDetailsSource
and provide it to RequestHeaderAuthenticationFilter
.
public class PreAuthAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new PreAuthenticationDetails(context);
}
}
The key here is the implementation of PreAuthenticationDetails
where it will grab the header from the request.
@EqualsAndHashCode(callSuper = true)
public class PreAuthenticationDetails extends WebAuthenticationDetails implements GrantedAuthoritiesContainer {
private static final String HEADER_AUTHORITY = "X-Authorities";
private final List<String> authorities;
public PreAuthenticationDetails(HttpServletRequest request) {
super(request);
this.authorities = List.of(request.getHeader(HEADER_AUTHORITY).split(","));
}
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities() {
return this.authorities.stream().map(SimpleGrantedAuthority::new).toList();
}
}
In addition to extending
WebAuthenticationDetails
, I'm also implementingGrantedAuthoritiesContainer
to indicate that this object (PreAuthenticationDetails
) can be used to obtain user authoritiesI define the header to be of
X-Authorities
and expect it to be a comma-separated string format
This is just an example of how I can get additional headers value. The number of headers or the format of the headers is entirely dependent on the implementor.
Ensure that proper validation is done while parsing the header such as checking if the key exists, the value is null, isBlank and so on
Update PreAuthUserDetailsService
With that, I will be getting the authorities from the PreAuthenticationDetails
instead of using the hard-coded values.
public class PreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
@Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) throws UsernameNotFoundException {
// update to get the details from token, instead of hardcoded value
PreAuthenticationDetails details = (PreAuthenticationDetails) token.getDetails();
return this.buildUserDetails(token, details.getGrantedAuthorities());
}
protected UserDetails buildUserDetails(PreAuthenticatedAuthenticationToken token, Collection<? extends GrantedAuthority> authorities) {
return new PreAuthUserDetails(token.getName(), authorities);
}
}
Update WebSecurityConfig
Lastly, I need to update the WebSecurityConfig
class again to configure RequestHeaderAuthenticationFilter
to use my custom AuthenticationDetailsSource
class.
@Bean
public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(AuthenticationManager authenticationManager) {
RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
requestHeaderAuthenticationFilter.setPrincipalRequestHeader("X-User");
requestHeaderAuthenticationFilter.setExceptionIfHeaderMissing(true);
requestHeaderAuthenticationFilter.setAuthenticationManager(authenticationManager);
// add this line
requestHeaderAuthenticationFilter.setAuthenticationDetailsSource(new PreAuthAuthenticationDetailsSource());
return requestHeaderAuthenticationFilter;
}
Update Test Case
I also need to update my test case to include the additional header and assertions.
@Test
void whenCallMe_shouldGetValidResponse() throws Exception {
this.mockMvc
.perform(MockMvcRequestBuilders
.get("/me")
.header("X-User", "joseph")
.header("X-Authorities", "moderator,user"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("$.username").value("joseph"))
.andExpect(MockMvcResultMatchers.jsonPath("$.authorities").isArray())
.andExpect(MockMvcResultMatchers.jsonPath("$.authorities[0].authority").value("moderator"))
.andExpect(MockMvcResultMatchers.jsonPath("$.authorities[1].authority").value("user"))
.andDo(MockMvcResultHandlers.print());
}
The output of the test is as follows
MockHttpServletRequest:
HTTP Method = GET
Request URI = /me
Parameters = {}
Headers = [X-User:"joseph", X-Authorities:"moderator,user"]
Body = null
Session Attrs = {SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=PreAuthenticatedAuthenticationToken [Principal=PreAuthUserDetails(username=joseph, authorities=[moderator, user]), Credentials=[PROTECTED], Authenticated=true, Details=PreAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[moderator, user]]]}
Handler:
Type = com.bwgjoseph.springsecuritycustompreauthenticationflow.MeController
Method = com.bwgjoseph.springsecuritycustompreauthenticationflow.MeController#me(UserDetails)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = application/json
Body = {"username":"joseph","authorities":[{"authority":"moderator"},{"authority":"user"}],"enabled":true,"password":"N/A","accountNonExpired":true,"credentialsNonExpired":true,"accountNonLocked":true}
Forwarded URL = null
Redirected URL = null
Cookies = []
To send the request via curl
curl localhost:8080/me -H "X-User: joseph" -H "X-Authorities: moderator,user"
With that, I have everything I needed for the entire implementation.
What's next?
I intend to build and package the implementation into a spring-boot-starter
library shortly, and then open-source it internally within my organization so that other projects can benefit from it and use it with zero-configuration.
Conclusion
In summary, we have looked at
Spring Security Architecture
- Understanding core components (
AuthenticationManager, Filter Chain, ProviderManager, AuthenticationProvider
)
- Understanding core components (
Configuring
SecurityFilterChain
Custom implementation of the various classes (
PreAuthUserDetailsService, PreAuthAuthenticationDetailsSource, PreAuthenticationDetails
) when the default implementation doesn't quite suit our needsDigging into some of the internal implementations of Spring Security
Wiring up everything together
Documenting the various insights and digging into some of the internal implementations helps me to learn that it is not that difficult to implement it. And finally, allow me to gain more insights into Spring Security Architecture which has been quite elusive to me for the longest time. This should also serve as a note for myself when I start to forget about the details in the future!
Source Code
As usual, the full source code is available on GitHub