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.
| Decision | Behavior |
|---|---|
BLOCK | Blocks the request immediately, or starts block-resolution MFA through ChallengeMfaInitializer when block-MFA is pending. |
ESCALATE | Returns an escalation response and sets retry metadata using the configured response writer. |
PENDING_ANALYSIS | Returns 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 URLs | Prevents 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/webauthn | Allows 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.
| Component | Role |
|---|---|
ZeroTrustActionRepository | Stores and retrieves the current runtime action for a user plus context-binding hash. |
AISessionSecurityContextRepository | Loads the authenticated security context that feeds the post-auth zero trust decision path. |
MfaSessionRepository | Persists or resumes MFA challenge sessions. |
MfaFlowUrlRegistry / AuthUrlProvider | Resolve default and prefixed MFA URLs for browser redirect and API response payloads. |
ChallengeMfaInitializer | Bootstraps 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