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

authentication-policies.mddocs/

0

# Authentication Policies

1

2

Authentication policies define the rules and requirements that must be satisfied for an authentication attempt to be considered successful. The CAS authentication API provides a comprehensive set of configurable policies that can be combined to implement complex authentication requirements.

3

4

## Core Policy Interface

5

6

```java { .api }

7

package org.apereo.cas.authentication;

8

9

public interface AuthenticationPolicy {

10

AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) throws Exception;

11

boolean shouldResumeOnFailure(Throwable failure);

12

String getName();

13

void setName(String name);

14

int getOrder();

15

void setOrder(int order);

16

}

17

```

18

19

## Policy Execution Result

20

21

```java { .api }

22

package org.apereo.cas.authentication;

23

24

import java.util.Optional;

25

26

public interface AuthenticationPolicyExecutionResult {

27

boolean isSuccess();

28

Optional<Throwable> getFailure();

29

30

static AuthenticationPolicyExecutionResult success() {

31

return new DefaultAuthenticationPolicyExecutionResult(true, null);

32

}

33

34

static AuthenticationPolicyExecutionResult failure(Throwable failure) {

35

return new DefaultAuthenticationPolicyExecutionResult(false, failure);

36

}

37

}

38

39

class DefaultAuthenticationPolicyExecutionResult implements AuthenticationPolicyExecutionResult {

40

private final boolean success;

41

private final Throwable failure;

42

43

public DefaultAuthenticationPolicyExecutionResult(boolean success, Throwable failure) {

44

this.success = success;

45

this.failure = failure;

46

}

47

48

public boolean isSuccess() { return success; }

49

public Optional<Throwable> getFailure() { return Optional.ofNullable(failure); }

50

}

51

```

52

53

## Base Authentication Policy

54

55

```java { .api }

56

package org.apereo.cas.authentication.policy;

57

58

import org.apereo.cas.authentication.AuthenticationPolicy;

59

import org.springframework.core.Ordered;

60

61

public abstract class BaseAuthenticationPolicy implements AuthenticationPolicy {

62

private int order = Ordered.LOWEST_PRECEDENCE;

63

private String name = getClass().getSimpleName();

64

65

public int getOrder() { return order; }

66

public void setOrder(int order) { this.order = order; }

67

public String getName() { return name; }

68

public void setName(String name) { this.name = name; }

69

70

public boolean shouldResumeOnFailure(Throwable failure) {

71

return false;

72

}

73

}

74

```

75

76

## Authentication Handler Policies

77

78

### All Authentication Handlers Succeeded Policy

79

80

Requires all configured authentication handlers to succeed:

81

82

```java { .api }

83

package org.apereo.cas.authentication.policy;

84

85

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

86

import org.apereo.cas.authentication.AuthenticationTransaction;

87

88

public class AllAuthenticationHandlersSucceededAuthenticationPolicy

89

extends BaseAuthenticationPolicy {

90

91

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

92

throws Exception {

93

94

Set<AuthenticationHandler> handlers = transaction.getAuthenticationHandlers();

95

Map<String, Throwable> failures = transaction.getFailures();

96

97

if (handlers.isEmpty()) {

98

return AuthenticationPolicyExecutionResult.failure(

99

new AuthenticationException("No authentication handlers configured"));

100

}

101

102

for (AuthenticationHandler handler : handlers) {

103

if (failures.containsKey(handler.getName())) {

104

return AuthenticationPolicyExecutionResult.failure(

105

new AuthenticationException("Handler " + handler.getName() + " failed"));

106

}

107

}

108

109

return AuthenticationPolicyExecutionResult.success();

110

}

111

}

112

```

113

114

### Required Authentication Handler Policy

115

116

Requires specific authentication handlers to succeed:

117

118

