contexa-identity

Zero Trust Filters

Request-time servlet filters that enforce access control and challenge continuation after authentication. These filters work with AISessionSecurityContextRepository, MFA context state, and ZeroTrustActionRepository to apply runtime zero trust outcomes inside the identity layer.

Overview

The identity layer uses two dedicated servlet filters. ZeroTrustAccessControlFilter handles BLOCK, ESCALATE, and PENDING_ANALYSIS. ZeroTrustChallengeFilter handles the CHALLENGE path and initializes or resumes MFA challenge sessions.


  Runtime Decision Path
  =====================

  [AISessionSecurityContextRepository]
              |
              v
  [Authenticated Request]
              |
              +--> [ZeroTrustAccessControlFilter]
              |         |- BLOCK
              |         |- ESCALATE
              |         `- PENDING_ANALYSIS
              |
              `--> [ZeroTrustChallengeFilter]
                        `- CHALLENGE

ZeroTrustAccessControlFilter

io.contexa.contexaidentity.security.zerotrust.ZeroTrustAccessControlFilter reads the current action with ZeroTrustActionRepository.getCurrentAction(userId, contextBindingHash). It only intercepts requests when the current runtime decision is BLOCK, ESCALATE, or PENDING_ANALYSIS.

DecisionBehavior
BLOCKBlocks the request immediately, or starts block-resolution MFA through ChallengeMfaInitializer when block-MFA is pending.
ESCALATEReturns an escalation response and sets retry metadata using the configured response writer.
PENDING_ANALYSISReturns a pending-analysis response until a definitive runtime action is available.

The filter deliberately skips logout, zero-trust API endpoints, and /.well-known/ paths via shouldNotFilter(...). It also avoids re-blocking MFA pages and dynamic MFA URLs discovered from MfaFlowUrlRegistry / AuthUrlProvider.

ZeroTrustChallengeFilter

io.contexa.contexaidentity.security.zerotrust.ZeroTrustChallengeFilter handles the CHALLENGE action. It resumes an existing MFA challenge session when possible, or calls ChallengeMfaInitializer.initializeChallengeFlow(...) to create a new challenge session.

For browser requests, the filter redirects to /zero-trust/challenge-required?mfaUrl=.... For API or AJAX requests, it returns a JSON error body containing mfaUrl, challengeNoticeUrl, sessionId, and the current MFA state.

Skipped by shouldNotFilter(...)Reason
/mfa/**, dynamic MFA page URLsPrevents re-triggering the challenge filter while the user is already inside an MFA flow.
/api/mfa/**, /admin/api/mfa/**Preserves MFA control-plane APIs.
/webauthn/**, /login/webauthnAllows WebAuthn registration and challenge processing to complete.
/zero-trust/**, /.well-known/**Avoids looping on challenge-notice and well-known endpoints.

Repositories and Runtime Context

Both filters depend on the runtime decision repository and MFA context services rather than acting as isolated servlet filters.

ComponentRole
ZeroTrustActionRepositoryStores and retrieves the current runtime action for a user plus context-binding hash.
AISessionSecurityContextRepositoryLoads the authenticated security context that feeds the post-auth zero trust decision path.
MfaSessionRepositoryPersists or resumes MFA challenge sessions.
MfaFlowUrlRegistry / AuthUrlProviderResolve default and prefixed MFA URLs for browser redirect and API response payloads.
ChallengeMfaInitializerBootstraps challenge MFA when runtime policy requires additional verification.

Request-Time Sequence


  Request-Time Zero Trust Sequence
  ================================

  [Authenticated Request]
          |
          v
  [ZeroTrustActionRepository.getCurrentAction(...)]
          |
          +--> BLOCK / ESCALATE / PENDING_ANALYSIS --> [ZeroTrustAccessControlFilter]
          |
          `--> CHALLENGE --> [ZeroTrustChallengeFilter]
                                   |
                                   +--> existing MFA session -> redirect / JSON response
                                   `--> initializeChallengeFlow(...) -> redirect / JSON response