Jenkins Security Authentication Flow: How HudsonFilter and HttpSessionContextIntegrationFilter2 Work Together

Jenkins authenticates users through a two-filter architecture where HudsonFilter acts as the entry point for request processing and Stapler routing, while HttpSessionContextIntegrationFilter2 persists the Authentication object in the HTTP session to maintain security context across stateless requests.

Jenkins operates within a standard servlet container (Jetty, Tomcat, etc.) and delegates request-level security processing to a specialized filter chain. The Jenkins security authentication flow relies on two pivotal classes in the hudson.security package—HudsonFilter and HttpSessionContextIntegrationFilter2—to bridge the servlet container's HTTP handling with Jenkins' pluggable SecurityRealm architecture.

HudsonFilter: The Request Entry Point

HudsonFilter serves as the primary servlet filter that intercepts every incoming request to Jenkins. Located in core/src/main/java/hudson/security/HudsonFilter.java, this filter initializes the Stapler request-routing engine and orchestrates the security filter chain.

The filter executes the following sequence in its doFilter method:

  1. Wraps the raw request in a StaplerRequest to enable Jenkins' URL-to-method mapping
  2. Invokes ChainedServletFilter2 to sequentially execute security filters including BasicAuthenticationFilter, AuthenticationProcessingFilter2, and CrumbFilter
  3. Establishes the SecurityContext by binding any valid Authentication object to the thread via SecurityContextHolder
  4. Dispatches to Stapler for final handling by the appropriate Jenkins action or REST endpoint
// Simplified flow inside HudsonFilter#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
        throws IOException, ServletException {
    // 1. Wrap request for Stapler
    StaplerRequest staplerRequest = new StaplerRequestWrapper((HttpServletRequest) req);

    // 2. Run the Jenkins-specific filter chain
    chainedFilter.doFilter(staplerRequest, res, chain);

    // 3. Security context is now available for the rest of the request
    //    (e.g., Jenkins.getAuthentication())
}

HttpSessionContextIntegrationFilter2: Session Security Bridge

While HudsonFilter handles request entry, HttpSessionContextIntegrationFilter2 manages the persistence of authentication state across multiple HTTP requests. This filter, found in core/src/main/java/hudson/security/HttpSessionContextIntegrationFilter2.java, bridges the servlet container's HTTP session with Jenkins' security framework.

The filter performs three critical operations:

  • Retrieval: On each request, it checks the HTTP session for an existing SecurityContext stored under the attribute key HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY
  • Storage: After successful authentication (performed by upstream filters like BasicAuthenticationFilter), it persists the Authentication object in the session
  • Thread binding: It pushes the retrieved or created SecurityContext onto SecurityContextHolder so that Jenkins.getAuthentication() returns the current user throughout the request lifecycle
// Core logic from HttpSessionContextIntegrationFilter2#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
        throws IOException, ServletException {
    HttpServletRequest httpReq = (HttpServletRequest) req;
    HttpSession session = httpReq.getSession(false);

    // 1. Retrieve stored SecurityContext (if any)
    SecurityContext ctx = (session == null) ? null
            : (SecurityContext) session.getAttribute(
                HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY);

    // 2. If request carries a new Authentication (post-login), store it
    if (SecurityContextHolder.getContext().getAuthentication() != null) {
        ctx = SecurityContextHolder.getContext();
        if (session != null) {
            session.setAttribute(
                HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY, ctx);
        }
    }

    // 3. Bind context to thread for downstream processing
    try {
        SecurityContextHolder.setContext(ctx != null ? ctx : 
            SecurityContextHolder.createEmptyContext());
        chain.doFilter(req, res);
    } finally {
        SecurityContextHolder.clearContext();
    }
}

End-to-End Authentication Sequence

Understanding how these filters interact reveals the complete Jenkins security authentication flow:

Phase Component Action
Initial Request HudsonFilter → BasicAuthenticationFilter Extracts credentials (HTTP Basic, form data, or API token) and validates against the configured SecurityRealm (LDAP, Jenkins database, OAuth)
Authentication AuthenticationProcessingFilter2 Creates an Authentication object upon successful credential validation
Session Persistence HttpSessionContextIntegrationFilter2 Stores the Authentication in the HTTP session attribute SECURITY_CONTEXT_KEY
Subsequent Requests HudsonFilter → HttpSessionContextIntegrationFilter2 Retrieves the stored Authentication from the session and binds it to SecurityContextHolder
Authorization AuthorizationStrategy Validates the current Authentication against permissions before allowing access to protected resources