```java { .api }

119

package org.apereo.cas.authentication.policy;

120

121

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

122

import org.apereo.cas.authentication.AuthenticationTransaction;

123

import java.util.Set;

124

125

public class RequiredAuthenticationHandlerAuthenticationPolicy

126

extends BaseAuthenticationHandlerAuthenticationPolicy {

127

128

private final Set<String> requiredHandlerNames;

129

private final boolean tryAll;

130

131

public RequiredAuthenticationHandlerAuthenticationPolicy(Set<String> requiredHandlerNames) {

132

this(requiredHandlerNames, false);

133

}

134

135

public RequiredAuthenticationHandlerAuthenticationPolicy(Set<String> requiredHandlerNames,

136

boolean tryAll) {

137

this.requiredHandlerNames = requiredHandlerNames;

138

this.tryAll = tryAll;

139

}

140

141

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

142

throws Exception {

143

144

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

145

146

for (String requiredHandler : requiredHandlerNames) {

147

if (!successes.containsKey(requiredHandler)) {

148

return AuthenticationPolicyExecutionResult.failure(

149

new AuthenticationException("Required handler " + requiredHandler + " did not succeed"));

150

}

151

}

152

153

return AuthenticationPolicyExecutionResult.success();

154

}

155

156

public boolean shouldResumeOnFailure(Throwable failure) {

157

return tryAll;

158

}

159

}

160

```

161

162

### Excluded Authentication Handler Policy

163

164

Excludes specific authentication handlers from being required:

165

166

```java { .api }

167

package org.apereo.cas.authentication.policy;

168

169

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

170

import org.apereo.cas.authentication.AuthenticationTransaction;

171

import java.util.Set;

172

173

public class ExcludedAuthenticationHandlerAuthenticationPolicy

174

extends BaseAuthenticationHandlerAuthenticationPolicy {

175

176

private final Set<String> excludedHandlerNames;

177

178

public ExcludedAuthenticationHandlerAuthenticationPolicy(Set<String> excludedHandlerNames) {

179

this.excludedHandlerNames = excludedHandlerNames;

180

}

181

182

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

183

throws Exception {

184

185

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

186

Map<String, Throwable> failures = transaction.getFailures();

187

188

// Check if any non-excluded handlers succeeded

189

boolean hasNonExcludedSuccess = successes.keySet().stream()

190

.anyMatch(handlerName -> !excludedHandlerNames.contains(handlerName));

191

192

if (hasNonExcludedSuccess) {

193

return AuthenticationPolicyExecutionResult.success();

194

}

195

196

// Check if only excluded handlers failed

197

boolean onlyExcludedFailures = failures.keySet().stream()

198

.allMatch(excludedHandlerNames::contains);

199

200

if (onlyExcludedFailures && !successes.isEmpty()) {

201

return AuthenticationPolicyExecutionResult.success();

202

}

203

204

return AuthenticationPolicyExecutionResult.failure(

205

new AuthenticationException("No non-excluded authentication handlers succeeded"));

206

}

207

}

208

```

209

210

## Credential Validation Policies

211

212

### All Credentials Validated Policy

213

214

Requires all provided credentials to be validated successfully:

215

216

```java { .api }

217

package org.apereo.cas.authentication.policy;

218

219

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

220

import org.apereo.cas.authentication.AuthenticationTransaction;

221

import org.apereo.cas.authentication.Credential;

222

223

public class AllCredentialsValidatedAuthenticationPolicy extends BaseAuthenticationPolicy {

224

225

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

226

throws Exception {

227

228

Collection<Credential> credentials = transaction.getCredentials();

229

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

230

231

if (credentials.isEmpty()) {

232

return AuthenticationPolicyExecutionResult.failure(

233

new AuthenticationException("No credentials provided"));

234

}

235

236

for (Credential credential : credentials) {

237

boolean credentialValidated = successes.values().stream()

238

.anyMatch(result -> result.getCredential().equals(credential));

239

240

if (!credentialValidated) {

241

return AuthenticationPolicyExecutionResult.failure(

242

new AuthenticationException("Credential not validated: " + credential.getId()));

243

}

244

}

245

246

return AuthenticationPolicyExecutionResult.success();

247

}

248

}

249

```

250

251

### At Least One Credential Validated Policy

252

253

Requires at least one credential to be validated successfully:

254

255

```java { .api }

256

package org.apereo.cas.authentication.policy;

257

258

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

259

import org.apereo.cas.authentication.AuthenticationTransaction;

260

261

public class AtLeastOneCredentialValidatedAuthenticationPolicy extends BaseAuthenticationPolicy {

262

263

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

264

throws Exception {

265

266

Collection<Credential> credentials = transaction.getCredentials();

267

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

268

269

if (credentials.isEmpty()) {

270

return AuthenticationPolicyExecutionResult.failure(

271

new AuthenticationException("No credentials provided"));

272

}

273

274

if (successes.isEmpty()) {

275

return AuthenticationPolicyExecutionResult.failure(

276

new AuthenticationException("No authentication handlers succeeded"));

277

}

278

279

// At least one credential was validated if we have successes

280

return AuthenticationPolicyExecutionResult.success();

281

}

282

}

283

```

