CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-authentication-api

Core authentication API module for Apereo CAS providing fundamental authentication interfaces, implementations, and components for the CAS server infrastructure

Pending
Overview
Eval results
Files

adaptive-authentication.mddocs/

Adaptive Authentication

Adaptive authentication provides risk-based security that dynamically adjusts authentication requirements based on contextual factors such as location, device characteristics, user behavior patterns, and threat intelligence. The CAS authentication API includes comprehensive support for adaptive authentication policies and intelligence services.

Core Adaptive Authentication Interface

package org.apereo.cas.authentication.adaptive;

import org.apereo.cas.authentication.adaptive.geo.GeoLocationRequest;
import org.springframework.webflow.execution.RequestContext;

public interface AdaptiveAuthenticationPolicy {
    boolean isAuthenticationRequestAllowed(RequestContext requestContext,
                                         String userAgent,
                                         GeoLocationRequest location) throws Throwable;
}

Default Adaptive Authentication Policy

package org.apereo.cas.authentication.adaptive;

import org.apereo.cas.authentication.adaptive.geo.GeoLocationRequest;
import org.apereo.cas.authentication.adaptive.geo.GeoLocationResponse;
import org.apereo.cas.authentication.adaptive.geo.GeoLocationService;
import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceService;
import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceResponse;
import org.apereo.cas.configuration.model.core.authentication.AdaptiveAuthenticationProperties;
import org.springframework.webflow.execution.RequestContext;

public class DefaultAdaptiveAuthenticationPolicy implements AdaptiveAuthenticationPolicy {
    
    private final GeoLocationService geoLocationService;
    private final IPAddressIntelligenceService ipAddressIntelligenceService;
    private final AdaptiveAuthenticationProperties adaptiveAuthenticationProperties;
    
    public DefaultAdaptiveAuthenticationPolicy(GeoLocationService geoLocationService,
                                             IPAddressIntelligenceService ipAddressIntelligenceService,
                                             AdaptiveAuthenticationProperties adaptiveAuthenticationProperties) {
        this.geoLocationService = geoLocationService;
        this.ipAddressIntelligenceService = ipAddressIntelligenceService;
        this.adaptiveAuthenticationProperties = adaptiveAuthenticationProperties;
    }
    
    @Override
    public boolean isAuthenticationRequestAllowed(RequestContext requestContext,
                                                String userAgent,
                                                GeoLocationRequest location) throws Throwable {
        
        ClientInfo clientInfo = ClientInfoHolder.getClientInfo();
        if (clientInfo == null || StringUtils.isBlank(userAgent)) {
            // Allow if no client information available
            return true;
        }
        
        String clientIp = clientInfo.getClientIpAddress();
        
        // Check IP address restrictions
        if (isIpAddressRejected(requestContext, clientIp)) {
            return false;
        }
        
        // Check user agent restrictions
        if (isUserAgentRejected(userAgent)) {
            return false;
        }
        
        // Check geographical location restrictions
        if (isGeoLocationRejected(location)) {
            return false;
        }
        
        // Check time-based restrictions
        if (isTimeBasedAuthenticationRejected()) {
            return false;
        }
        
        return true;
    }
    
    private boolean isIpAddressRejected(RequestContext requestContext, String clientIp) {
        // Check rejected IP patterns
        Set<String> rejectedIps = adaptiveAuthenticationProperties.getRejectIpAddresses();
        if (rejectedIps.contains(clientIp)) {
            return true;
        }
        
        // Check with IP intelligence service
        IPAddressIntelligenceResponse intelligence = 
            ipAddressIntelligenceService.examine(requestContext, clientIp);
            
        if (intelligence != null && intelligence.getScore() > adaptiveAuthenticationProperties.getIpIntelligence().getRiskThreshold()) {
            return true;
        }
        
        return false;
    }
    
    private boolean isUserAgentRejected(String userAgent) {
        Set<String> rejectedUserAgents = adaptiveAuthenticationProperties.getRejectUserAgents();
        return rejectedUserAgents.stream()
            .anyMatch(pattern -> RegexUtils.matches(pattern, userAgent));
    }
    