The filter order declared in Jenkins' generated web.xml ensures that HttpSessionContextIntegrationFilter2 executes after authentication filters create the Authentication object but before any business logic requires the security context.

Architectural Separation: Why Two Filters?

Jenkins separates these concerns to support both stateless API access and stateful web sessions:

  • HudsonFilter provides a generic entry point that guarantees every request undergoes Stapler processing and security chain evaluation. It remains agnostic to session management, making it suitable for API calls that authenticate on every request without cookies.

  • HttpSessionContextIntegrationFilter2 isolates session-binding logic, allowing multiple authentication mechanisms (form login, basic auth, token-based) to reuse the same session persistence code without duplication.

This separation enables API clients to remain stateless while providing browser users with persistent login sessions through standard HTTP cookies.

Practical Code Examples

Registering a Custom SecurityRealm

// In a Jenkins plugin's initialization code
@Extension
public class MyRealmPlugin extends Plugin {
    @Override
    public void start() throws Exception {
        Jenkins.get().setSecurityRealm(new MyCustomSecurityRealm());
        // Configure authorization strategy
        Jenkins.get().setAuthorizationStrategy(
            new GlobalMatrixAuthorizationStrategy());
    }
}

Accessing the Current Authentication

public class MyAction implements RootAction {
    @Override
    public String getDisplayName() {
        // Obtains user from SecurityContext populated by the filter chain
        Authentication auth = Jenkins.getAuthentication();
        User user = User.getById(auth.getName(), false);
        return "Hello, " + (user != null ? user.getFullName() : "anonymous");
    }
}

Implementing Secure Logout

public void doLogout(StaplerRequest req, StaplerResponse rsp) 
        throws IOException {
    HttpSession session = req.getSession(false);
    if (session != null) {
        session.removeAttribute(
            HttpSessionContextIntegrationFilter2.SECURITY_CONTEXT_KEY);
    }
    SecurityContextHolder.clearContext();  // Clear thread-local context
    rsp.sendRedirect2(req.getContextPath() + "/login");
}

Summary

  • HudsonFilter in hudson/security/HudsonFilter.java acts as the mandatory entry point for all Jenkins requests, initializing Stapler and executing the security filter chain.
  • HttpSessionContextIntegrationFilter2 in hudson/security/HttpSessionContextIntegrationFilter2.java maintains authentication state by storing and retrieving SecurityContext objects from the HTTP session.
  • The Jenkins security authentication flow separates request processing from session management to support both stateless API clients and stateful browser sessions.
  • Filter ordering in the servlet container ensures that HttpSessionContextIntegrationFilter2 executes after authentication filters create credentials but before authorization checks occur.
  • Developers can access the current authentication via Jenkins.getAuthentication(), which returns the Authentication object bound to the thread by these filters.

Frequently Asked Questions

How does HttpSessionContextIntegrationFilter2 handle concurrent requests from the same user?

Each HTTP request executes in its own thread, and HttpSessionContextIntegrationFilter2 retrieves the SecurityContext from the shared HTTP session at the start of each request. The filter binds this context to the thread-local SecurityContextHolder, ensuring thread isolation while allowing multiple simultaneous requests to share the same authentication credentials safely.

Can Jenkins operate without HttpSessionContextIntegrationFilter2?

While technically possible for purely stateless API access, removing this filter would force users to re-authenticate on every single request, eliminating the ability to maintain login sessions via browser cookies. The filter is essential for the standard web UI experience where users expect to remain logged in across multiple page loads.

Where does HudsonFilter fit in the standard servlet filter chain?

HudsonFilter is registered in the servlet container's web.xml (generated at runtime) and serves as the primary dispatch filter for all Jenkins URLs. It executes after container-level filters but before individual Jenkins components process the request, ensuring that SecurityContext is properly established regardless of whether the request targets a UI page, REST endpoint, or plugin resource.

What happens to the security context when a session expires?

When the HTTP session expires or is invalidated, HttpSessionContextIntegrationFilter2 cannot retrieve a stored SecurityContext on subsequent requests. The filter creates an empty context, causing Jenkins.getAuthentication() to return an anonymous or null authentication. The user must then re-authenticate through the configured SecurityRealm to establish a new valid session.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →