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

credential-handling.mddocs/

0

# Credential Handling

1

2

Credentials represent the authentication information provided by users, such as usernames/passwords, certificates, tokens, or other authentication artifacts. The CAS authentication API provides a comprehensive framework for handling various credential types with validation, security, and extensibility features.

3

4

## Core Credential Interface

5

6

```java { .api }

7

package org.apereo.cas.authentication;

8

9

import java.io.Serializable;

10

11

public interface Credential extends Serializable {

12

String getId();

13

CredentialMetadata getCredentialMetadata();

14

}

15

```

16

17

## Mutable Credential Interface

18

19

```java { .api }

20

package org.apereo.cas.authentication;

21

22

public interface MutableCredential extends Credential {

23

void setId(String id);

24

}

25

```

26

27

## Credential Metadata

28

29

```java { .api }

30

package org.apereo.cas.authentication;

31

32

import java.time.ZonedDateTime;

33

import java.util.Map;

34

35

public interface CredentialMetadata {

36

String getId();

37

Map<String, Object> getProperties();

38

void addProperty(String name, Object value);

39

ZonedDateTime getAuthenticationDate();

40

void setAuthenticationDate(ZonedDateTime authenticationDate);

41

Class<? extends Credential> getCredentialClass();

42

}

43

```

44

45

## Abstract Credential Base Class

46

47

```java { .api }

48

package org.apereo.cas.authentication.credential;

49

50

import org.apereo.cas.authentication.Credential;

51

import org.apereo.cas.authentication.CredentialMetadata;

52

import org.apereo.cas.authentication.metadata.BasicCredentialMetadata;

53

import org.springframework.binding.validation.ValidationContext;

54

55

public abstract class AbstractCredential implements Credential {

56

private CredentialMetadata credentialMetadata;

57

58

public CredentialMetadata getCredentialMetadata() {

59

if (credentialMetadata == null) {

60

this.credentialMetadata = new BasicCredentialMetadata(this);

61

}

62

return this.credentialMetadata;

63

}

64

65

public void setCredentialMetadata(CredentialMetadata credentialMetadata) {

66

this.credentialMetadata = credentialMetadata;

67

}

68

69

public boolean isValid() {

70

return StringUtils.isNotBlank(getId());

71

}

72

73

public void validate(ValidationContext context) {

74

// Default validation - subclasses can override

75

if (!isValid()) {

76

context.getMessageContext().addMessage(

77

new MessageBuilder()

78

.error()

79

.source(getClass().getSimpleName())

80

.defaultText("Invalid credential")

81

.code("credential.invalid")

82

.build());

83

}

84

}

85

}

86

```

87

88

## Username/Password Credentials

89

90

### Basic Username/Password Credential

91

92

The most common credential type supporting username/password authentication:

93

94

```java { .api }

95

package org.apereo.cas.authentication.credential;

96

97

import org.apereo.cas.authentication.MutableCredential;

98

import org.springframework.binding.validation.ValidationContext;

99

import jakarta.validation.constraints.Size;

100

import java.util.Map;

101

102

public class UsernamePasswordCredential extends AbstractCredential implements MutableCredential {

103

104

public static final String AUTHENTICATION_ATTRIBUTE_PASSWORD = "credential";

105

106

@Size(min = 1, message = "username.required")

107

private String username;

108

109

@Size(min = 1, message = "password.required")

110

private char[] password;

111

112

private String source;

113

private Map<String, Object> customFields = new LinkedHashMap<>();

114

115

// Constructors

116

public UsernamePasswordCredential() {}

117

118

public UsernamePasswordCredential(String username, String password) {

119

this.username = username;

120

assignPassword(StringUtils.defaultString(password));

121

}

122

123

public UsernamePasswordCredential(String username, char[] password,

124

String source, Map<String, Object> customFields) {

125

this.username = username;

126

this.password = password.clone();

127

this.source = source;

128

this.customFields = new HashMap<>(customFields);

129

}

130

131

// MutableCredential implementation

132

public String getId() {

133

return this.username;

134

}

135

136

public void setId(String id) {

137

this.username = id;

138

}

139

140

// Password handling

141

public char[] getPassword() {

142

return password != null ? password.clone() : null;

143

}

144

145

public void setPassword(char[] password) {

146

this.password = password != null ? password.clone() : null;

147

}

148

149

public String toPassword() {

150

return password != null ? new String(password) : null;

151

}

152

153

public void assignPassword(String password) {

154

if (password != null) {

155

this.password = password.toCharArray();

156

} else {

157

this.password = null;

158

}

159

}

160

161

// Properties

162

public String getUsername() { return username; }

163

public void setUsername(String username) { this.username = username; }

164

165

public String getSource() { return source; }

166

public void setSource(String source) { this.source = source; }

167

168

public Map<String, Object> getCustomFields() { return customFields; }

169

public void setCustomFields(Map<String, Object> customFields) {

170

this.customFields = customFields;

171

}

172

173

// Validation

174

@Override

175

public void validate(ValidationContext context) {

176

super.validate(context);

177

178

MessageContext messageContext = context.getMessageContext();

179

if (!"submit".equalsIgnoreCase(context.getUserEvent()) ||

180

messageContext.hasErrorMessages()) {

181

return;

182

}

183

184

// Validate username

185

if (StringUtils.isBlank(username)) {

186

messageContext.addMessage(new MessageBuilder()

187

.error()

188

.source("username")

189

.defaultText("Username is required")

190

.code("username.required")

191

.build());

192

}

193

194

// Validate password

195

if (password == null || password.length == 0) {

196

messageContext.addMessage(new MessageBuilder()

197

.error()

198

.source("password")

199

.defaultText("Password is required")

200

.code("password.required")

201

.build());

202

}

203

}

204

205

@Override

206

public boolean equals(Object obj) {

207

if (obj == null || !this.getClass().equals(obj.getClass())) {

208

return false;

209

}

210

UsernamePasswordCredential other = (UsernamePasswordCredential) obj;

211

return Objects.equals(this.username, other.username);

212

}

213

214

@Override

215

public int hashCode() {

216

return Objects.hashCode(this.username);

217

}

218

219

@Override

220

public String toString() {

221

return String.format("UsernamePasswordCredential[username=%s, source=%s]",

222

username, source);

223

}

224

}

225

```

226

227

### Remember-Me Username/Password Credential

228

229

Extended credential that supports "Remember Me" functionality:

230

231

```java { .api }

232

package org.apereo.cas.authentication.credential;

233

234

import org.apereo.cas.authentication.RememberMeCredential;

235

236

public class RememberMeUsernamePasswordCredential extends UsernamePasswordCredential

237

implements RememberMeCredential {

238

239

public static final String AUTHENTICATION_ATTRIBUTE_REMEMBER_ME = "rememberMe";

240

241

private boolean rememberMe;

242

243

// Constructors

244

public RememberMeUsernamePasswordCredential() {

245

super();

246

}

247

248

public RememberMeUsernamePasswordCredential(String username, String password,

249

boolean rememberMe) {

250

super(username, password);

251

this.rememberMe = rememberMe;

252

}

253

254

// RememberMeCredential implementation

255

public boolean isRememberMe() {

256

return this.rememberMe;

257

}

258

259

public void setRememberMe(boolean rememberMe) {

260

this.rememberMe = rememberMe;

261

}

262

263

@Override

264

public String toString() {

265

return String.format("RememberMeUsernamePasswordCredential[username=%s, rememberMe=%s]",

266

getUsername(), rememberMe);

267

}

268

}

269

```

270

271

## Token-Based Credentials

272

273

### One-Time Password Credential

274

275

For OTP-based authentication systems:

276

277

```java { .api }

278

package org.apereo.cas.authentication.credential;

279

280

import jakarta.validation.constraints.Size;

281

282

public class OneTimePasswordCredential extends AbstractCredential {

283

284

@Size(min = 1, message = "oneTimePassword.required")

285

private String password;

286

287

private String id;

288

289

// Constructors

290

public OneTimePasswordCredential() {}

291

292

public OneTimePasswordCredential(String id, String password) {

293

this.id = id;

294

this.password = password;

295

}

296

297

public String getId() { return id; }

298

public void setId(String id) { this.id = id; }

299

300

public String getPassword() { return password; }

301

public void setPassword(String password) { this.password = password; }

302

303

@Override

304

public void validate(ValidationContext context) {

305

super.validate(context);

306

307

MessageContext messageContext = context.getMessageContext();

308

309

if (StringUtils.isBlank(id)) {

310

messageContext.addMessage(new MessageBuilder()

311

.error()

312

.source("id")

313

.defaultText("ID is required")

314

.code("id.required")

315

.build());

316

}

317

318

if (StringUtils.isBlank(password)) {

319

messageContext.addMessage(new MessageBuilder()

320

.error()

321

.source("password")

322

.defaultText("One-time password is required")

323

.code("oneTimePassword.required")

324

.build());

325

}

326

}

327

}

328

```

329

330

### One-Time Token Credential

331

332

For single-use token authentication:

333

334

```java { .api }

335

package org.apereo.cas.authentication.credential;

336

337

import jakarta.validation.constraints.Size;

338

339

public class OneTimeTokenCredential extends AbstractCredential {

340

341

@Size(min = 1, message = "token.required")

342

private String token;

343

344

private String id;

345

346

// Constructors

347

public OneTimeTokenCredential() {}

348

349

public OneTimeTokenCredential(String id, String token) {

350

this.id = id;

351

this.token = token;

352

}

353

354

public String getId() { return id; }

355

public void setId(String id) { this.id = id; }

356

357

public String getToken() { return token; }

358

public void setToken(String token) { this.token = token; }

359

360

@Override

361

public void validate(ValidationContext context) {

362

super.validate(context);

363

364

MessageContext messageContext = context.getMessageContext();

365

366

if (StringUtils.isBlank(token)) {

367

messageContext.addMessage(new MessageBuilder()

368

.error()

369

.source("token")

370

.defaultText("Token is required")

371

.code("token.required")

372

.build());

373

}

374

}

375

}

376

```

377

378

## Service-Based Credentials

379

380

### Basic Identifiable Credential

381

382

Simple credential with just an identifier:

383

384

```java { .api }

385

package org.apereo.cas.authentication.credential;

386

387

public class BasicIdentifiableCredential extends AbstractCredential {

388

389

private String id;

390

391

// Constructors

392

public BasicIdentifiableCredential() {}

393

394

public BasicIdentifiableCredential(String id) {

395

this.id = id;

396

}

397

398

public String getId() { return id; }

399

public void setId(String id) { this.id = id; }

400

401

@Override

402

public String toString() {

403

return String.format("BasicIdentifiableCredential[id=%s]", id);

404

}

405

}

406

```

407

408

### HTTP-Based Service Credential

409

410

Credential for HTTP service callback authentication:

411

412

```java { .api }

413

package org.apereo.cas.authentication.credential;

414

415

import org.apereo.cas.services.RegisteredService;

416

import java.net.URL;

417

418

public class HttpBasedServiceCredential extends AbstractCredential {

419

420

private final URL callbackUrl;

421

private final RegisteredService registeredService;

422

423

public HttpBasedServiceCredential(URL callbackUrl, RegisteredService registeredService) {

424

this.callbackUrl = callbackUrl;

425

this.registeredService = registeredService;

426

}

427

428

public String getId() {

429

return this.callbackUrl != null ? this.callbackUrl.toExternalForm() : null;

430

}

431

432

public URL getCallbackUrl() { return callbackUrl; }

433

434

public RegisteredService getService() { return registeredService; }

435

436

@Override

437

public String toString() {

438

return String.format("HttpBasedServiceCredential[callbackUrl=%s, service=%s]",

439

callbackUrl, registeredService != null ? registeredService.getName() : "null");

440

}

441

}

442

```

443

444

## Credential Validation

445

446

### Validation Context Usage

447

448

```java { .api }

449

// Validate credential in Spring WebFlow context

450

ValidationContext context = new DefaultValidationContext(requestContext, "credentialValidation");

451

UsernamePasswordCredential credential = new UsernamePasswordCredential("user", "pass");

452

453

credential.validate(context);

454

455

if (context.getMessageContext().hasErrorMessages()) {

456

// Handle validation errors

457

for (Message message : context.getMessageContext().getAllMessages()) {

458

System.out.println("Validation error: " + message.getText());

459

}

460

}

461

```

462

463

### Custom Validation

464

465