284

285

## Attribute-Based Policies

286

287

### Required Attributes Policy

288

289

Requires specific attributes to be present in the authentication:

290

291

```java { .api }

292

package org.apereo.cas.authentication.policy;

293

294

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

295

import org.apereo.cas.authentication.AuthenticationTransaction;

296

import org.apereo.cas.authentication.principal.Principal;

297

import java.util.Map;

298

import java.util.Set;

299

300

public class RequiredAttributesAuthenticationPolicy extends BaseAuthenticationPolicy {

301

302

private final Map<String, Set<Object>> requiredAttributes;

303

private final boolean tryAll;

304

305

public RequiredAttributesAuthenticationPolicy(Map<String, Set<Object>> requiredAttributes) {

306

this(requiredAttributes, false);

307

}

308

309

public RequiredAttributesAuthenticationPolicy(Map<String, Set<Object>> requiredAttributes,

310

boolean tryAll) {

311

this.requiredAttributes = requiredAttributes;

312

this.tryAll = tryAll;

313

}

314

315

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

316

throws Exception {

317

318

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

319

320

for (AuthenticationHandlerExecutionResult result : successes.values()) {

321

Principal principal = result.getPrincipal();

322

Map<String, List<Object>> principalAttributes = principal.getAttributes();

323

324

for (Map.Entry<String, Set<Object>> requiredEntry : requiredAttributes.entrySet()) {

325

String attributeName = requiredEntry.getKey();

326

Set<Object> requiredValues = requiredEntry.getValue();

327

328

List<Object> actualValues = principalAttributes.get(attributeName);

329

if (actualValues == null || actualValues.isEmpty()) {

330

return AuthenticationPolicyExecutionResult.failure(

331

new AuthenticationException("Required attribute missing: " + attributeName));

332

}

333

334

if (!requiredValues.isEmpty()) {

335

boolean hasRequiredValue = actualValues.stream()

336

.anyMatch(requiredValues::contains);

337

if (!hasRequiredValue) {

338

return AuthenticationPolicyExecutionResult.failure(

339

new AuthenticationException("Required attribute value not found: " + attributeName));

340

}

341

}

342

}

343

}

344

345

return AuthenticationPolicyExecutionResult.success();

346

}

347

348

public boolean shouldResumeOnFailure(Throwable failure) {

349

return tryAll;

350

}

351

}

352

```

353

354

## Prevention Policies

355

356

### Not Prevented Policy

357

358

Ensures that no authentication attempts are marked as prevented:

359

360

```java { .api }

361

package org.apereo.cas.authentication.policy;

362

363

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

364

import org.apereo.cas.authentication.AuthenticationTransaction;

365

import org.apereo.cas.authentication.PreventedException;

366

367

public class NotPreventedAuthenticationPolicy extends BaseAuthenticationPolicy {

368

369

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

370

throws Exception {

371

372

Map<String, Throwable> failures = transaction.getFailures();

373

374

for (Throwable failure : failures.values()) {

375

if (failure instanceof PreventedException) {

376

return AuthenticationPolicyExecutionResult.failure(failure);

377

}

378

}

379

380

return AuthenticationPolicyExecutionResult.success();

381

}

382

}

383

```

384

385

## Principal Uniqueness Policy

386

387

### Unique Principal Policy

388

389

Ensures that all successful authentications resolve to the same principal:

390

391

```java { .api }

392

package org.apereo.cas.authentication.policy;

393

394

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

395

import org.apereo.cas.authentication.AuthenticationTransaction;

396

import org.apereo.cas.authentication.principal.Principal;

397

import java.util.Set;

398

import java.util.stream.Collectors;

399

400

public class UniquePrincipalAuthenticationPolicy extends BaseAuthenticationPolicy {

401

402

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

403

throws Exception {

404

405

Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();

406

407

if (successes.size() <= 1) {

408

return AuthenticationPolicyExecutionResult.success();

409

}

410

411

Set<String> principalIds = successes.values().stream()

412

.map(AuthenticationHandlerExecutionResult::getPrincipal)

413

.map(Principal::getId)

414

.collect(Collectors.toSet());

415

416

if (principalIds.size() > 1) {

417

return AuthenticationPolicyExecutionResult.failure(

418

new MixedPrincipalException("Multiple principals found: " + principalIds));

419

}

420

421

return AuthenticationPolicyExecutionResult.success();

422

}

423

}

424

```

