or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

adaptive-authentication.mdauthentication-handlers.mdauthentication-policies.mdcredential-handling.mdindex.mdpassword-policies.mdprincipal-resolution.md

adaptive-authentication.mddocs/

0

# Adaptive Authentication

1

2

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.

3

4

## Core Adaptive Authentication Interface

5

6

```java { .api }

7

package org.apereo.cas.authentication.adaptive;

8

9

import org.apereo.cas.authentication.adaptive.geo.GeoLocationRequest;

10

import org.springframework.webflow.execution.RequestContext;

11

12

public interface AdaptiveAuthenticationPolicy {

13

boolean isAuthenticationRequestAllowed(RequestContext requestContext,

14

String userAgent,

15

GeoLocationRequest location) throws Throwable;

16

}

17

```

18

19

## Default Adaptive Authentication Policy

20

21

```java { .api }

22

package org.apereo.cas.authentication.adaptive;

23

24

import org.apereo.cas.authentication.adaptive.geo.GeoLocationRequest;

25

import org.apereo.cas.authentication.adaptive.geo.GeoLocationResponse;

26

import org.apereo.cas.authentication.adaptive.geo.GeoLocationService;

27

import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceService;

28

import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceResponse;

29

import org.apereo.cas.configuration.model.core.authentication.AdaptiveAuthenticationProperties;

30

import org.springframework.webflow.execution.RequestContext;

31

32

public class DefaultAdaptiveAuthenticationPolicy implements AdaptiveAuthenticationPolicy {

33

34

private final GeoLocationService geoLocationService;

35

private final IPAddressIntelligenceService ipAddressIntelligenceService;

36

private final AdaptiveAuthenticationProperties adaptiveAuthenticationProperties;

37

38

public DefaultAdaptiveAuthenticationPolicy(GeoLocationService geoLocationService,

39

IPAddressIntelligenceService ipAddressIntelligenceService,

40

AdaptiveAuthenticationProperties adaptiveAuthenticationProperties) {

41

this.geoLocationService = geoLocationService;

42

this.ipAddressIntelligenceService = ipAddressIntelligenceService;

43

this.adaptiveAuthenticationProperties = adaptiveAuthenticationProperties;

44

}

45

46

@Override

47

public boolean isAuthenticationRequestAllowed(RequestContext requestContext,

48

String userAgent,

49

GeoLocationRequest location) throws Throwable {

50

51

ClientInfo clientInfo = ClientInfoHolder.getClientInfo();

52

if (clientInfo == null || StringUtils.isBlank(userAgent)) {

53

// Allow if no client information available

54

return true;

55

}

56

57

String clientIp = clientInfo.getClientIpAddress();

58

59

// Check IP address restrictions

60

if (isIpAddressRejected(requestContext, clientIp)) {

61

return false;

62

}

63

64

// Check user agent restrictions

65

if (isUserAgentRejected(userAgent)) {

66

return false;

67

}

68

69

// Check geographical location restrictions

70

if (isGeoLocationRejected(location)) {

71

return false;

72

}

73

74

// Check time-based restrictions

75

if (isTimeBasedAuthenticationRejected()) {

76

return false;

77

}

78

79

return true;

80

}

81

82

private boolean isIpAddressRejected(RequestContext requestContext, String clientIp) {

83

// Check rejected IP patterns

84

Set<String> rejectedIps = adaptiveAuthenticationProperties.getRejectIpAddresses();

85

if (rejectedIps.contains(clientIp)) {

86

return true;

87

}

88

89

// Check with IP intelligence service

90

IPAddressIntelligenceResponse intelligence =

91

ipAddressIntelligenceService.examine(requestContext, clientIp);

92

93

if (intelligence != null && intelligence.getScore() > adaptiveAuthenticationProperties.getIpIntelligence().getRiskThreshold()) {

94

return true;

95

}

96

97

return false;

98

}

99

100

private boolean isUserAgentRejected(String userAgent) {

101

Set<String> rejectedUserAgents = adaptiveAuthenticationProperties.getRejectUserAgents();

102

return rejectedUserAgents.stream()

103

.anyMatch(pattern -> RegexUtils.matches(pattern, userAgent));

104

}

105

106

private boolean isGeoLocationRejected(GeoLocationRequest location) {

107

if (location == null || geoLocationService == null) {

108

return false;

109

}

110

111

try {

112

GeoLocationResponse response = geoLocationService.locate(location);

113

114

if (response != null) {

115

String country = response.getCountry();

116

Set<String> rejectedCountries = adaptiveAuthenticationProperties.getRejectCountries();

117

118

if (rejectedCountries.contains(country)) {

119

return true;

120

}

121

}

122

} catch (Exception e) {

123

// Log error but don't reject authentication due to geo-location failure

124

}

125

126

return false;

127

}

128

129

private boolean isTimeBasedAuthenticationRejected() {

130

LocalTime now = LocalTime.now();

131

LocalTime startTime = adaptiveAuthenticationProperties.getRequireTimePeriod().getStartTime();

132

LocalTime endTime = adaptiveAuthenticationProperties.getRequireTimePeriod().getEndTime();

133

134

if (startTime != null && endTime != null) {

135

return now.isBefore(startTime) || now.isAfter(endTime);

136

}

137

138

return false;

139

}

140

}

141

```

