or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdindex.mdjaas.mdlogin-services.mdsecurity-framework.mduser-identity.md

authentication.mddocs/

0

# Authentication

1

2

Jetty Security provides multiple authentication mechanisms to support various security requirements, from simple Basic authentication to complex form-based flows.

3

4

## Base Authenticator Classes

5

6

### LoginAuthenticator

7

8

Abstract base class for login-based authenticators:

9

10

```java { .api }

11

public abstract class LoginAuthenticator implements Authenticator {

12

13

// User login and logout

14

public UserIdentity login(String username, Object password, Request request, Response response);

15

public void logout(Request request, Response response);

16

17

// Access to login service

18

public LoginService getLoginService();

19

20

// Session management

21

protected void updateSession(Request httpRequest, Response httpResponse);

22

23

// Nested authentication state classes

24

public static class UserAuthenticationSucceeded implements AuthenticationState.Succeeded {

25

public UserAuthenticationSucceeded(String method, UserIdentity userIdentity);

26

27

@Override

28

public UserIdentity getUserIdentity();

29

30

@Override

31

public String getAuthenticationType();

32

}

33

34

public static class UserAuthenticationSent extends UserAuthenticationSucceeded

35

implements AuthenticationState.ResponseSent {

36

public UserAuthenticationSent(String method, UserIdentity userIdentity);

37

}

38

39

public static class LoggedOutAuthentication implements AuthenticationState.Deferred {

40

@Override

41

public AuthenticationState authenticate(Request request, Response response, Callback callback);

42

}

43

}

44

```

45

46

## HTTP Basic Authentication

47

48

### BasicAuthenticator

49

50

Simple username/password authentication using HTTP Basic scheme:

51

52

```java { .api }

53

public class BasicAuthenticator extends LoginAuthenticator {

54

55

// Character encoding configuration

56

public Charset getCharset();

57

public void setCharset(Charset charset);

58

59

@Override

60

public String getAuthenticationType() {

61

return Authenticator.BASIC_AUTH;

62

}

63

64

@Override

65

public AuthenticationState validateRequest(Request req, Response res, Callback callback)

66

throws ServerAuthException;

67

68

// Utility for creating Basic auth headers

69

public static String authorization(String user, String password);

70

}

71

```

72

73

### Basic Authentication Setup

74

75

```java { .api }

76

public class BasicAuthExample {

77

78

public void setupBasicAuthentication() {

79

SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();

80

81

// Configure login service

82

HashLoginService loginService = new HashLoginService("MyRealm");

83

loginService.setConfig(Resource.newResource("users.properties"));

84

security.setLoginService(loginService);

85

86

// Create and configure Basic authenticator

87

BasicAuthenticator basicAuth = new BasicAuthenticator();

88

basicAuth.setCharset(StandardCharsets.UTF_8);

89

security.setAuthenticator(basicAuth);

90

91

// Set realm name

92

security.setRealmName("MyRealm");

93

94

// Define protected areas

95

security.put("/api/*", Constraint.from("user"));

96

security.put("/admin/*", Constraint.from("admin"));

97

}

98

99

public void createBasicAuthHeader() {

100

// Create authorization header value

101

String authHeader = BasicAuthenticator.authorization("john", "password");

102

103

// Use in HTTP client

104

HttpClient client = new HttpClient();

105

Request request = client.newRequest("https://example.com/api/data");

106

request.header(HttpHeader.AUTHORIZATION, authHeader);

107

}

108

}

109

```

110

111

## Form-Based Authentication

112

113

### FormAuthenticator

114

115

Web form-based authentication with custom login pages:

116

117

```java { .api }

118

public class FormAuthenticator extends LoginAuthenticator {

119

120

// Form authentication constants

121

public static final String __FORM_LOGIN_PAGE = "org.eclipse.jetty.security.form_login_page";

122

public static final String __FORM_ERROR_PAGE = "org.eclipse.jetty.security.form_error_page";

123

public static final String __FORM_DISPATCH = "org.eclipse.jetty.security.dispatch";

124

public static final String __J_SECURITY_CHECK = "/j_security_check";

125

public static final String __J_USERNAME = "j_username";

126

public static final String __J_PASSWORD = "j_password";

127

128

// Constructors

129

public FormAuthenticator();

130

public FormAuthenticator(String login, String error, boolean dispatch);

131

132

// Configuration

133

public void setAlwaysSaveUri(boolean alwaysSave);

134

public boolean getAlwaysSaveUri();

135

136

@Override

137

public String getAuthenticationType() {

138

return Authenticator.FORM_AUTH;

139

}

140

141

// Security check methods

142

public boolean isJSecurityCheck(String uri);

143

public boolean isLoginOrErrorPage(String pathInContext);

144

145

@Override

146

public AuthenticationState validateRequest(Request req, Response res, Callback callback)

147

throws ServerAuthException;

148

}

149

```

150

151

### Form Authentication Setup

152

153

```java { .api }

154

public class FormAuthExample {

155

156

public void setupFormAuthentication() {

157

SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();

158

159

// Configure login service

160

HashLoginService loginService = new HashLoginService("FormRealm");

161

loginService.setConfig(Resource.newResource("users.properties"));

162

security.setLoginService(loginService);

163

164

// Create form authenticator

165

FormAuthenticator formAuth = new FormAuthenticator(

166

"/login.html", // Login page

167

"/login-error.html", // Error page

168

false // Don't dispatch

169

);

170

formAuth.setAlwaysSaveUri(true); // Save original URI for redirect

171

security.setAuthenticator(formAuth);

172

173

// Security configuration

174

security.setRealmName("FormRealm");

175

security.setSessionRenewedOnAuthentication(true);

176

177

// Constraints

178

security.put("/login.html", Constraint.ALLOWED);

179

security.put("/login-error.html", Constraint.ALLOWED);

180

security.put("/css/*", Constraint.ALLOWED);

181

security.put("/js/*", Constraint.ALLOWED);

182

security.put("/app/*", Constraint.ANY_USER);

183

}

184

}

185

```

186

187

### Custom Login Pages

188

189

```html

190

<!-- login.html -->

191

<!DOCTYPE html>

192

<html>

193

<head>

194

<title>Login</title>

195

</head>

196

<body>

197

<h2>Please Login</h2>

198

<form method="post" action="j_security_check">

199

<table>

200

<tr>

201

<td>Username:</td>

202

<td><input type="text" name="j_username"/></td>

203

</tr>

204

<tr>

205

<td>Password:</td>

206

<td><input type="password" name="j_password"/></td>

207

</tr>

208

<tr>

209

<td colspan="2"><input type="submit" value="Login"/></td>

210

</tr>

211

</table>

212

</form>

213

</body>

214

</html>

215

```

216

217

## HTTP Digest Authentication

218

219

### DigestAuthenticator

220

221

More secure than Basic auth, using challenge-response mechanism:

222

223

```java { .api }

224

public class DigestAuthenticator extends LoginAuthenticator {

225

226

@Override

227

public String getAuthenticationType() {

228

return Authenticator.DIGEST_AUTH;

229

}

230

231

// Nonce configuration for security

232

public int getMaxNonceAge();

233

public void setMaxNonceAge(int maxNonceAge);

234

235

public long getNonceSecret();

236

public void setNonceSecret(long nonceSecret);

237

238

@Override

239

public AuthenticationState validateRequest(Request req, Response res, Callback callback)

240

throws ServerAuthException;

241

}

242

```

243

244

### Digest Authentication Setup

245

246

```java { .api }

247

public class DigestAuthExample {

248

249

public void setupDigestAuthentication() {

250

SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();

251

252

// Configure login service with digest-compatible credentials

253

HashLoginService loginService = new HashLoginService("DigestRealm");

254

loginService.setConfig(Resource.newResource("digest-users.properties"));

255

security.setLoginService(loginService);

256

257

// Create and configure Digest authenticator

258

DigestAuthenticator digestAuth = new DigestAuthenticator();

259

digestAuth.setMaxNonceAge(60000); // 1 minute nonce lifetime

260

digestAuth.setNonceSecret(System.currentTimeMillis());

261

security.setAuthenticator(digestAuth);

262

263

security.setRealmName("DigestRealm");

264

265

// Protected areas

266

security.put("/secure/*", Constraint.ANY_USER);

267

}

268

}

269

```

270

271

## SSL Client Certificate Authentication

272

273

### SslClientCertAuthenticator

274

275

Authentication using SSL client certificates:

276

277

```java { .api }

278

public class SslClientCertAuthenticator extends LoginAuthenticator {

279

280

@Override

281

public String getAuthenticationType() {

282

return Authenticator.CERT_AUTH;

283

}

284

285

// Certificate validation configuration

286

public boolean isValidateCerts();

287

public void setValidateCerts(boolean validateCerts);

288

289

@Override

290

public AuthenticationState validateRequest(Request req, Response res, Callback callback)

291

throws ServerAuthException;

292

}

293

```

294

295

### Client Certificate Setup

296

297

```java { .api }

298

public class ClientCertExample {

299

300

public void setupClientCertAuth() {

301

SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();

302

303

// Special login service for certificate authentication

304

CertificateLoginService loginService = new CertificateLoginService();

305

loginService.setName("CertRealm");

306

security.setLoginService(loginService);

307

308

// Certificate authenticator

309

SslClientCertAuthenticator certAuth = new SslClientCertAuthenticator();

310

certAuth.setValidateCerts(true); // Validate certificate chain

311

security.setAuthenticator(certAuth);

312

313

security.setRealmName("CertRealm");

314

315

// All access requires valid certificate

316

security.put("/*", Constraint.ANY_USER);

317

318

// Admin areas require specific certificate roles

319

security.put("/admin/*", Constraint.from("admin-cert"));

320

}

321

322

// Custom login service for certificates

323

public static class CertificateLoginService extends AbstractLoginService {

324

325

@Override

326

protected UserPrincipal loadUserInfo(String username) {

327

// Load user based on certificate subject

328

return findUserByCertificateSubject(username);

329

}

330

331

@Override

332

protected List<RolePrincipal> loadRoleInfo(UserPrincipal user) {

333

// Load roles based on certificate attributes

334

return getCertificateRoles(user);

335

}

336

337

private UserPrincipal findUserByCertificateSubject(String subject) {

338

// Implementation to map certificate subject to user

339

return null;

340

}

341

342

private List<RolePrincipal> getCertificateRoles(UserPrincipal user) {

343

// Implementation to determine roles from certificate

344

return new ArrayList<>();

345

}

346

}

347

}

348

```

349

350

## SPNEGO Authentication

351

352

### SPNEGOAuthenticator

353

354

Kerberos-based authentication for enterprise environments:

355

356

```java { .api }

357

public class SPNEGOAuthenticator extends LoginAuthenticator {

358

359

@Override

360

public String getAuthenticationType() {

361

return Authenticator.SPNEGO_AUTH;

362

}

363

364

@Override

365

public AuthenticationState validateRequest(Request req, Response res, Callback callback)

366

throws ServerAuthException;

367

}

368

```

369

370

### SPNEGOUserPrincipal

371

372

Principal class for SPNEGO-authenticated users that carries encoded authentication tokens:

373

374

```java { .api }

375

public class SPNEGOUserPrincipal implements Principal {

376

377

// Constructor

378

public SPNEGOUserPrincipal(String name, String encodedToken);

379

380

// Principal interface

381

@Override

382

public String getName();

383

384

// SPNEGO-specific access

385

public String getEncodedToken();

386

}

387

```

388

389

### SPNEGO Setup

390

391

```java { .api }

392

public class SPNEGOExample {

393

394

public void setupSPNEGOAuthentication() {

395

SecurityHandler.PathMapped security = new SecurityHandler.PathMapped();

396

397

// SPNEGO login service

398

SPNEGOLoginService spnegoService = new SPNEGOLoginService();

399

spnegoService.setName("SPNEGO");

400

// Configure Kerberos settings via system properties or JAAS config

401

security.setLoginService(spnegoService);

402

403

// SPNEGO authenticator

404

SPNEGOAuthenticator spnegoAuth = new SPNEGOAuthenticator();

405

security.setAuthenticator(spnegoAuth);

406

407

security.setRealmName("KERBEROS");

408

409

// Enterprise application areas

410

security.put("/app/*", Constraint.ANY_USER);

411

security.put("/admin/*", Constraint.from("Domain Admins"));

412

}

413

}

414

```