425

426

## Scriptable Policies

427

428

### Groovy Script Policy

429

430

Allows defining authentication policies using Groovy scripts:

431

432

```java { .api }

433

package org.apereo.cas.authentication.policy;

434

435

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

436

import org.apereo.cas.authentication.AuthenticationTransaction;

437

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

438

439

public class GroovyScriptAuthenticationPolicy extends BaseAuthenticationPolicy {

440

441

private final ExecutableCompiledGroovyScript watchableScript;

442

443

public GroovyScriptAuthenticationPolicy(ExecutableCompiledGroovyScript watchableScript) {

444

this.watchableScript = watchableScript;

445

}

446

447

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

448

throws Exception {

449

450

Object result = watchableScript.execute(transaction,

451

AuthenticationPolicyExecutionResult.class);

452

453

if (result instanceof AuthenticationPolicyExecutionResult) {

454

return (AuthenticationPolicyExecutionResult) result;

455

} else if (result instanceof Boolean) {

456

return (Boolean) result

457

? AuthenticationPolicyExecutionResult.success()

458

: AuthenticationPolicyExecutionResult.failure(

459

new AuthenticationException("Groovy script returned false"));

460

}

461

462

return AuthenticationPolicyExecutionResult.success();

463

}

464

}

465

```

466

467

## RESTful Policies

468

469

### RESTful Authentication Policy

470

471

Delegates policy decisions to external REST services:

472

473

```java { .api }

474

package org.apereo.cas.authentication.policy;

475

476

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;

477

import org.apereo.cas.authentication.AuthenticationTransaction;

478

import org.springframework.http.HttpEntity;

479

import org.springframework.http.HttpMethod;

480

import org.springframework.http.ResponseEntity;

481

import org.springframework.web.client.RestTemplate;

482

import java.util.Map;

483

484

public class RestfulAuthenticationPolicy extends BaseAuthenticationPolicy {

485

486

private final RestTemplate restTemplate;

487

private final String endpoint;

488

489

public RestfulAuthenticationPolicy(RestTemplate restTemplate, String endpoint) {

490

this.restTemplate = restTemplate;

491

this.endpoint = endpoint;

492

}

493

494

public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction)

495

throws Exception {

496

497

try {

498

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

499

"transaction", transaction,

500

"credentials", transaction.getCredentials(),

501

"successes", transaction.getSuccesses(),

502

"failures", transaction.getFailures()

503

);

504

505

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

506

ResponseEntity<Map> response = restTemplate.exchange(

507

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

508

509

Map<String, Object> responseBody = response.getBody();

510

if (responseBody != null) {

511

Boolean success = (Boolean) responseBody.get("success");

512

if (success != null && success) {

513

return AuthenticationPolicyExecutionResult.success();

514

}

515

516

String message = (String) responseBody.get("message");

517

return AuthenticationPolicyExecutionResult.failure(

518

new AuthenticationException(message != null ? message : "REST policy failed"));

519

}

520

521

} catch (Exception e) {

522

return AuthenticationPolicyExecutionResult.failure(

523

new AuthenticationException("REST policy endpoint error", e));

524

}

525

526

return AuthenticationPolicyExecutionResult.failure(

527

new AuthenticationException("REST policy evaluation failed"));

528

}

529

}

530

```

531

532

## Policy Resolvers

533

534

Policy resolvers determine which policies should be applied to authentication transactions:

535

536

```java { .api }

537

package org.apereo.cas.authentication;

538

539

import java.util.Set;

540

541

public interface AuthenticationPolicyResolver {

542

Set<AuthenticationPolicy> resolve(AuthenticationTransaction transaction);

543

boolean supports(AuthenticationTransaction transaction);

544

}

545

```

546

547

### Registered Service Policy Resolver

548

549

Resolves policies based on the registered service configuration:

550

551