142

143

## IP Address Intelligence Services

144

145

### IP Address Intelligence Interface

146

147

```java { .api }

148

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

149

150

import org.springframework.webflow.execution.RequestContext;

151

152

public interface IPAddressIntelligenceService {

153

IPAddressIntelligenceResponse examine(RequestContext requestContext, String clientIpAddress);

154

}

155

```

156

157

### IP Address Intelligence Response

158

159

```java { .api }

160

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

161

162

import java.util.Map;

163

164

public class IPAddressIntelligenceResponse {

165

166

public enum IPAddressIntelligenceStatus {

167

ALLOWED, BLOCKED, SUSPICIOUS, UNKNOWN

168

}

169

170

private final IPAddressIntelligenceStatus status;

171

private final double score;

172

private final Map<String, Object> details;

173

174

public IPAddressIntelligenceResponse(IPAddressIntelligenceStatus status,

175

double score,

176

Map<String, Object> details) {

177

this.status = status;

178

this.score = score;

179

this.details = details != null ? details : Map.of();

180

}

181

182

public IPAddressIntelligenceStatus getStatus() { return status; }

183

public double getScore() { return score; }

184

public Map<String, Object> getDetails() { return details; }

185

186

public boolean isBanned() {

187

return status == IPAddressIntelligenceStatus.BLOCKED;

188

}

189

190

public boolean isAllowed() {

191

return status == IPAddressIntelligenceStatus.ALLOWED;

192

}

193

194

public boolean isSuspicious() {

195

return status == IPAddressIntelligenceStatus.SUSPICIOUS;

196

}

197

}

198

```

199

200

### Base IP Address Intelligence Service

201

202

```java { .api }

203

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

204

205

import org.springframework.webflow.execution.RequestContext;

206

207

public abstract class BaseIPAddressIntelligenceService implements IPAddressIntelligenceService {

208

209

protected boolean isPrivateIpAddress(String ipAddress) {

210

return ipAddress.startsWith("10.") ||

211

ipAddress.startsWith("192.168.") ||

212

ipAddress.startsWith("172.") ||

213

ipAddress.equals("127.0.0.1") ||

214

ipAddress.equals("localhost");

215

}

216

217

protected double calculateRiskScore(Map<String, Object> factors) {

218

double score = 0.0;

219

220

// Example risk scoring logic

221

if (factors.containsKey("knownMalicious") && (Boolean) factors.get("knownMalicious")) {

222

score += 0.8;

223

}

224

225

if (factors.containsKey("recentActivity")) {

226

Integer activityCount = (Integer) factors.get("recentActivity");

227

if (activityCount > 100) {

228

score += 0.3;

229

}

230

}

231

232

if (factors.containsKey("geoAnomaly") && (Boolean) factors.get("geoAnomaly")) {

233

score += 0.4;

234

}

235

236

return Math.min(1.0, score);

237

}

238

}

239

```

