Skip to content

Add HTMX-aware AuthenticationEntryPoint for session expiry handling #294

@devondragon

Description

@devondragon

Problem

When an HTMX-powered page has polling or dynamic fragment requests, and the user's session expires, Spring Security's default LoginUrlAuthenticationEntryPoint sends a 302 redirect to the login page. HTMX transparently follows the redirect and swaps the full login page HTML into each target element, resulting in multiple broken login forms rendered inside card containers.

This is a common issue for any Spring Boot + Thymeleaf + HTMX application using the DS Spring User Framework.

Screenshot Context

In MagicMenu's admin dashboard (the first project to hit this), each HTMX-polled metric card renders a full login page when the session expires — Google button, email/password form, register link — all crammed into card-width containers.

Proposed Solution

1. Create HtmxAwareAuthenticationEntryPoint

A new AuthenticationEntryPoint implementation that wraps LoginUrlAuthenticationEntryPoint:

  • HTMX requests (HX-Request: true header) → Return 401 with JSON body:
    {"error": "authentication_required", "message": "Session expired. Please log in.", "loginUrl": "/user/login"}
  • Browser requests → Delegate to LoginUrlAuthenticationEntryPoint (current 302 redirect behavior)

~40 lines. Location: com.digitalsanctuary.spring.user.security.HtmxAwareAuthenticationEntryPoint

2. Wire into WebSecurityConfig.securityFilterChain()

Currently, exceptionHandling() is only configured when OAuth2 is enabled. Change to always configure it:

  • For standard form login: use HtmxAwareAuthenticationEntryPoint wrapping LoginUrlAuthenticationEntryPoint
  • For OAuth2: continue using CustomOAuth2AuthenticationEntryPoint (or wrap it with HTMX awareness too)

3. Allow consumer override via @ConditionalOnMissingBean

Follow the existing RegistrationGuard pattern — if a consuming application provides its own AuthenticationEntryPoint bean, use it instead of the default HtmxAwareAuthenticationEntryPoint.

Backward Compatibility

100% backward-compatible:

  • Browser requests get the same 302 redirect they always did
  • Only HTMX requests (which were broken before) get the new 401 behavior
  • Consumers can override with their own entry point bean if needed

Acceptance Criteria

  • HtmxAwareAuthenticationEntryPoint created and unit tested
  • WebSecurityConfig always configures exceptionHandling() with the HTMX-aware entry point
  • Consumer override works via @ConditionalOnMissingBean
  • Existing form login, OAuth2, MFA, and WebAuthn flows unaffected
  • Documentation updated

References

  • Discovered via MagicMenu issue MM-213 (admin dashboard shows login page in each HTMX card when session expires)
  • HTMX request detection pattern: check for HX-Request: true header
  • Existing HTMX detection in MagicMenu's TermsAcceptanceInterceptor validates this approach

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions