or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconnection-pooling.mdcontent-management.mdhttp-operations.mdindex.mdproxy-configuration.mdrequest-configuration.mdresponse-processing.md

authentication.mddocs/

0

# Authentication

1

2

The authentication capability provides comprehensive support for HTTP authentication mechanisms including Basic, Digest, and SPNEGO/Kerberos authentication with credential storage, automatic challenge handling, and per-request authentication configuration.

3

4

## AuthenticationStore Interface

5

6

The central storage system for authentication credentials and results.

7

8

```java { .api }

9

public interface AuthenticationStore {

10

// Authentication management

11

void addAuthentication(Authentication authentication);

12

void removeAuthentication(Authentication authentication);

13

void clearAuthentications();

14

Collection<Authentication> getAuthentications();

15

16

// Authentication result management

17

void addAuthenticationResult(Authentication.Result result);

18

void removeAuthenticationResult(Authentication.Result result);

19

void clearAuthenticationResults();

20

Authentication.Result findAuthenticationResult(URI uri);

21

}

22

```

23

24

## Authentication Interface

25

26

The base interface for all authentication mechanisms.

27

28

```java { .api }

29

public interface Authentication {

30

// Authentication matching

31

boolean matches(String type, URI uri, String realm);

32

33

// Authentication execution

34

Authentication.Result authenticate(Request request, ContentResponse response,

35

Authentication.HeaderInfo headerInfo, Attributes context);

36

37

// Header information class

38

static class HeaderInfo {

39

String getType();

40

String getRealm();

41

String getBase64();

42

Map<String, String> getParameters();

43

String getParameter(String paramName);

44

}

45

}

46

```

47

48

## Authentication.Result Interface

49

50

Represents the result of an authentication attempt.

51

52

```java { .api }

53

public interface Authentication.Result {

54

URI getURI();

55

56

void apply(Request request);

57

}

58

```

59

60

## Basic Authentication

61

62

HTTP Basic authentication using username and password credentials.

63

64

### BasicAuthentication Class

65

66

```java { .api }

67

public class BasicAuthentication implements Authentication {

68

public BasicAuthentication(URI uri, String realm, String user, String password);

69

public BasicAuthentication(String user, String password);

70

71

public String getUser();

72

public String getPassword();

73

}

74

```

75

76

### Usage Examples

77

78

```java

79

// Basic authentication for specific URI and realm

80

URI apiUri = URI.create("https://api.example.com");

81

BasicAuthentication auth = new BasicAuthentication(apiUri, "API Realm", "username", "password");

82

83

// Add to authentication store

84

client.getAuthenticationStore().addAuthentication(auth);

85

86

// Make authenticated request

87

ContentResponse response = client.GET("https://api.example.com/protected");

88

89

// Global basic authentication (matches any realm)

90

BasicAuthentication globalAuth = new BasicAuthentication("admin", "secret123");

91

client.getAuthenticationStore().addAuthentication(globalAuth);

92

```

93

94

### Preemptive Basic Authentication

95

96

```java

97

// Add authentication result directly to avoid initial challenge

98

URI uri = URI.create("https://api.example.com");

99

String credentials = Base64.getEncoder().encodeToString("user:pass".getBytes());

100

AuthenticationResult result = AuthenticationResult.from(uri, null, "Authorization", "Basic " + credentials);

101

102

client.getAuthenticationStore().addAuthenticationResult(result);

103

104

// First request will include Authorization header immediately

105

ContentResponse response = client.GET("https://api.example.com/data");

106

```

107

108

## Digest Authentication

109

110

HTTP Digest authentication providing improved security over Basic authentication.

111

112

### DigestAuthentication Class

113

114

```java { .api }

115

public class DigestAuthentication implements Authentication {

116

public DigestAuthentication(URI uri, String realm, String user, String password);

117

118

public String getUser();

119

public String getPassword();

120

}

121

```

122

123

### Usage Examples

124

125

```java

126

// Digest authentication setup

127

URI secureUri = URI.create("https://secure.example.com");

128

DigestAuthentication digestAuth = new DigestAuthentication(

129

secureUri,

130

"Secure Area",

131

"username",

132

"password"

133

);

134

135

client.getAuthenticationStore().addAuthentication(digestAuth);

136

137

// Make request - digest challenge will be handled automatically

138

ContentResponse response = client.GET("https://secure.example.com/data");

139

```