240

241

### Default IP Address Intelligence Service

242

243

```java { .api }

244

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

245

246

import org.springframework.webflow.execution.RequestContext;

247

import java.util.Map;

248

249

public class DefaultIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {

250

251

private final Map<String, IPAddressIntelligenceResponse.IPAddressIntelligenceStatus> knownIpAddresses;

252

253

public DefaultIPAddressIntelligenceService() {

254

this.knownIpAddresses = new ConcurrentHashMap<>();

255

}

256

257

@Override

258

public IPAddressIntelligenceResponse examine(RequestContext requestContext,

259

String clientIpAddress) {

260

261

// Check private/local addresses

262

if (isPrivateIpAddress(clientIpAddress)) {

263

return new IPAddressIntelligenceResponse(

264

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,

265

0.0,

266

Map.of("reason", "private IP address"));

267

}

268

269

// Check known IP addresses

270

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus knownStatus =

271

knownIpAddresses.get(clientIpAddress);

272

273

if (knownStatus != null) {

274

double score = knownStatus == IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED ? 1.0 : 0.0;

275

return new IPAddressIntelligenceResponse(knownStatus, score, Map.of("source", "known addresses"));

276

}

277

278

// Default to unknown/allowed

279

return new IPAddressIntelligenceResponse(

280

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,

281

0.0,

282

Map.of("reason", "no intelligence available"));

283

}

284

285

public void addKnownIpAddress(String ipAddress,

286

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status) {

287

knownIpAddresses.put(ipAddress, status);

288

}

289

290

public void removeKnownIpAddress(String ipAddress) {

291

knownIpAddresses.remove(ipAddress);

292

}

293

}

294

```

295

296

### BlackDot IP Address Intelligence Service

297

298

Service that integrates with BlackDot threat intelligence:

299

300

```java { .api }

301

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

302

303

import org.springframework.webflow.execution.RequestContext;

304

import org.springframework.web.client.RestTemplate;

305

import java.util.Map;

306

307

public class BlackDotIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {

308

309

private final RestTemplate restTemplate;

310

private final String apiEndpoint;

311

private final String apiKey;

312

313

public BlackDotIPAddressIntelligenceService(RestTemplate restTemplate,

314

String apiEndpoint,

315

String apiKey) {

316

this.restTemplate = restTemplate;

317

this.apiEndpoint = apiEndpoint;

318

this.apiKey = apiKey;

319

}

320

321

@Override

322

public IPAddressIntelligenceResponse examine(RequestContext requestContext,

323

String clientIpAddress) {

324

325

if (isPrivateIpAddress(clientIpAddress)) {

326

return new IPAddressIntelligenceResponse(

327

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,

328

0.0,

329

Map.of("reason", "private IP address"));

330

}

331

332

try {

333

// Call BlackDot API

334

Map<String, Object> request = Map.of(

335

"ip", clientIpAddress,

336

"apiKey", apiKey

337

);

338

339

Map<String, Object> response = restTemplate.postForObject(

340

apiEndpoint + "/check", request, Map.class);

341

342

if (response != null) {

343

Boolean isBlacklisted = (Boolean) response.get("blacklisted");

344

Double riskScore = (Double) response.get("riskScore");

345

346

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status;

347

if (isBlacklisted != null && isBlacklisted) {

348

status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED;

349

} else if (riskScore != null && riskScore > 0.5) {

350

status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS;

351

} else {

352

status = IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED;

353

}

354

355

return new IPAddressIntelligenceResponse(status, riskScore != null ? riskScore : 0.0, response);

356

}

357

358

} catch (Exception e) {

359

// Log error and return unknown status

360

}

361

362

return new IPAddressIntelligenceResponse(

363

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,

364

0.0,

365

Map.of("error", "BlackDot API unavailable"));

366

}

367

}

368

```

369

370

### RESTful IP Address Intelligence Service

371

372

Generic REST-based intelligence service:

373

374