    private boolean isGeoLocationRejected(GeoLocationRequest location) {
        if (location == null || geoLocationService == null) {
            return false;
        }
        
        try {
            GeoLocationResponse response = geoLocationService.locate(location);
            
            if (response != null) {
                String country = response.getCountry();
                Set<String> rejectedCountries = adaptiveAuthenticationProperties.getRejectCountries();
                
                if (rejectedCountries.contains(country)) {
                    return true;
                }
            }
        } catch (Exception e) {
            // Log error but don't reject authentication due to geo-location failure
        }
        
        return false;
    }
    
    private boolean isTimeBasedAuthenticationRejected() {
        LocalTime now = LocalTime.now();
        LocalTime startTime = adaptiveAuthenticationProperties.getRequireTimePeriod().getStartTime();
        LocalTime endTime = adaptiveAuthenticationProperties.getRequireTimePeriod().getEndTime();
        
        if (startTime != null && endTime != null) {
            return now.isBefore(startTime) || now.isAfter(endTime);
        }
        
        return false;
    }
}

IP Address Intelligence Services

IP Address Intelligence Interface

package org.apereo.cas.authentication.adaptive.intel;

import org.springframework.webflow.execution.RequestContext;

public interface IPAddressIntelligenceService {
    IPAddressIntelligenceResponse examine(RequestContext requestContext, String clientIpAddress);
}

IP Address Intelligence Response

package org.apereo.cas.authentication.adaptive.intel;

import java.util.Map;

public class IPAddressIntelligenceResponse {
    
    public enum IPAddressIntelligenceStatus {
        ALLOWED, BLOCKED, SUSPICIOUS, UNKNOWN
    }
    
    private final IPAddressIntelligenceStatus status;
    private final double score;
    private final Map<String, Object> details;
    
    public IPAddressIntelligenceResponse(IPAddressIntelligenceStatus status,
                                       double score,
                                       Map<String, Object> details) {
        this.status = status;
        this.score = score;
        this.details = details != null ? details : Map.of();
    }
    
    public IPAddressIntelligenceStatus getStatus() { return status; }
    public double getScore() { return score; }
    public Map<String, Object> getDetails() { return details; }
    
    public boolean isBanned() {
        return status == IPAddressIntelligenceStatus.BLOCKED;
    }
    
    public boolean isAllowed() {
        return status == IPAddressIntelligenceStatus.ALLOWED;
    }
    
    public boolean isSuspicious() {
        return status == IPAddressIntelligenceStatus.SUSPICIOUS;
    }
}

Base IP Address Intelligence Service

package org.apereo.cas.authentication.adaptive.intel;

import org.springframework.webflow.execution.RequestContext;

public abstract class BaseIPAddressIntelligenceService implements IPAddressIntelligenceService {
    
    protected boolean isPrivateIpAddress(String ipAddress) {
        return ipAddress.startsWith("10.") ||
               ipAddress.startsWith("192.168.") ||
               ipAddress.startsWith("172.") ||
               ipAddress.equals("127.0.0.1") ||
               ipAddress.equals("localhost");
    }
    
    protected double calculateRiskScore(Map<String, Object> factors) {
        double score = 0.0;
        
        // Example risk scoring logic
        if (factors.containsKey("knownMalicious") && (Boolean) factors.get("knownMalicious")) {
            score += 0.8;
        }
        
        if (factors.containsKey("recentActivity")) {
            Integer activityCount = (Integer) factors.get("recentActivity");
            if (activityCount > 100) {
                score += 0.3;
            }
        }
        
        if (factors.containsKey("geoAnomaly") && (Boolean) factors.get("geoAnomaly")) {
            score += 0.4;
        }
        
        return Math.min(1.0, score);
    }
}

Default IP Address Intelligence Service

package org.apereo.cas.authentication.adaptive.intel;

import org.springframework.webflow.execution.RequestContext;
import java.util.Map;

public class DefaultIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {
    
    private final Map<String, IPAddressIntelligenceResponse.IPAddressIntelligenceStatus> knownIpAddresses;
    
    public DefaultIPAddressIntelligenceService() {
        this.knownIpAddresses = new ConcurrentHashMap<>();
    }
    
    @Override
    public IPAddressIntelligenceResponse examine(RequestContext requestContext, 
                                               String clientIpAddress) {
        
        // Check private/local addresses
        if (isPrivateIpAddress(clientIpAddress)) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,
                0.0,
                Map.of("reason", "private IP address"));
        }
        
        // Check known IP addresses
        IPAddressIntelligenceResponse.IPAddressIntelligenceStatus knownStatus = 
            knownIpAddresses.get(clientIpAddress);
            
        if (knownStatus != null) {
            double score = knownStatus == IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED ? 1.0 : 0.0;
            return new IPAddressIntelligenceResponse(knownStatus, score, Map.of("source", "known addresses"));
        }
        
        // Default to unknown/allowed
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,
            0.0,
            Map.of("reason", "no intelligence available"));
    }
    
    public void addKnownIpAddress(String ipAddress, 
                                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status) {
        knownIpAddresses.put(ipAddress, status);
    }
    
    public void removeKnownIpAddress(String ipAddress) {
        knownIpAddresses.remove(ipAddress);
    }
}

BlackDot IP Address Intelligence Service

Service that integrates with BlackDot threat intelligence:

package org.apereo.cas.authentication.adaptive.intel;

import org.springframework.webflow.execution.RequestContext;
import org.springframework.web.client.RestTemplate;
import java.util.Map;

public class BlackDotIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {
    
    private final RestTemplate restTemplate;
    private final String apiEndpoint;
    private final String apiKey;
    
    public BlackDotIPAddressIntelligenceService(RestTemplate restTemplate,
                                              String apiEndpoint,
                                              String apiKey) {
        this.restTemplate = restTemplate;
        this.apiEndpoint = apiEndpoint;
        this.apiKey = apiKey;
    }
    
    @Override
    public IPAddressIntelligenceResponse examine(RequestContext requestContext, 
                                               String clientIpAddress) {
        
        if (isPrivateIpAddress(clientIpAddress)) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,
                0.0,
                Map.of("reason", "private IP address"));
        }
        
        try {
            // Call BlackDot API
            Map<String, Object> request = Map.of(
                "ip", clientIpAddress,
                "apiKey", apiKey
            );
            
            Map<String, Object> response = restTemplate.postForObject(
                apiEndpoint + "/check", request, Map.class);
                
            if (response != null) {
                Boolean isBlacklisted = (Boolean) response.get("blacklisted");
                Double riskScore = (Double) response.get("riskScore");
                
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status;
                if (isBlacklisted != null && isBlacklisted) {
                    status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED;
                } else if (riskScore != null && riskScore > 0.5) {
                    status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS;
                } else {
                    status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED;
                }
                
                return new IPAddressIntelligenceResponse(status, riskScore != null ? riskScore : 0.0, response);
            }
            
        } catch (Exception e) {
            // Log error and return unknown status
        }
        
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,
            0.0,
            Map.of("error", "BlackDot API unavailable"));
    }
}

RESTful IP Address Intelligence Service

Generic REST-based intelligence service:

package org.apereo.cas.authentication.adaptive.intel;

import org.springframework.webflow.execution.RequestContext;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import java.util.Map;

public class RestfulIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {
    
    private final RestTemplate restTemplate;
    private final String endpoint;
    private final HttpHeaders headers;
    
    public RestfulIPAddressIntelligenceService(RestTemplate restTemplate,
                                             String endpoint,
                                             HttpHeaders headers) {
        this.restTemplate = restTemplate;
        this.endpoint = endpoint;
        this.headers = headers;
    }
    
    @Override
    public IPAddressIntelligenceResponse examine(RequestContext requestContext, 
                                               String clientIpAddress) {
        
        if (isPrivateIpAddress(clientIpAddress)) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,
                0.0,
                Map.of("reason", "private IP address"));
        }
        
        try {
            Map<String, Object> requestBody = Map.of(
                "ipAddress", clientIpAddress,
                "context", extractContextFromRequest(requestContext)
            );
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
            
            Map<String, Object> response = restTemplate.exchange(
                endpoint, HttpMethod.POST, entity, Map.class).getBody();
                
            if (response != null) {
                String statusStr = (String) response.get("status");
                Double score = (Double) response.get("score");
                
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status = 
                    parseStatus(statusStr);
                    
                return new IPAddressIntelligenceResponse(status, score != null ? score : 0.0, response);
            }
            
        } catch (Exception e) {
            // Log error and return unknown status
        }
        
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,
            0.0,
            Map.of("error", "REST service unavailable"));
    }
    
    private Map<String, Object> extractContextFromRequest(RequestContext requestContext) {
        // Extract additional context information
        return Map.of(
            "userAgent", requestContext.getExternalContext().getNativeRequest().getHeader("User-Agent"),
            "timestamp", System.currentTimeMillis(),
            "sessionId", requestContext.getFlowScope().getString("sessionId")
        );
    }
    
    private IPAddressIntelligenceResponse.IPAddressIntelligenceStatus parseStatus(String statusStr) {
        if (statusStr == null) {
            return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN;
        }
        
        switch (statusStr.toUpperCase()) {
            case "BLOCKED":
            case "BANNED":
                return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED;
            case "ALLOWED":
            case "CLEAN":
                return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED;
            case "SUSPICIOUS":
            case "RISKY":
                return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS;
            default:
                return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN;
        }
    }
}

Groovy IP Address Intelligence Service

Scriptable intelligence service using Groovy:

package org.apereo.cas.authentication.adaptive.intel;

import org.apereo.cas.util.scripting.ExecutableCompiledGroovyScript;
import org.springframework.webflow.execution.RequestContext;

public class GroovyIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {
    
    private final ExecutableCompiledGroovyScript watchableScript;
    
    public GroovyIPAddressIntelligenceService(ExecutableCompiledGroovyScript watchableScript) {
        this.watchableScript = watchableScript;
    }
    
    @Override
    public IPAddressIntelligenceResponse examine(RequestContext requestContext, 
                                               String clientIpAddress) {
        
        try {
            Object result = watchableScript.execute(requestContext, clientIpAddress, 
                                                  IPAddressIntelligenceResponse.class);
            
            if (result instanceof IPAddressIntelligenceResponse) {
                return (IPAddressIntelligenceResponse) result;
            }
            
        } catch (Exception e) {
            // Log error and return unknown status
        }
        
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,
            0.0,
            Map.of("error", "Groovy script execution failed"));
    }
}

Geographical Location Services

Geo-Location Request

package org.apereo.cas.authentication.adaptive.geo;

public class GeoLocationRequest {
    
    private final String ipAddress;
    private final double latitude;
    private final double longitude;
    private final String userAgent;
    
    public GeoLocationRequest(String ipAddress) {
        this(ipAddress, 0.0, 0.0, null);
    }
    
    public GeoLocationRequest(String ipAddress, double latitude, double longitude, String userAgent) {
        this.ipAddress = ipAddress;
        this.latitude = latitude;
        this.longitude = longitude;
        this.userAgent = userAgent;
    }
    
    public String getIpAddress() { return ipAddress; }
    public double getLatitude() { return latitude; }
    public double getLongitude() { return longitude; }
    public String getUserAgent() { return userAgent; }
}

Geo-Location Response

package org.apereo.cas.authentication.adaptive.geo;

public class GeoLocationResponse {
    
    private final double latitude;
    private final double longitude;
    private final String city;
    private final String region;
    private final String country;
    private final String countryCode;
    private final String timezone;
    private final String organization;
    
    public GeoLocationResponse(double latitude, double longitude, String city, String region,
                             String country, String countryCode, String timezone, String organization) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.city = city;
        this.region = region;
        this.country = country;
        this.countryCode = countryCode;
        this.timezone = timezone;
        this.organization = organization;
    }
    
    // Getters
    public double getLatitude() { return latitude; }
    public double getLongitude() { return longitude; }
    public String getCity() { return city; }
    public String getRegion() { return region; }
    public String getCountry() { return country; }
    public String getCountryCode() { return countryCode; }
    public String getTimezone() { return timezone; }
    public String getOrganization() { return organization; }
}

Geo-Location Service

package org.apereo.cas.authentication.adaptive.geo;

public interface GeoLocationService {
    GeoLocationResponse locate(GeoLocationRequest request) throws Exception;
    GeoLocationResponse locate(String ipAddress) throws Exception;
}

Configuration Properties

Adaptive Authentication Properties

package org.apereo.cas.configuration.model.core.authentication;

import java.time.LocalTime;
import java.util.Set;

public class AdaptiveAuthenticationProperties implements Serializable {
    
    private boolean enabled = false;
    private Set<String> rejectCountries = new LinkedHashSet<>();
    private Set<String> rejectBrowsers = new LinkedHashSet<>();
    private Set<String> rejectIpAddresses = new LinkedHashSet<>();
    private Set<String> rejectUserAgents = new LinkedHashSet<>();
    
