Skip to main content

Continuous Verification and Telemetry

[!IMPORTANT] Core Principle: Authentication is not a one-time gateway check at initial login. Trust decays over time and must be re-evaluated continuously throughout the lifespan of every active session.


1. Mechanics of Continuous Adaptive Trust

In traditional systems, session tokens remain valid for hours regardless of contextual changes. Zero Trust introduces a Continuous Adaptive Trust Engine that recalculates trust dynamically whenever risk signals change.

Risk Signal Sources & Continuous Evaluation Vectors

Risk VectorTelemetry SourceSignal / MetricAutomated Action
Impossible TravelIdP Audit LogsLogin from London, 10 minutes later request from TokyoImmediate session revocation + security alert
Endpoint EDR AlertCrowdStrike / SentinelOneMalware execution detected on client machineRevoke all OAuth tokens bound to device ID
IP Reputation ChangeThreat Intelligence FeedClient IP flagged as TOR exit node / Botnet IPTrigger step-up FIDO2 re-authentication
Privilege EscalationAdmin IAM PortalUser removed from Finance-Admins Okta groupRevoke active access tokens via CAEP broadcast

2. Standards: CAEP & OpenID Shared Signals (SSF)

The Continuous Access Evaluation Protocol (CAEP) and RISC (Risk and Incident Sharing and Coordination), standardized under the OpenID Foundation Shared Signals Framework (SSF), allow Identity Providers (IdPs) and Service Providers (SPs) to exchange real-time security events via Security Event Tokens (SETs).

// Example CAEP Security Event Token (SET)
{
"iss": "https://idp.enterprise.com/",
"jti": "set-evt-9948104812",
"iat": 1774483200,
"aud": "https://api.internal-app.com",
"events": {
"https://schemas.openid.net/secevent/caep/event-type/session-revocation": {
"subject": {
"format": "iss_sub",
"iss": "https://idp.enterprise.com/",
"sub": "user_891234"
},
"event_timestamp": 1774483195,
"reason_admin": {
"en": "Device posture compliance check failed: EDR disabled"
}
}
}
}

3. Production Code Implementations


Python: CAEP Event Receiver & Instant Token Revocation Engine

The code below implements an event-driven risk evaluation engine that consumes CAEP Security Event Tokens and instantly invalidates active user sessions stored in a distributed Redis cache.

# continuous_risk_engine.py
import json
import redis
import jwt
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
from pydantic import BaseModel

app = FastAPI(title="Zero Trust Risk Evaluation & Revocation Engine")

# Distributed cache for active session JTI blacklisting
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

IDP_PUBLIC_KEY = "idp-public-key-pem"

class SecurityEventToken(BaseModel):
set_jwt: str

def revoke_user_sessions(user_id: str, reason: str):
"""Adds all active user session tokens to the revocation blacklist."""
print(f"[CAEP REVOCATION] Revoking sessions for user {user_id}. Reason: {reason}")
# Store revocation flag in Redis with TTL matching max token expiration (e.g. 3600s)
r.setex(name=f"revoked:user:{user_id}", time=3600, value=reason)

@app.post("/events/caep/receiver")
async def receive_caep_event(event_payload: SecurityEventToken, background_tasks: BackgroundTasks):
"""
Webhook endpoint to consume incoming OpenID CAEP Security Event Tokens (SETs).
"""
try:
payload = jwt.decode(event_payload.set_jwt, IDP_PUBLIC_KEY, algorithms=["RS256"])
events = payload.get("events", {})

caep_schema = "https://schemas.openid.net/secevent/caep/event-type/session-revocation"
if caep_schema in events:
evt_data = events[caep_schema]
user_id = evt_data.get("subject", {}).get("sub")
reason = evt_data.get("reason_admin", {}).get("en", "Risk Threshold Exceeded")

if user_id:
# Trigger asynchronous revocation in background
background_tasks.add_task(revoke_user_sessions, user_id, reason)
return {"status": "accepted", "action": "session_revocation_queued"}

except jwt.PyJWTError as e:
raise HTTPException(status_code=400, detail=f"Invalid SET Signature: {str(e)}")

return {"status": "ignored", "detail": "Event type not handled"}

@app.get("/api/v1/protected-resource")
async def get_resource(request: Request):
user_id = request.headers.get("X-User-Id")

# CONTINUOUS VERIFICATION CHECK: Query Redis blacklist on every API call
revocation_reason = r.get(f"revoked:user:{user_id}")
if revocation_reason:
raise HTTPException(
status_code=401,
detail=f"Session revoked dynamically. Cause: {revocation_reason}"
)

return {"data": "Sensitive corporate data"}

Go: OpenTelemetry (OTel) Zero Trust Telemetry Logging

// logger.go
package main

import (
"context"
"encoding/json"
"log"
"net/http"
"time"
)

type ZeroTrustAuditLog struct {
Timestamp string `json:"timestamp"`
TraceID string `json:"trace_id"`
SubjectID string `json:"subject_id"`
SpiffeID string `json:"spiffe_id"`
ClientIP string `json:"client_ip"`
DeviceManaged bool `json:"device_managed"`
RiskScore int `json:"risk_score"`
PolicyDecision string `json:"policy_decision"`
ResourcePath string `json:"resource_path"`
}

func TelemetryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()

// Extract security context from headers
auditEntry := ZeroTrustAuditLog{
Timestamp: startTime.Format(time.RFC3339),
TraceID: r.Header.Get("X-Trace-Id"),
SubjectID: r.Header.Get("X-User-Id"),
SpiffeID: r.Header.Get("X-Spiffe-Client-Id"),
ClientIP: r.RemoteAddr,
DeviceManaged: r.Header.Get("X-Device-Managed") == "true",
RiskScore: 15, // Calculated risk metric
PolicyDecision: "ALLOW",
ResourcePath: r.URL.Path,
}

// Execute next handler
next.ServeHTTP(w, r)

// Emit structured JSON telemetry log for SIEM correlation
logBytes, _ := json.Marshal(auditEntry)
log.Println(string(logBytes))
})
}

4. Nuances & Operational Edge Cases

Challenge / Edge CaseFailure ScenarioOperational Mitigation
Network Switching False PositivesExecutive switches from office Wi-Fi to cellular 5G, triggering an IP anomaly alert and abrupt logouts.Configure risk engine to require Multi-Factor Anomaly Signals (IP shift + EDR threat + device change) before hard revocation.
Redis Cache OutagePEP cannot verify token revocation status if distributed cache goes down.Implement Fail-Closed Fallback with short local TTL memory caches (15-30 seconds).
High-Volume Telemetry BottleneckLogging every single microservice L7 call exhausts SIEM ingestion budgets.Use eBPF sampling (e.g., Cilium Hubble) to log 100% of DENY events, but sample 5-10% of high-frequency ALLOW traffic.

[!TIP] SIEM & SOAR Integration: Ensure all PEP access logs include standardized correlation identifiers (X-Trace-Id, X-Request-Id) to enable instant SOAR playbook execution during an active security incident.

Share this guide