```java { .api }

375

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

376

377

import org.springframework.webflow.execution.RequestContext;

378

import org.springframework.web.client.RestTemplate;

379

import org.springframework.http.HttpEntity;

380

import org.springframework.http.HttpHeaders;

381

import org.springframework.http.HttpMethod;

382

import java.util.Map;

383

384

public class RestfulIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {

385

386

private final RestTemplate restTemplate;

387

private final String endpoint;

388

private final HttpHeaders headers;

389

390

public RestfulIPAddressIntelligenceService(RestTemplate restTemplate,

391

String endpoint,

392

HttpHeaders headers) {

393

this.restTemplate = restTemplate;

394

this.endpoint = endpoint;

395

this.headers = headers;

396

}

397

398

@Override

399

public IPAddressIntelligenceResponse examine(RequestContext requestContext,

400

String clientIpAddress) {

401

402

if (isPrivateIpAddress(clientIpAddress)) {

403

return new IPAddressIntelligenceResponse(

404

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,

405

0.0,

406

Map.of("reason", "private IP address"));

407

}

408

409

try {

410

Map<String, Object> requestBody = Map.of(

411

"ipAddress", clientIpAddress,

412

"context", extractContextFromRequest(requestContext)

413

);

414

415

HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);

416

417

Map<String, Object> response = restTemplate.exchange(

418

endpoint, HttpMethod.POST, entity, Map.class).getBody();

419

420

if (response != null) {

421

String statusStr = (String) response.get("status");

422

Double score = (Double) response.get("score");

423

424

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus status =

425

parseStatus(statusStr);

426

427

return new IPAddressIntelligenceResponse(status, score != null ? score : 0.0, response);

428

}

429

430

} catch (Exception e) {

431

// Log error and return unknown status

432

}

433

434

return new IPAddressIntelligenceResponse(

435

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,

436

0.0,

437

Map.of("error", "REST service unavailable"));

438

}

439

440

private Map<String, Object> extractContextFromRequest(RequestContext requestContext) {

441

// Extract additional context information

442

return Map.of(

443

"userAgent", requestContext.getExternalContext().getNativeRequest().getHeader("User-Agent"),

444

"timestamp", System.currentTimeMillis(),

445

"sessionId", requestContext.getFlowScope().getString("sessionId")

446

);

447

}

448

449

private IPAddressIntelligenceResponse.IPAddressIntelligenceStatus parseStatus(String statusStr) {

450

if (statusStr == null) {

451

return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN;

452

}

453

454

switch (statusStr.toUpperCase()) {

455

case "BLOCKED":

456

case "BANNED":

457

return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED;

458

case "ALLOWED":

459

case "CLEAN":

460

return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED;

461

case "SUSPICIOUS":

462

case "RISKY":

463

return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS;

464

default:

465

return IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN;

466

}

467

}

468

}

469

```

470

471

### Groovy IP Address Intelligence Service

472

473

Scriptable intelligence service using Groovy:

474

475

```java { .api }

476

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

477

478

import org.apereo.cas.util.scripting.ExecutableCompiledGroovyScript;

479

import org.springframework.webflow.execution.RequestContext;

480

481

public class GroovyIPAddressIntelligenceService extends BaseIPAddressIntelligenceService {

482

483

private final ExecutableCompiledGroovyScript watchableScript;

484

485

public GroovyIPAddressIntelligenceService(ExecutableCompiledGroovyScript watchableScript) {

486

this.watchableScript = watchableScript;

487

}

488

489

@Override

490

public IPAddressIntelligenceResponse examine(RequestContext requestContext,

491

String clientIpAddress) {

492

493

try {

494

Object result = watchableScript.execute(requestContext, clientIpAddress,

495

IPAddressIntelligenceResponse.class);

496

497

if (result instanceof IPAddressIntelligenceResponse) {

498

return (IPAddressIntelligenceResponse) result;

499

}

500

501

} catch (Exception e) {

502

// Log error and return unknown status

503

}

504

505

return new IPAddressIntelligenceResponse(

506

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,

507

0.0,

508

Map.of("error", "Groovy script execution failed"));

509

}

510

}

511

```

512

513

## Geographical Location Services

514

515

### Geo-Location Request

516

517