```java { .api }

466

public class CustomUsernamePasswordCredential extends UsernamePasswordCredential {

467

468

@Override

469

public void validate(ValidationContext context) {

470

super.validate(context);

471

472

MessageContext messageContext = context.getMessageContext();

473

474

// Custom username validation

475

String username = getUsername();

476

if (username != null && username.contains("@")) {

477

if (!isValidEmail(username)) {

478

messageContext.addMessage(new MessageBuilder()

479

.error()

480

.source("username")

481

.defaultText("Invalid email format")

482

.code("username.invalid.email")

483

.build());

484

}

485

}

486

487

// Password complexity validation

488

String password = toPassword();

489

if (password != null && !isPasswordComplex(password)) {

490

messageContext.addMessage(new MessageBuilder()

491

.error()

492

.source("password")

493

.defaultText("Password does not meet complexity requirements")

494

.code("password.complexity.insufficient")

495

.build());

496

}

497

}

498

499

private boolean isValidEmail(String email) {

500

return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");

501

}

502

503

private boolean isPasswordComplex(String password) {

504

return password.length() >= 8 &&

505

password.matches(".*[A-Z].*") &&

506

password.matches(".*[a-z].*") &&

507

password.matches(".*[0-9].*") &&

508

password.matches(".*[!@#$%^&*()].*");

509

}

510

}

511

```

512

513

## Credential Security

514

515

### Password Security

516

517

```java { .api }

518

public class SecureUsernamePasswordCredential extends UsernamePasswordCredential {

519

520

@Override

521

public void setPassword(char[] password) {

522

super.setPassword(password);

523

524

// Clear the input parameter for security

525

if (password != null) {

526

Arrays.fill(password, '\0');

527

}

528

}

529

530

@Override

531

public void assignPassword(String password) {

532

super.assignPassword(password);

533

534

// Note: String parameter cannot be cleared,

535

// prefer char[] methods for security

536

}

537

538

public void clearPassword() {

539

char[] password = getPassword();

540

if (password != null) {

541

Arrays.fill(password, '\0');

542

}

543

setPassword(null);

544

}

545

}

546

```

547

548

### Credential Encryption

549

550

```java { .api }

551

public class EncryptedUsernamePasswordCredential extends UsernamePasswordCredential {

552

553

private final Cipher cipher;

554

555

public EncryptedUsernamePasswordCredential(Cipher cipher) {

556

this.cipher = cipher;

557

}

558

559

@Override

560

public void setPassword(char[] password) {

561

if (password != null && cipher != null) {

562

try {

563

byte[] encrypted = cipher.doFinal(new String(password).getBytes());

564

String encodedPassword = Base64.getEncoder().encodeToString(encrypted);

565

super.assignPassword(encodedPassword);

566

567

// Clear original password

568

Arrays.fill(password, '\0');

569

} catch (Exception e) {

570

throw new RuntimeException("Failed to encrypt password", e);

571

}

572

} else {

573

super.setPassword(password);

574

}

575

}

576

577

public char[] getDecryptedPassword() throws Exception {

578

String encryptedPassword = toPassword();

579

if (encryptedPassword != null && cipher != null) {

580

byte[] encrypted = Base64.getDecoder().decode(encryptedPassword);

581

byte[] decrypted = cipher.doFinal(encrypted);

582

return new String(decrypted).toCharArray();

583

}

584

return getPassword();

585

}

586

}

587

```

588

589

## Credential Metadata Management

590

591

### Basic Credential Metadata

592

593

```java { .api }

594

package org.apereo.cas.authentication.metadata;

595

596

import org.apereo.cas.authentication.Credential;

597

import org.apereo.cas.authentication.CredentialMetadata;

598

import java.time.ZonedDateTime;

599

import java.util.Map;

600

import java.util.concurrent.ConcurrentHashMap;

601

602

public class BasicCredentialMetadata implements CredentialMetadata {

603

604

private final String id;

605

private final Class<? extends Credential> credentialClass;

606

private final Map<String, Object> properties = new ConcurrentHashMap<>();

607

private ZonedDateTime authenticationDate;

608

609

public BasicCredentialMetadata(Credential credential) {

610

this.id = credential.getId();

611

this.credentialClass = credential.getClass();

612

this.authenticationDate = ZonedDateTime.now();

613

}

614

615

public String getId() { return id; }

616

617

public Class<? extends Credential> getCredentialClass() { return credentialClass; }

618

619

public Map<String, Object> getProperties() { return properties; }

620

621

public void addProperty(String name, Object value) {

622

properties.put(name, value);

623

}

624

625

public ZonedDateTime getAuthenticationDate() { return authenticationDate; }

626

627

public void setAuthenticationDate(ZonedDateTime authenticationDate) {

628

this.authenticationDate = authenticationDate;

629

}

630

}

631

```