140

141

### Digest Authentication Flow

142

143

```java

144

// The client automatically handles the digest authentication flow:

145

// 1. Initial request without authentication

146

// 2. Server responds with 401 and WWW-Authenticate header

147

// 3. Client calculates digest response using provided nonce

148

// 4. Client retries request with Authorization header

149

// 5. Server validates digest and responds with requested content

150

151

DigestAuthentication auth = new DigestAuthentication(

152

URI.create("https://api.example.com"),

153

"Protected",

154

"user",

155

"pass"

156

);

157

158

client.getAuthenticationStore().addAuthentication(auth);

159

160

// This request will automatically handle the digest challenge

161

ContentResponse response = client.GET("https://api.example.com/protected-resource");

162

```

163

164

## SPNEGO Authentication

165

166

SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication for Kerberos/Windows authentication.

167

168

### SPNEGOAuthentication Class

169

170

```java { .api }

171

public class SPNEGOAuthentication implements Authentication {

172

public SPNEGOAuthentication(String serviceName);

173

174

public String getServiceName();

175

}

176

```

177

178

### Usage Examples

179

180

```java

181

// SPNEGO authentication for Kerberos

182

SPNEGOAuthentication spnegoAuth = new SPNEGOAuthentication("HTTP/server.example.com");

183

184

client.getAuthenticationStore().addAuthentication(spnegoAuth);

185

186

// SPNEGO authentication requires proper Kerberos configuration

187

// System properties or JAAS configuration may be needed

188

System.setProperty("java.security.auth.login.config", "/path/to/jaas.conf");

189

System.setProperty("java.security.krb5.conf", "/path/to/krb5.conf");

190

191

ContentResponse response = client.GET("https://server.example.com/protected");

192

```

193

194

### SPNEGO Configuration

195

196

```java

197

// Example JAAS configuration (jaas.conf)

198

/*

199

Client {

200

com.sun.security.auth.module.Krb5LoginModule required

201

useTicketCache=true

202

renewTGT=true

203

doNotPrompt=true;

204

};

205

*/

206

207

// Example Kerberos configuration (krb5.conf)

208

/*

209

[libdefaults]

210

default_realm = EXAMPLE.COM

211

212

[realms]

213

EXAMPLE.COM = {

214

kdc = kdc.example.com

215

admin_server = admin.example.com

216

}

217

*/

218

219

public class KerberosClient {

220

public void configureSpnego() {

221

// Set system properties for Kerberos

222

System.setProperty("java.security.auth.login.config", "jaas.conf");

223

System.setProperty("java.security.krb5.conf", "krb5.conf");

224

System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");

225

226

// Create SPNEGO authentication

227

SPNEGOAuthentication auth = new SPNEGOAuthentication("HTTP/api.example.com");

228

client.getAuthenticationStore().addAuthentication(auth);

229

}

230

}

231

```

232

233

## Authentication Store Management

234

235

### Adding Multiple Authentication Mechanisms

236

237

```java

238

AuthenticationStore authStore = client.getAuthenticationStore();

239

240

// Add multiple authentication mechanisms

241

authStore.addAuthentication(new BasicAuthentication("user1", "pass1"));

242

authStore.addAuthentication(new DigestAuthentication(

243

URI.create("https://secure.example.com"),

244

"Secure",

245

"user2",

246

"pass2"

247

));

248

authStore.addAuthentication(new SPNEGOAuthentication("HTTP/kerberos.example.com"));

249

250

// The client will automatically select the appropriate authentication

251

// based on server challenges

252

```

253

254

### Authentication Result Management

255

256