```java { .api }

518

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

519

520

public class GeoLocationRequest {

521

522

private final String ipAddress;

523

private final double latitude;

524

private final double longitude;

525

private final String userAgent;

526

527

public GeoLocationRequest(String ipAddress) {

528

this(ipAddress, 0.0, 0.0, null);

529

}

530

531

public GeoLocationRequest(String ipAddress, double latitude, double longitude, String userAgent) {

532

this.ipAddress = ipAddress;

533

this.latitude = latitude;

534

this.longitude = longitude;

535

this.userAgent = userAgent;

536

}

537

538

public String getIpAddress() { return ipAddress; }

539

public double getLatitude() { return latitude; }

540

public double getLongitude() { return longitude; }

541

public String getUserAgent() { return userAgent; }

542

}

543

```

544

545

### Geo-Location Response

546

547

```java { .api }

548

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

549

550

public class GeoLocationResponse {

551

552

private final double latitude;

553

private final double longitude;

554

private final String city;

555

private final String region;

556

private final String country;

557

private final String countryCode;

558

private final String timezone;

559

private final String organization;

560

561

public GeoLocationResponse(double latitude, double longitude, String city, String region,

562

String country, String countryCode, String timezone, String organization) {

563

this.latitude = latitude;

564

this.longitude = longitude;

565

this.city = city;

566

this.region = region;

567

this.country = country;

568

this.countryCode = countryCode;

569

this.timezone = timezone;

570

this.organization = organization;

571

}

572

573

// Getters

574

public double getLatitude() { return latitude; }

575

public double getLongitude() { return longitude; }

576

public String getCity() { return city; }

577

public String getRegion() { return region; }

578

public String getCountry() { return country; }

579

public String getCountryCode() { return countryCode; }

580

public String getTimezone() { return timezone; }

581

public String getOrganization() { return organization; }

582

}

583

```

584

585

### Geo-Location Service

586

587

```java { .api }

588

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

589

590

public interface GeoLocationService {

591

GeoLocationResponse locate(GeoLocationRequest request) throws Exception;

592

GeoLocationResponse locate(String ipAddress) throws Exception;

593

}

594

```

595

596

## Configuration Properties

597

598

### Adaptive Authentication Properties

599

600

```java { .api }

601

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

602

603

import java.time.LocalTime;

604

import java.util.Set;

605

606

public class AdaptiveAuthenticationProperties implements Serializable {

607

608

private boolean enabled = false;

609

private Set<String> rejectCountries = new LinkedHashSet<>();

610

private Set<String> rejectBrowsers = new LinkedHashSet<>();

611

private Set<String> rejectIpAddresses = new LinkedHashSet<>();

612

private Set<String> rejectUserAgents = new LinkedHashSet<>();

613

614

private TimePeriod requireTimePeriod = new TimePeriod();

615

private IPIntelligence ipIntelligence = new IPIntelligence();

616

private GeoLocation geoLocation = new GeoLocation();

617

618

// Getters and setters

619

public boolean isEnabled() { return enabled; }

620

public void setEnabled(boolean enabled) { this.enabled = enabled; }

621

622

public Set<String> getRejectCountries() { return rejectCountries; }

623

public void setRejectCountries(Set<String> rejectCountries) { this.rejectCountries = rejectCountries; }

624

625

// Additional getters and setters...

626

627

public static class TimePeriod implements Serializable {

628

private LocalTime startTime;

629

private LocalTime endTime;

630

631

public LocalTime getStartTime() { return startTime; }

632

public void setStartTime(LocalTime startTime) { this.startTime = startTime; }

633

634

public LocalTime getEndTime() { return endTime; }

635

public void setEndTime(LocalTime endTime) { this.endTime = endTime; }

636

}

637

638

public static class IPIntelligence implements Serializable {

639

private boolean enabled = false;

640

private String provider = "DEFAULT";

641

private String endpoint;

642

private String apiKey;

643

private double riskThreshold = 0.7;

644

645

// Getters and setters

646

public boolean isEnabled() { return enabled; }

647

public void setEnabled(boolean enabled) { this.enabled = enabled; }

648

649

public double getRiskThreshold() { return riskThreshold; }

650

public void setRiskThreshold(double riskThreshold) { this.riskThreshold = riskThreshold; }

651

652

// Additional getters and setters...

653

}

654

655

public static class GeoLocation implements Serializable {

656

private boolean enabled = false;

657

private String provider = "DEFAULT";

658

private String endpoint;

659

private String apiKey;

660

661

// Getters and setters...

662

}

663

}

664

```