415

416

## Session Authentication

417

418

### SessionAuthentication

419

420

Manages authentication state in HTTP sessions:

421

422

```java { .api }

423

public class SessionAuthentication implements AuthenticationState.Succeeded, Session.ValueListener {

424

425

// Session attribute for authenticated user

426

public static final String AUTHENTICATED_ATTRIBUTE = "org.eclipse.jetty.security.UserIdentity";

427

428

// Constructor

429

public SessionAuthentication(String method, UserIdentity userIdentity, Object credentials);

430

431

@Override

432

public UserIdentity getUserIdentity();

433

434

@Override

435

public String getAuthenticationType();

436

437

// Session lifecycle methods

438

@Override

439

public void valueSet(Session session, String name, Object newValue, Object oldValue);

440

441

@Override

442

public void valueRemoved(Session session, String name, Object oldValue);

443

}

444

```

445

446

## Custom Authenticator Implementation

447

448

### Creating Custom Authenticators

449

450

```java { .api }

451

public class TokenAuthenticator extends LoginAuthenticator {

452

private final String tokenHeader;

453

private final TokenValidator tokenValidator;

454

455

public TokenAuthenticator(String tokenHeader, TokenValidator validator) {

456

this.tokenHeader = tokenHeader;

457

this.tokenValidator = validator;

458

}

459

460

@Override

461

public String getAuthenticationType() {

462

return "TOKEN";

463

}

464

465

@Override

466

public AuthenticationState validateRequest(Request request, Response response, Callback callback)

467

throws ServerAuthException {

468

469

// Extract token from header

470

String token = request.getHeaders().get(tokenHeader);

471

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

472

return sendChallenge(response, callback);

473

}

474

475

try {

476

// Validate token

477

TokenInfo tokenInfo = tokenValidator.validate(token);

478

if (tokenInfo == null) {

479

return AuthenticationState.SEND_FAILURE;

480

}

481

482

// Create user identity

483

UserIdentity user = createUserIdentity(tokenInfo);

484

if (user == null) {

485

return AuthenticationState.SEND_FAILURE;

486

}

487

488

return new UserAuthenticationSucceeded(getAuthenticationType(), user);

489

490

} catch (Exception e) {

491

throw new ServerAuthException("Token validation failed", e);

492

}

493

}

494

495

private AuthenticationState sendChallenge(Response response, Callback callback) {

496

response.setStatus(HttpStatus.UNAUTHORIZED_401);

497

response.getHeaders().put("WWW-Authenticate", "Token realm=\"" + getConfiguration().getRealmName() + "\"");

498

callback.succeeded();

499

return AuthenticationState.CHALLENGE;

500

}

501

502

private UserIdentity createUserIdentity(TokenInfo tokenInfo) {

503

// Create user identity from token information

504

Subject subject = new Subject();

505

UserPrincipal userPrincipal = new UserPrincipal(tokenInfo.getUsername(), Credential.getCredential(""));

506

String[] roles = tokenInfo.getRoles().toArray(new String[0]);

507

508

return getConfiguration().getIdentityService().newUserIdentity(subject, userPrincipal, roles);

509

}

510

511

// Token validation interface

512

public interface TokenValidator {

513

TokenInfo validate(String token) throws Exception;

514

}

515

516

// Token information class

517

public static class TokenInfo {

518

private final String username;

519

private final List<String> roles;

520

private final long expiration;

521

522

public TokenInfo(String username, List<String> roles, long expiration) {

523

this.username = username;

524

this.roles = roles;

525

this.expiration = expiration;

526

}

527

528

public String getUsername() { return username; }

529

public List<String> getRoles() { return roles; }

530

public long getExpiration() { return expiration; }

531

public boolean isExpired() { return System.currentTimeMillis() > expiration; }

532

}

533

}

534

```

535

536

### JWT Token Authenticator

537

538

```java { .api }

539

public class JWTAuthenticator extends TokenAuthenticator {

540

541

public JWTAuthenticator(String secretKey) {

542

super("Authorization", new JWTTokenValidator(secretKey));

543

}

544

545

@Override

546

public String getAuthenticationType() {

547

return "JWT";

548

}

549

550

private static class JWTTokenValidator implements TokenValidator {

551

private final String secretKey;

552

553

public JWTTokenValidator(String secretKey) {

554

this.secretKey = secretKey;

555

}

556

557

@Override

558

public TokenInfo validate(String token) throws Exception {

559

// Remove "Bearer " prefix if present

560

if (token.startsWith("Bearer ")) {

561

token = token.substring(7);

562

}

563

564

// JWT validation logic (using a JWT library)

565

// This is a simplified example

566

JWTClaims claims = parseAndValidateJWT(token, secretKey);

567

568

String username = claims.getSubject();

569

List<String> roles = claims.getRoles();

570

long expiration = claims.getExpiration();

571

572

return new TokenInfo(username, roles, expiration);

573

}

574

575

private JWTClaims parseAndValidateJWT(String token, String secret) throws Exception {

576

// JWT parsing and validation implementation

577

// Would typically use a library like jose4j or jsonwebtoken

578

throw new UnsupportedOperationException("JWT parsing implementation required");

579

}

580

}

581

582

private static class JWTClaims {

583

public String getSubject() { return null; }

584

public List<String> getRoles() { return Collections.emptyList(); }

585

public long getExpiration() { return 0; }

586

}

587

}

588

```

589

590

## Multi-Factor Authentication

591

592

### Multi-Factor Authenticator Wrapper

593

594

```java { .api }

595

public class MultiFactorAuthenticator implements Authenticator {

596

private final List<Authenticator> authenticators;

597

private final boolean requireAll;

598

599

public MultiFactorAuthenticator(boolean requireAll, Authenticator... authenticators) {

600

this.requireAll = requireAll;

601

this.authenticators = Arrays.asList(authenticators);

602

}

603

604

@Override

605

public void setConfiguration(Configuration configuration) {

606

authenticators.forEach(auth -> auth.setConfiguration(configuration));

607

}

608

609

@Override

610

public String getAuthenticationType() {

611

return "MULTI_FACTOR";

612

}

613

614

@Override

615

public AuthenticationState validateRequest(Request request, Response response, Callback callback)

616

throws ServerAuthException {

617

618

List<AuthenticationState.Succeeded> successes = new ArrayList<>();

619

620

for (Authenticator authenticator : authenticators) {

621

AuthenticationState state = authenticator.validateRequest(request, response, callback);

622

623

if (state instanceof AuthenticationState.Succeeded) {

624

successes.add((AuthenticationState.Succeeded) state);

625

} else if (requireAll) {

626

return state; // Fail if any required factor fails

627

}

628

}

629

630

// Check if we have enough successful authentications

631

if (requireAll && successes.size() != authenticators.size()) {

632

return AuthenticationState.SEND_FAILURE;

633

} else if (!requireAll && successes.isEmpty()) {

634

return AuthenticationState.SEND_FAILURE;

635

}

636

637

// Combine successful authentications

638

UserIdentity combinedUser = combineUserIdentities(successes);

639

return new LoginAuthenticator.UserAuthenticationSucceeded(getAuthenticationType(), combinedUser);

640

}

641

642

private UserIdentity combineUserIdentities(List<AuthenticationState.Succeeded> successes) {

643

// Logic to combine multiple user identities

644

// Could merge roles, use primary identity, etc.

645

AuthenticationState.Succeeded primary = successes.get(0);

646

return primary.getUserIdentity();

647

}

648

}

649

```

650

651

## Authentication Error Handling

652

653

### Comprehensive Error Handling

654

655

```java { .api }

656

public class AuthenticationErrorHandling {

657

658

public void handleAuthenticationErrors(Authenticator authenticator,

659

Request request, Response response, Callback callback) {

660

661

try {

662

AuthenticationState state = authenticator.validateRequest(request, response, callback);

663

664

if (state instanceof AuthenticationState.Succeeded) {

665

// Authentication successful

666

AuthenticationState.Succeeded success = (AuthenticationState.Succeeded) state;

667

logSuccessfulAuthentication(success);

668

669

} else if (state == AuthenticationState.CHALLENGE) {

670

// Challenge sent to client

671

logAuthenticationChallenge(request);

672

673

} else if (state == AuthenticationState.SEND_FAILURE) {

674

// Authentication failed

675

logAuthenticationFailure(request);

676

677

} else if (state instanceof AuthenticationState.Deferred) {

678

// Deferred authentication

679

logDeferredAuthentication(request);

680

}

681

682

} catch (ServerAuthException e) {

683

handleServerAuthException(e, request, response);

684

}

685

}

686

687

private void logSuccessfulAuthentication(AuthenticationState.Succeeded success) {

688

logger.info("Authentication successful: user={}, type={}",

689

success.getUserIdentity().getUserPrincipal().getName(),

690

success.getAuthenticationType());

691

}

692

693

private void logAuthenticationChallenge(Request request) {

694

logger.debug("Authentication challenge sent to client: {}",

695

Request.getRemoteAddr(request));

696

}

697

698

private void logAuthenticationFailure(Request request) {

699

logger.warn("Authentication failed for client: {}",

700

Request.getRemoteAddr(request));

701

}

702

703

private void logDeferredAuthentication(Request request) {

704

logger.debug("Authentication deferred for request: {}",

705

request.getHttpURI().getPath());

706

}

707

708

private void handleServerAuthException(ServerAuthException e, Request request, Response response) {

709

logger.error("Server authentication exception for client: {} - {}",

710

Request.getRemoteAddr(request), e.getMessage());

711

712

if (!response.isCommitted()) {

713

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);

714

try {

715

response.getWriter().write("Authentication service unavailable");

716

} catch (IOException ioE) {

717

logger.error("Error writing authentication error response", ioE);

718

}

719

}

720

}

721

}

722

```

723

724

## Performance Optimization

725

726

### Caching and Optimization

727

728

```java { .api }

729

public class AuthenticationOptimization {

730

731

// Cached authenticator for frequently accessed resources

732

public static class CachingAuthenticator implements Authenticator {

733

private final Authenticator delegate;

734

private final Cache<String, AuthenticationState> cache;

735

736

public CachingAuthenticator(Authenticator delegate, Duration cacheTTL) {

737

this.delegate = delegate;

738

this.cache = Caffeine.newBuilder()

739

.maximumSize(1000)

740

.expireAfterWrite(cacheTTL)

741

.build();

742

}

743

744

@Override

745

public void setConfiguration(Configuration configuration) {

746

delegate.setConfiguration(configuration);

747

}

748

749

@Override

750

public String getAuthenticationType() {

751

return delegate.getAuthenticationType();

752

}

753

754

@Override

755

public AuthenticationState validateRequest(Request request, Response response, Callback callback)

756

throws ServerAuthException {

757

758

String cacheKey = buildCacheKey(request);

759

if (cacheKey != null) {

760

AuthenticationState cached = cache.getIfPresent(cacheKey);

761

if (cached instanceof AuthenticationState.Succeeded) {

762

return cached;

763

}

764

}

765

766

AuthenticationState result = delegate.validateRequest(request, response, callback);

767

768

if (result instanceof AuthenticationState.Succeeded && cacheKey != null) {

769

cache.put(cacheKey, result);

770

}

771

772

return result;

773

}

774

775

private String buildCacheKey(Request request) {

776

// Build cache key from authentication credentials

777

String authHeader = request.getHeaders().get(HttpHeader.AUTHORIZATION);

778

return authHeader != null ? authHeader.hashCode() + "" : null;

779

}

780

}

781

}

782

```

783

784

Authentication in Jetty Security provides flexible, secure, and performant solutions for various authentication requirements, from simple Basic auth to complex multi-factor scenarios.