    private TimePeriod requireTimePeriod = new TimePeriod();
    private IPIntelligence ipIntelligence = new IPIntelligence();
    private GeoLocation geoLocation = new GeoLocation();
    
    // Getters and setters
    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    
    public Set<String> getRejectCountries() { return rejectCountries; }
    public void setRejectCountries(Set<String> rejectCountries) { this.rejectCountries = rejectCountries; }
    
    // Additional getters and setters...
    
    public static class TimePeriod implements Serializable {
        private LocalTime startTime;
        private LocalTime endTime;
        
        public LocalTime getStartTime() { return startTime; }
        public void setStartTime(LocalTime startTime) { this.startTime = startTime; }
        
        public LocalTime getEndTime() { return endTime; }
        public void setEndTime(LocalTime endTime) { this.endTime = endTime; }
    }
    
    public static class IPIntelligence implements Serializable {
        private boolean enabled = false;
        private String provider = "DEFAULT";
        private String endpoint;
        private String apiKey;
        private double riskThreshold = 0.7;
        
        // Getters and setters
        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean enabled) { this.enabled = enabled; }
        
        public double getRiskThreshold() { return riskThreshold; }
        public void setRiskThreshold(double riskThreshold) { this.riskThreshold = riskThreshold; }
        
        // Additional getters and setters...
    }
    
    public static class GeoLocation implements Serializable {
        private boolean enabled = false;
        private String provider = "DEFAULT";
        private String endpoint;
        private String apiKey;
        
        // Getters and setters...
    }
}

Integration Examples

Spring Configuration

@Configuration
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class AdaptiveAuthenticationConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public IPAddressIntelligenceService ipAddressIntelligenceService(
        CasConfigurationProperties casProperties) {
        
        AdaptiveAuthenticationProperties.IPIntelligence props = 
            casProperties.getAuthn().getAdaptive().getIpIntelligence();
            
        if (!props.isEnabled()) {
            return new DefaultIPAddressIntelligenceService();
        }
        
        switch (props.getProvider().toUpperCase()) {
            case "BLACKDOT":
                return new BlackDotIPAddressIntelligenceService(
                    new RestTemplate(), props.getEndpoint(), props.getApiKey());
            case "REST":
                return new RestfulIPAddressIntelligenceService(
                    new RestTemplate(), props.getEndpoint(), createHeaders(props));
            default:
                return new DefaultIPAddressIntelligenceService();
        }
    }
    
    @Bean
    @ConditionalOnMissingBean
    public AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy(
        GeoLocationService geoLocationService,
        IPAddressIntelligenceService ipAddressIntelligenceService,
        CasConfigurationProperties casProperties) {
        
        return new DefaultAdaptiveAuthenticationPolicy(
            geoLocationService,
            ipAddressIntelligenceService,
            casProperties.getAuthn().getAdaptive());
    }
    
    private HttpHeaders createHeaders(AdaptiveAuthenticationProperties.IPIntelligence props) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + props.getApiKey());
        headers.set("Content-Type", "application/json");
        return headers;
    }
}

Programmatic Usage

// Create adaptive authentication policy
AdaptiveAuthenticationProperties properties = new AdaptiveAuthenticationProperties();
properties.setEnabled(true);
properties.getRejectCountries().addAll(Set.of("XX", "YY"));
properties.getRejectUserAgents().add(".*bot.*");
properties.getIpIntelligence().setEnabled(true);
properties.getIpIntelligence().setRiskThreshold(0.8);

IPAddressIntelligenceService intelligenceService = new DefaultIPAddressIntelligenceService();
GeoLocationService geoLocationService = new DefaultGeoLocationService();

AdaptiveAuthenticationPolicy policy = new DefaultAdaptiveAuthenticationPolicy(
    geoLocationService, intelligenceService, properties);

// Use in authentication flow
RequestContext requestContext = // ... get from WebFlow
String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
GeoLocationRequest geoRequest = new GeoLocationRequest("203.0.113.1");

boolean allowed = policy.isAuthenticationRequestAllowed(requestContext, userAgent, geoRequest);

if (!allowed) {
    // Block authentication or require additional verification
    throw new AdaptiveAuthenticationException("Authentication request blocked by adaptive policy");
}