665

666

## Integration Examples

667

668

### Spring Configuration

669

670

```java { .api }

671

@Configuration

672

@EnableConfigurationProperties(CasConfigurationProperties.class)

673

public class AdaptiveAuthenticationConfiguration {

674

675

@Bean

676

@ConditionalOnMissingBean

677

public IPAddressIntelligenceService ipAddressIntelligenceService(

678

CasConfigurationProperties casProperties) {

679

680

AdaptiveAuthenticationProperties.IPIntelligence props =

681

casProperties.getAuthn().getAdaptive().getIpIntelligence();

682

683

if (!props.isEnabled()) {

684

return new DefaultIPAddressIntelligenceService();

685

}

686

687

switch (props.getProvider().toUpperCase()) {

688

case "BLACKDOT":

689

return new BlackDotIPAddressIntelligenceService(

690

new RestTemplate(), props.getEndpoint(), props.getApiKey());

691

case "REST":

692

return new RestfulIPAddressIntelligenceService(

693

new RestTemplate(), props.getEndpoint(), createHeaders(props));

694

default:

695

return new DefaultIPAddressIntelligenceService();

696

}

697

}

698

699

@Bean

700

@ConditionalOnMissingBean

701

public AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy(

702

GeoLocationService geoLocationService,

703

IPAddressIntelligenceService ipAddressIntelligenceService,

704

CasConfigurationProperties casProperties) {

705

706

return new DefaultAdaptiveAuthenticationPolicy(

707

geoLocationService,

708

ipAddressIntelligenceService,

709

casProperties.getAuthn().getAdaptive());

710

}

711

712

private HttpHeaders createHeaders(AdaptiveAuthenticationProperties.IPIntelligence props) {

713

HttpHeaders headers = new HttpHeaders();

714

headers.set("Authorization", "Bearer " + props.getApiKey());

715

headers.set("Content-Type", "application/json");

716

return headers;

717

}

718

}

719

```

720

721

### Programmatic Usage

722

723