632

633

## Integration Interfaces

634

635

### Remember Me Support

636

637

```java { .api }

638

package org.apereo.cas.authentication;

639

640

public interface RememberMeCredential {

641

String AUTHENTICATION_ATTRIBUTE_REMEMBER_ME = "rememberMe";

642

643

boolean isRememberMe();

644

void setRememberMe(boolean rememberMe);

645

}

646

```

647

648

### Credential Selection

649

650

```java { .api }

651

// Create credential selection predicates

652

Predicate<Credential> usernamePasswordPredicate =

653

credential -> credential instanceof UsernamePasswordCredential;

654

655

Predicate<Credential> tokenCredentialPredicate =

656

credential -> credential instanceof OneTimeTokenCredential;

657

658

Predicate<Credential> sourcePredicate = credential -> {

659

if (credential instanceof UsernamePasswordCredential) {

660

UsernamePasswordCredential upc = (UsernamePasswordCredential) credential;

661

return "LDAP".equals(upc.getSource());

662

}

663

return false;

664

};

665

666

// Using predicates for handler selection

667

public class ConditionalAuthenticationHandler extends AbstractAuthenticationHandler {

668

669

private final Predicate<Credential> credentialSelector;

670

671

public ConditionalAuthenticationHandler(Predicate<Credential> credentialSelector) {

672

this.credentialSelector = credentialSelector;

673

}

674

675

@Override

676

public boolean supports(Credential credential) {

677

return credentialSelector.test(credential);

678

}

679

}

680

```

681

682

## Configuration Examples

683

684

### Spring Configuration

685

686

```java { .api }

687

@Configuration

688

public class CredentialConfiguration {

689

690

@Bean

691

public Predicate<Credential> usernamePasswordCredentialSelector() {

692

return credential -> credential instanceof UsernamePasswordCredential;

693

}

694

695

@Bean

696

public Predicate<Credential> rememberMeCredentialSelector() {

697

return credential -> credential instanceof RememberMeCredential &&

698

((RememberMeCredential) credential).isRememberMe();

699

}

700

701

@Bean

702

public MessageSource credentialValidationMessageSource() {

703

ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();

704

messageSource.setBasename("credential-validation-messages");

705

return messageSource;

706

}

707

}

708

```

709

710

### Programmatic Usage

711

712

```java { .api }

713

// Create and validate credentials

714

UsernamePasswordCredential credential = new UsernamePasswordCredential("user", "password");

715

credential.setSource("LDAP");

716

credential.getCustomFields().put("department", "IT");

717

718

// Validate credential

719

ValidationContext validationContext = new DefaultValidationContext();

720

credential.validate(validationContext);

721

722

if (!validationContext.getMessageContext().hasErrorMessages()) {

723

// Credential is valid, proceed with authentication

724

AuthenticationHandler handler = getAppropriateHandler(credential);

725

AuthenticationHandlerExecutionResult result = handler.authenticate(credential);

726

}

727

728

// Working with remember-me credentials

729

RememberMeUsernamePasswordCredential rememberMeCredential =

730

new RememberMeUsernamePasswordCredential("user", "password", true);

731

732

// Check remember-me status

733

if (rememberMeCredential.isRememberMe()) {

734

// Apply remember-me logic

735

}

736

737

// Secure password handling

738

char[] password = "securePassword".toCharArray();

739

try {

740

UsernamePasswordCredential secureCredential =

741

new UsernamePasswordCredential("user", password);

742

// Use credential

743

} finally {

744

// Always clear sensitive data

745

Arrays.fill(password, '\0');

746

}

747

```

748

749

Credential handling provides the foundation for secure authentication data management, supporting various credential types, validation mechanisms, and security practices essential for enterprise authentication systems.