// Custom IP intelligence implementation
IPAddressIntelligenceService customService = new IPAddressIntelligenceService() {
    @Override
    public IPAddressIntelligenceResponse examine(RequestContext requestContext, String clientIpAddress) {
        // Custom threat intelligence logic
        if (isKnownMaliciousIp(clientIpAddress)) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED,
                1.0,
                Map.of("reason", "Known malicious IP")
            );
        }
        
        if (isHighRiskCountry(clientIpAddress)) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS,
                0.6,
                Map.of("reason", "High-risk country")
            );
        }
        
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,
            0.1,
            Map.of("reason", "Clean IP")
        );
    }
    
    private boolean isKnownMaliciousIp(String ip) {
        // Check against threat intelligence feeds
        return false;
    }
    
    private boolean isHighRiskCountry(String ip) {
        // Geo-location and country risk assessment
        return false;
    }
};

// Groovy-based adaptive policy
String groovyScript = """
    import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceResponse
    
    def examine(requestContext, clientIpAddress) {
        // Custom Groovy intelligence logic
        if (clientIpAddress.startsWith("10.")) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,
                0.0,
                [reason: "Internal IP"]
            )
        }
        
        if (clientIpAddress.contains("suspicious")) {
            return new IPAddressIntelligenceResponse(
                IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED,
                0.9,
                [reason: "Suspicious pattern detected"]
            )
        }
        
        return new IPAddressIntelligenceResponse(
            IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,
            0.3,
            [reason: "Unknown IP"]
        )
    }
    
    examine(binding.variables.requestContext, binding.variables.clientIpAddress)
""";

ExecutableCompiledGroovyScript script = new ExecutableCompiledGroovyScript(groovyScript);
GroovyIPAddressIntelligenceService groovyService = new GroovyIPAddressIntelligenceService(script);

Risk-Based Authentication Flow

public class RiskBasedAuthenticationHandler extends AbstractAuthenticationHandler {
    
    private final AdaptiveAuthenticationPolicy adaptivePolicy;
    private final AuthenticationHandler delegateHandler;
    
    public RiskBasedAuthenticationHandler(AdaptiveAuthenticationPolicy adaptivePolicy,
                                        AuthenticationHandler delegateHandler) {
        super("RiskBasedHandler", null, new DefaultPrincipalFactory(), 0);
        this.adaptivePolicy = adaptivePolicy;
        this.delegateHandler = delegateHandler;
    }
    
    @Override
    public AuthenticationHandlerExecutionResult authenticate(Credential credential) 
        throws GeneralSecurityException, PreventedException {
        
        RequestContext requestContext = RequestContextHolder.getRequestContext();
        String userAgent = extractUserAgent(requestContext);
        GeoLocationRequest geoRequest = createGeoLocationRequest(requestContext);
        
        try {
            boolean allowed = adaptivePolicy.isAuthenticationRequestAllowed(
                requestContext, userAgent, geoRequest);
                
            if (!allowed) {
                // Risk too high - require additional authentication
                throw new MultiFactorAuthenticationRequiredException(
                    "Additional authentication required due to risk assessment");
            }
            
            // Proceed with normal authentication
            return delegateHandler.authenticate(credential);
            
        } catch (Exception e) {
            throw new PreventedException("Adaptive authentication failed", e);
        }
    }
    
    @Override
    public boolean supports(Credential credential) {
        return delegateHandler.supports(credential);
    }
    
    private String extractUserAgent(RequestContext requestContext) {
        return requestContext.getExternalContext().getNativeRequest().getHeader("User-Agent");
    }
    
    private GeoLocationRequest createGeoLocationRequest(RequestContext requestContext) {
        ClientInfo clientInfo = ClientInfoHolder.getClientInfo();
        return new GeoLocationRequest(clientInfo.getClientIpAddress());
    }
}

Adaptive authentication provides sophisticated risk assessment capabilities that enhance security by dynamically adjusting authentication requirements based on contextual factors, threat intelligence, and behavioral analysis.

Install with Tessl CLI

npx tessl i tessl/maven-org-apereo-cas--cas-server-core-authentication-api

docs

adaptive-authentication.md

authentication-handlers.md

authentication-policies.md

credential-handling.md

index.md

password-policies.md

principal-resolution.md

tile.json