Core authentication API module for Apereo CAS providing fundamental authentication interfaces, implementations, and components for the CAS server infrastructure
—
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.
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;
}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;
}
}package org.apereo.cas.authentication.adaptive.intel;
import org.springframework.webflow.execution.RequestContext;
public interface IPAddressIntelligenceService {
IPAddressIntelligenceResponse examine(RequestContext requestContext, String clientIpAddress);
}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;
}
}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);
}
}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);
}
}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"));
}
}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;
}
}
}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"));
}
}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; }
}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; }
}package org.apereo.cas.authentication.adaptive.geo;
public interface GeoLocationService {
GeoLocationResponse locate(GeoLocationRequest request) throws Exception;
GeoLocationResponse locate(String ipAddress) throws Exception;
}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...
}
}@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;
}
}// 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);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