```java { .api }

724

// Create adaptive authentication policy

725

AdaptiveAuthenticationProperties properties = new AdaptiveAuthenticationProperties();

726

properties.setEnabled(true);

727

properties.getRejectCountries().addAll(Set.of("XX", "YY"));

728

properties.getRejectUserAgents().add(".*bot.*");

729

properties.getIpIntelligence().setEnabled(true);

730

properties.getIpIntelligence().setRiskThreshold(0.8);

731

732

IPAddressIntelligenceService intelligenceService = new DefaultIPAddressIntelligenceService();

733

GeoLocationService geoLocationService = new DefaultGeoLocationService();

734

735

AdaptiveAuthenticationPolicy policy = new DefaultAdaptiveAuthenticationPolicy(

736

geoLocationService, intelligenceService, properties);

737

738

// Use in authentication flow

739

RequestContext requestContext = // ... get from WebFlow

740

String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";

741

GeoLocationRequest geoRequest = new GeoLocationRequest("203.0.113.1");

742

743

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

744

745

if (!allowed) {

746

// Block authentication or require additional verification

747

throw new AdaptiveAuthenticationException("Authentication request blocked by adaptive policy");

748

}

749

750

// Custom IP intelligence implementation

751

IPAddressIntelligenceService customService = new IPAddressIntelligenceService() {

752

@Override

753

public IPAddressIntelligenceResponse examine(RequestContext requestContext, String clientIpAddress) {

754

// Custom threat intelligence logic

755

if (isKnownMaliciousIp(clientIpAddress)) {

756

return new IPAddressIntelligenceResponse(

757

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED,

758

1.0,

759

Map.of("reason", "Known malicious IP")

760

);

761

}

762

763

if (isHighRiskCountry(clientIpAddress)) {

764

return new IPAddressIntelligenceResponse(

765

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.SUSPICIOUS,

766

0.6,

767

Map.of("reason", "High-risk country")

768

);

769

}

770

771

return new IPAddressIntelligenceResponse(

772

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,

773

0.1,

774

Map.of("reason", "Clean IP")

775

);

776

}

777

778

private boolean isKnownMaliciousIp(String ip) {

779

// Check against threat intelligence feeds

780

return false;

781

}

782

783

private boolean isHighRiskCountry(String ip) {

784

// Geo-location and country risk assessment

785

return false;

786

}

787

};

788

789

// Groovy-based adaptive policy

790

String groovyScript = """

791

import org.apereo.cas.authentication.adaptive.intel.IPAddressIntelligenceResponse

792

793

def examine(requestContext, clientIpAddress) {

794

// Custom Groovy intelligence logic

795

if (clientIpAddress.startsWith("10.")) {

796

return new IPAddressIntelligenceResponse(

797

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.ALLOWED,

798

0.0,

799

[reason: "Internal IP"]

800

)

801

}

802

803

if (clientIpAddress.contains("suspicious")) {

804

return new IPAddressIntelligenceResponse(

805

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.BLOCKED,

806

0.9,

807

[reason: "Suspicious pattern detected"]

808

)

809

}

810

811

return new IPAddressIntelligenceResponse(

812

IPAddressIntelligenceResponse.IPAddressIntelligenceStatus.UNKNOWN,

813

0.3,

814

[reason: "Unknown IP"]

815

)

816

}

817

818

examine(binding.variables.requestContext, binding.variables.clientIpAddress)

819

""";

820

821

ExecutableCompiledGroovyScript script = new ExecutableCompiledGroovyScript(groovyScript);

822

GroovyIPAddressIntelligenceService groovyService = new GroovyIPAddressIntelligenceService(script);

823

```

824

825

### Risk-Based Authentication Flow

826

827

```java { .api }

828

public class RiskBasedAuthenticationHandler extends AbstractAuthenticationHandler {

829

830

private final AdaptiveAuthenticationPolicy adaptivePolicy;

831

private final AuthenticationHandler delegateHandler;

832

833

public RiskBasedAuthenticationHandler(AdaptiveAuthenticationPolicy adaptivePolicy,

834

AuthenticationHandler delegateHandler) {

835

super("RiskBasedHandler", null, new DefaultPrincipalFactory(), 0);

836

this.adaptivePolicy = adaptivePolicy;

837

this.delegateHandler = delegateHandler;

838

}

839

840

@Override

841

public AuthenticationHandlerExecutionResult authenticate(Credential credential)

842

throws GeneralSecurityException, PreventedException {

843

844

RequestContext requestContext = RequestContextHolder.getRequestContext();

845

String userAgent = extractUserAgent(requestContext);

846

GeoLocationRequest geoRequest = createGeoLocationRequest(requestContext);

847

848

try {

849

boolean allowed = adaptivePolicy.isAuthenticationRequestAllowed(

850

requestContext, userAgent, geoRequest);

851

852

if (!allowed) {

853

// Risk too high - require additional authentication

854

throw new MultiFactorAuthenticationRequiredException(

855

"Additional authentication required due to risk assessment");

856

}

857

858

// Proceed with normal authentication

859

return delegateHandler.authenticate(credential);

860

861

} catch (Exception e) {

862

throw new PreventedException("Adaptive authentication failed", e);

863

}

864

}

865

866

@Override

867

public boolean supports(Credential credential) {

868

return delegateHandler.supports(credential);

869

}

870

871

private String extractUserAgent(RequestContext requestContext) {

872

return requestContext.getExternalContext().getNativeRequest().getHeader("User-Agent");

873

}

874

875

private GeoLocationRequest createGeoLocationRequest(RequestContext requestContext) {

876

ClientInfo clientInfo = ClientInfoHolder.getClientInfo();

877

return new GeoLocationRequest(clientInfo.getClientIpAddress());

878

}

879

}

880

```

881

882

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