```java { .api }

552

package org.apereo.cas.authentication.policy;

553

554

import org.apereo.cas.authentication.AuthenticationPolicy;

555

import org.apereo.cas.authentication.AuthenticationPolicyResolver;

556

import org.apereo.cas.authentication.AuthenticationTransaction;

557

import org.apereo.cas.services.RegisteredService;

558

import org.apereo.cas.services.ServicesManager;

559

import java.util.Set;

560

import java.util.LinkedHashSet;

561

562

public class RegisteredServiceAuthenticationPolicyResolver

563

implements AuthenticationPolicyResolver {

564

565

private final ServicesManager servicesManager;

566

567

public RegisteredServiceAuthenticationPolicyResolver(ServicesManager servicesManager) {

568

this.servicesManager = servicesManager;

569

}

570

571

public Set<AuthenticationPolicy> resolve(AuthenticationTransaction transaction) {

572

Set<AuthenticationPolicy> policies = new LinkedHashSet<>();

573

574

Service service = transaction.getService();

575

if (service != null) {

576

RegisteredService registeredService = servicesManager.findServiceBy(service);

577

if (registeredService != null && registeredService.getAuthenticationPolicy() != null) {

578

579

// Add required handler policy if specified

580

Set<String> requiredHandlers = registeredService.getAuthenticationPolicy()

581

.getRequiredAuthenticationHandlers();

582

if (!requiredHandlers.isEmpty()) {

583

policies.add(new RequiredAuthenticationHandlerAuthenticationPolicy(requiredHandlers));

584

}

585

586

// Add required attribute policy if specified

587

Map<String, Set<Object>> requiredAttributes = registeredService.getAuthenticationPolicy()

588

.getRequiredAttributes();

589

if (!requiredAttributes.isEmpty()) {

590

policies.add(new RequiredAttributesAuthenticationPolicy(requiredAttributes));

591

}

592

}

593

}

594

595

return policies;

596

}

597

598

public boolean supports(AuthenticationTransaction transaction) {

599

return transaction.getService() != null;

600

}

601

}

602

```

603

604

## Configuration Examples

605

606

### Spring Configuration

607

608

```java { .api }

609

@Configuration

610

public class AuthenticationPolicyConfiguration {

611

612

@Bean

613

public AuthenticationPolicy allCredentialsValidatedAuthenticationPolicy() {

614

return new AllCredentialsValidatedAuthenticationPolicy();

615

}

616

617

@Bean

618

public AuthenticationPolicy requiredHandlersAuthenticationPolicy() {

619

Set<String> requiredHandlers = Set.of("LdapAuthenticationHandler", "DatabaseAuthenticationHandler");

620

return new RequiredAuthenticationHandlerAuthenticationPolicy(requiredHandlers);

621

}

622

623

@Bean

624

public AuthenticationPolicy requiredAttributesAuthenticationPolicy() {

625

Map<String, Set<Object>> requiredAttributes = Map.of(

626

"memberOf", Set.of("CN=Users,DC=example,DC=com"),

627

"accountEnabled", Set.of("true")

628

);

629

return new RequiredAttributesAuthenticationPolicy(requiredAttributes);

630

}

631

632

@Bean

633

public AuthenticationPolicyResolver registeredServiceAuthenticationPolicyResolver(

634

ServicesManager servicesManager) {

635

return new RegisteredServiceAuthenticationPolicyResolver(servicesManager);

636

}

637

}

638

```

639

640

### Programmatic Configuration

641

642

```java { .api }

643

// Create policy chain

644

List<AuthenticationPolicy> policies = List.of(

645

new NotPreventedAuthenticationPolicy(),

646

new AtLeastOneCredentialValidatedAuthenticationPolicy(),

647

new UniquePrincipalAuthenticationPolicy()

648

);

649

650

// Register policies in execution plan

651

for (AuthenticationPolicy policy : policies) {

652

authenticationEventExecutionPlan.registerAuthenticationPolicy(policy);

653

}

654

655

// Create Groovy-based policy

656

ExecutableCompiledGroovyScript script = // ... create script

657

GroovyScriptAuthenticationPolicy groovyPolicy = new GroovyScriptAuthenticationPolicy(script);

658

authenticationEventExecutionPlan.registerAuthenticationPolicy(groovyPolicy);

659

660

// Create REST-based policy

661

RestTemplate restTemplate = new RestTemplate();

662

RestfulAuthenticationPolicy restPolicy = new RestfulAuthenticationPolicy(

663

restTemplate, "https://policy.example.com/validate");

664

authenticationEventExecutionPlan.registerAuthenticationPolicy(restPolicy);

665

```

666

667

Authentication policies provide fine-grained control over authentication requirements and enable complex security scenarios by composing multiple policy types to meet specific organizational needs.