```java

257

AuthenticationStore authStore = client.getAuthenticationStore();

258

259

// Find existing authentication result

260

URI uri = URI.create("https://api.example.com");

261

AuthenticationResult existingResult = authStore.findAuthenticationResult(uri);

262

263

if (existingResult != null) {

264

System.out.println("Found cached authentication for: " + uri);

265

} else {

266

// Add preemptive authentication

267

AuthenticationResult result = AuthenticationResult.from(

268

uri,

269

"API",

270

"Authorization",

271

"Bearer " + accessToken

272

);

273

authStore.addAuthenticationResult(result);

274

}

275

276

// Clear authentication results when tokens expire

277

authStore.clearAuthenticationResults();

278

```

279

280

## Custom Authentication

281

282

Implement custom authentication mechanisms by extending the Authentication interface.

283

284

### Custom Token Authentication

285

286

```java

287

public class BearerTokenAuthentication implements Authentication {

288

private final URI uri;

289

private final String token;

290

291

public BearerTokenAuthentication(URI uri, String token) {

292

this.uri = uri;

293

this.token = token;

294

}

295

296

@Override

297

public String getType() {

298

return "Bearer";

299

}

300

301

@Override

302

public URI getURI() {

303

return uri;

304

}

305

306

@Override

307

public String getRealm() {

308

return null; // No realm for bearer tokens

309

}

310

311

@Override

312

public boolean matches(String type, URI uri, String realm) {

313

return "Bearer".equalsIgnoreCase(type) &&

314

(this.uri == null || this.uri.equals(uri));

315

}

316

317

@Override

318

public AuthenticationResult authenticate(Request request, ContentResponse response,

319

HeaderInfo headerInfo, Context context) {

320

return AuthenticationResult.from(

321

context.getURI(),

322

context.getRealm(),

323

"Authorization",

324

"Bearer " + token

325

);

326

}

327

}

328

329

// Usage

330

BearerTokenAuthentication tokenAuth = new BearerTokenAuthentication(

331

URI.create("https://api.example.com"),

332

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

333

);

334

335

client.getAuthenticationStore().addAuthentication(tokenAuth);

336

```

337

338

### API Key Authentication

339

340

```java

341

public class ApiKeyAuthentication implements Authentication {

342

private final URI uri;

343

private final String apiKey;

344

private final String headerName;

345

346

public ApiKeyAuthentication(URI uri, String headerName, String apiKey) {

347

this.uri = uri;

348

this.headerName = headerName;

349

this.apiKey = apiKey;

350

}

351

352

@Override

353

public String getType() {

354

return "ApiKey";

355

}

356

357

@Override

358

public URI getURI() {

359

return uri;

360

}

361

362

@Override

363

public String getRealm() {

364

return null;

365

}

366

367

@Override

368

public boolean matches(String type, URI uri, String realm) {

369

return this.uri.getHost().equals(uri.getHost());

370

}

371

372

@Override

373

public AuthenticationResult authenticate(Request request, ContentResponse response,

374

HeaderInfo headerInfo, Context context) {

375

return AuthenticationResult.from(

376

context.getURI(),

377

context.getRealm(),

378

headerName,

379

apiKey

380

);

381

}

382

}

383

384

// Usage

385

ApiKeyAuthentication apiKeyAuth = new ApiKeyAuthentication(

386

URI.create("https://api.example.com"),

387

"X-API-Key",

388

"your-api-key-here"

389

);

390

391

client.getAuthenticationStore().addAuthentication(apiKeyAuth);

392

```

393

394

## Per-Request Authentication

395

396

Override authentication for specific requests without modifying the global authentication store.

397

398

### Request-Specific Headers

399

400

```java

401

// Override authentication for a single request

402

ContentResponse response = client.newRequest("https://api.example.com/data")

403

.header("Authorization", "Bearer " + specificToken)

404

.send();

405

406

// Use different API key for specific request

407

ContentResponse response2 = client.newRequest("https://different-api.com/data")

408

.header("X-API-Key", "different-api-key")

409

.send();

410

```

411

412

### Temporary Authentication

413

414

```java

415

public class TemporaryAuth {

416

private final HttpClient client;

417

private final AuthenticationStore originalStore;

418

419

public TemporaryAuth(HttpClient client) {

420

this.client = client;

421

this.originalStore = client.getAuthenticationStore();

422

}

423

424

public ContentResponse requestWithAuth(String url, Authentication auth) throws Exception {

425

// Create temporary authentication store

426

AuthenticationStore tempStore = new HttpAuthenticationStore();

427

tempStore.addAuthentication(auth);

428

429

// Temporarily replace authentication store

430

client.setAuthenticationStore(tempStore);

431

432

try {

433

return client.GET(url);

434

} finally {

435

// Restore original authentication store

436

client.setAuthenticationStore(originalStore);

437

}

438

}

439

}

440

```

441

442

## Authentication Error Handling

443

444

### Handling Authentication Failures

445

446

```java

447

client.newRequest("https://api.example.com/protected")

448

.send(result -> {

449

if (result.isSucceeded()) {

450

Response response = result.getResponse();

451

if (response.getStatus() == 401) {

452

System.err.println("Authentication failed");

453

454

// Check WWW-Authenticate header for challenge details

455

String wwwAuth = response.getHeaders().get("WWW-Authenticate");

456

System.err.println("Challenge: " + wwwAuth);

457

458

// Handle specific authentication errors

459

if (wwwAuth != null && wwwAuth.contains("expired_token")) {

460

refreshToken();

461

}

462

}

463

} else {

464

Throwable failure = result.getFailure();

465

if (failure instanceof HttpResponseException) {

466

HttpResponseException httpEx = (HttpResponseException) failure;

467

if (httpEx.getResponse().getStatus() == 401) {

468

System.err.println("Authentication challenge failed");

469

}

470

}

471

}

472

});

473

```

474

475

### Authentication Retry Logic

476

477

```java

478

public class AuthenticatedClient {

479

private final HttpClient client;

480

private String accessToken;

481

482

public ContentResponse authenticatedRequest(String url) throws Exception {

483

// First attempt with current token

484

ContentResponse response = client.newRequest(url)

485

.header("Authorization", "Bearer " + accessToken)

486

.send();

487

488

if (response.getStatus() == 401) {

489

// Token expired, refresh and retry

490

refreshAccessToken();

491

492

response = client.newRequest(url)

493

.header("Authorization", "Bearer " + accessToken)

494

.send();

495

496

if (response.getStatus() == 401) {

497

throw new SecurityException("Authentication failed after token refresh");

498

}

499

}

500

501

return response;

502

}

503

504

private void refreshAccessToken() {

505

// Implement token refresh logic

506

// This could involve calling a refresh token endpoint

507

// or re-authenticating with username/password

508

}

509

}

510

```

511

512

## Authentication Best Practices

513

514

### Secure Credential Storage

515

516

```java

517

// Avoid hardcoding credentials

518

public class SecureAuthConfig {

519

public static BasicAuthentication createFromEnvironment() {

520

String username = System.getenv("API_USERNAME");

521

String password = System.getenv("API_PASSWORD");

522

523

if (username == null || password == null) {

524

throw new IllegalStateException("Authentication credentials not configured");

525

}

526

527

return new BasicAuthentication(username, password);

528

}

529

530

public static BearerTokenAuthentication createFromTokenFile(Path tokenFile) throws IOException {

531

String token = Files.readString(tokenFile).trim();

532

return new BearerTokenAuthentication(null, token);

533

}

534

}

535

```

536

537

### Authentication Logging

538

539

```java

540

public class LoggingAuthenticationStore implements AuthenticationStore {

541

private final AuthenticationStore delegate;

542

private final Logger logger;

543

544

@Override

545

public void addAuthentication(Authentication authentication) {

546

logger.info("Adding authentication for: {} - {}",

547

authentication.getType(),

548

authentication.getURI());

549

delegate.addAuthentication(authentication);

550

}

551

552

@Override

553

public AuthenticationResult findAuthenticationResult(URI uri) {

554

AuthenticationResult result = delegate.findAuthenticationResult(uri);

555

if (result != null) {

556

logger.debug("Found cached authentication result for: {}", uri);

557

} else {

558

logger.debug("No cached authentication result for: {}", uri);

559

}

560

return result;

561

}

562

563

// Implement other methods...

564

}

565

566

// Usage

567

LoggingAuthenticationStore loggingStore = new LoggingAuthenticationStore(

568

client.getAuthenticationStore(),

569

LoggerFactory.getLogger("auth")

570

);

571

```