or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcore-adapters.mdhttp-operations.mdindex.mdjaas-integration.mdkey-rotation.mdpolicy-enforcement.mdtoken-storage.mdutility-operations.md

jaas-integration.mddocs/

0

# JAAS Integration

1

2

JAAS (Java Authentication and Authorization Service) integration for enterprise Java applications with login modules and principal management. This module provides standard JAAS interfaces for integrating Keycloak authentication into existing Java security frameworks.

3

4

## Capabilities

5

6

### AbstractKeycloakLoginModule

7

8

Base abstract login module providing common JAAS integration functionality for Keycloak authentication.

9

10

```java { .api }

11

/**

12

* Base abstract login module providing common JAAS integration functionality for Keycloak authentication

13

*/

14

public abstract class AbstractKeycloakLoginModule implements LoginModule {

15

/**

16

* Configuration option key for Keycloak configuration file path

17

*/

18

public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file";

19

20

/**

21

* Configuration option key for role principal class name

22

*/

23

public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class";

24

25

/**

26

* Resource path for profile configuration

27

*/

28

public static final String PROFILE_RESOURCE = "profile-resource";

29

30

/**

31

* Authentication result container

32

*/

33

public static class Auth {

34

// Contains authentication details and tokens

35

}

36

37

/**

38

* Initialize the login module with configuration

39

* @param subject Subject to authenticate

40

* @param callbackHandler Handler for obtaining credentials

41

* @param sharedState Shared state between login modules

42

* @param options Configuration options

43

*/

44

public void initialize(

45

Subject subject,

46

CallbackHandler callbackHandler,

47

Map<String, ?> sharedState,

48

Map<String, ?> options

49

);

50

51

/**

52

* Perform authentication

53

* @return true if authentication succeeded

54

* @throws LoginException If authentication fails

55

*/

56

public boolean login() throws LoginException;

57

58

/**

59

* Commit the authentication (add principals to subject)

60

* @return true if commit succeeded

61

* @throws LoginException If commit fails

62

*/

63

public boolean commit() throws LoginException;

64

65

/**

66

* Abort the authentication (cleanup on failure)

67

* @return true if abort succeeded

68

* @throws LoginException If abort fails

69

*/

70

public boolean abort() throws LoginException;

71

72

/**

73

* Logout (remove principals from subject)

74

* @return true if logout succeeded

75

* @throws LoginException If logout fails

76

*/

77

public boolean logout() throws LoginException;

78

79

/**

80

* Resolve Keycloak deployment from configuration file

81

* @param keycloakConfigFile Path to Keycloak configuration file

82

* @return Resolved KeycloakDeployment

83

* @throws RuntimeException If configuration cannot be loaded

84

*/

85

protected KeycloakDeployment resolveDeployment(String keycloakConfigFile);

86

87

/**

88

* Create role principal for the given role name

89

* @param roleName Name of the role

90

* @return Principal representing the role

91

*/

92

protected Principal createRolePrincipal(String roleName);

93

94

/**

95

* Authenticate using bearer token

96

* @param tokenString Bearer token string

97

* @return Authentication result

98

* @throws VerificationException If token verification fails

99

*/

100

protected Auth bearerAuth(String tokenString) throws VerificationException;

101

102

/**

103

* Post-process authentication result after token verification

104

* @param tokenString Original token string

105

* @param token Verified access token

106

* @return Updated authentication result

107

*/

108

protected Auth postTokenVerification(String tokenString, AccessToken token);

109

110

/**

111

* Perform authentication with username and password (abstract method)

112

* @param username Username credential

113

* @param password Password credential

114

* @return Authentication result

115

* @throws Exception If authentication fails

116

*/

117

protected abstract Auth doAuth(String username, String password) throws Exception;

118

119

/**

120

* Get logger for this login module (abstract method)

121

* @return Logger instance

122

*/

123

protected abstract Logger getLogger();

124

}

125

```

126

127

**Usage Examples:**

128

129

```java

130

// Custom login module implementation

131

public class CustomKeycloakLoginModule extends AbstractKeycloakLoginModule {

132

private static final Logger logger = LoggerFactory.getLogger(CustomKeycloakLoginModule.class);

133

134

@Override

135

protected Auth doAuth(String username, String password) throws Exception {

136

// Implement username/password authentication

137

KeycloakDeployment deployment = resolveDeployment(getConfigFile());

138

139

// Use Keycloak's direct access grants (resource owner password credentials)

140

try {

141

AccessTokenResponse tokenResponse = authenticateWithPassword(deployment, username, password);

142

String tokenString = tokenResponse.getToken();

143

144

// Verify the token

145

AccessToken token = AdapterTokenVerifier.verifyToken(tokenString, deployment);

146

147

// Return authentication result

148

return postTokenVerification(tokenString, token);

149

150

} catch (Exception e) {

151

getLogger().warn("Authentication failed for user: {}", username, e);

152

throw new LoginException("Authentication failed: " + e.getMessage());

153

}

154

}

155

156

@Override

157

protected Logger getLogger() {

158

return logger;

159

}

160

161

private AccessTokenResponse authenticateWithPassword(KeycloakDeployment deployment,

162

String username, String password) throws Exception {

163

// Implementation for direct access grant

164

HttpClient client = deployment.getClient();

165

HttpPost post = new HttpPost(deployment.getTokenUrl());

166

167

List<NameValuePair> formParams = new ArrayList<>();

168

formParams.add(new BasicNameValuePair("grant_type", "password"));

169

formParams.add(new BasicNameValuePair("username", username));

170

formParams.add(new BasicNameValuePair("password", password));

171

172

AdapterUtils.setClientCredentials(deployment, post, formParams);

173

174

post.setEntity(new UrlEncodedFormEntity(formParams));

175

176

HttpResponse response = client.execute(post);

177

int statusCode = response.getStatusLine().getStatusCode();

178

179

if (statusCode == 200) {

180

return JsonSerialization.readValue(response.getEntity().getContent(), AccessTokenResponse.class);

181

} else {

182

throw new Exception("Authentication failed with status: " + statusCode);

183

}

184

}

185

}

186

187

// JAAS configuration file (jaas.conf)

188

/*

189

MyApplication {

190

com.example.CustomKeycloakLoginModule required

191

keycloak-config-file="/path/to/keycloak.json"

192

role-principal-class="org.keycloak.adapters.jaas.RolePrincipal";

193

};

194

*/

195

196

// Usage in application

197

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

198

199

LoginContext loginContext = new LoginContext("MyApplication", new CallbackHandler() {

200

@Override

201

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

202

for (Callback callback : callbacks) {

203

if (callback instanceof NameCallback) {

204

((NameCallback) callback).setName("user@example.com");

205

} else if (callback instanceof PasswordCallback) {

206

((PasswordCallback) callback).setPassword("password".toCharArray());

207

}

208

}

209

}

210

});

211

212

try {

213

loginContext.login();

214

Subject subject = loginContext.getSubject();

215

216

// Access authenticated user information

217

Set<Principal> principals = subject.getPrincipals();

218

for (Principal principal : principals) {

219

System.out.println("Principal: " + principal.getName());

220

}

221

222

// Perform authorized actions

223

Subject.doAs(subject, new PrivilegedAction<Void>() {

224

@Override

225

public Void run() {

226

// Code running with authenticated subject

227

return null;

228

}

229

});

230

231

} finally {

232

loginContext.logout();

233

}

234

```

235

236

### RolePrincipal

237

238

Principal implementation representing a user role in JAAS context.

239

240

```java { .api }

241

/**

242

* Principal implementation representing a user role in JAAS context

243

*/

244

public class RolePrincipal implements Principal {

245

/**

246

* Constructor with role name

247

* @param roleName Name of the role

248

*/

249

public RolePrincipal(String roleName);

250

251

/**

252

* Check equality with another principal

253

* @param p Principal to compare with

254

* @return true if principals are equal

255

*/

256

public boolean equals(Object p);

257

258

/**

259

* Get hash code for this principal

260

* @return Hash code value

261

*/

262

public int hashCode();

263

264

/**

265

* Get the role name

266

* @return Role name string

267

*/

268

public String getName();

269

270

/**

271

* String representation of the role principal

272

* @return Formatted string representation

273

*/

274

public String toString();

275

}

276

```

277

278

**Usage Examples:**

279

280

```java

281

// Create role principals

282

RolePrincipal adminRole = new RolePrincipal("admin");

283

RolePrincipal userRole = new RolePrincipal("user");

284

285

// Use in custom login module

286

@Override

287

public boolean commit() throws LoginException {

288

if (authenticationSucceeded) {

289

// Add user principal

290

subject.getPrincipals().add(new KeycloakPrincipal<>(username, securityContext));

291

292

// Add role principals

293

Set<String> roles = extractRolesFromToken(accessToken);

294

for (String role : roles) {

295

subject.getPrincipals().add(new RolePrincipal(role));

296

}

297

298

return true;

299

}

300

return false;

301

}

302

303

// Check roles in authorized code

304

public boolean hasRole(Subject subject, String roleName) {

305

Set<RolePrincipal> rolePrincipals = subject.getPrincipals(RolePrincipal.class);

306

return rolePrincipals.stream()

307

.anyMatch(role -> roleName.equals(role.getName()));

308

}

309

310

// Authorization check example

311

Subject.doAs(subject, new PrivilegedAction<Void>() {

312

@Override

313

public Void run() {

314

Subject currentSubject = Subject.getSubject(AccessController.getContext());

315

316

if (hasRole(currentSubject, "admin")) {

317

// Perform admin operations

318

performAdminOperation();

319

} else if (hasRole(currentSubject, "user")) {

320

// Perform user operations

321

performUserOperation();

322

} else {

323

throw new SecurityException("Insufficient privileges");

324

}

325

326

return null;

327

}

328

});

329

```

330

331

## JAAS Integration Patterns

332

333

### Bearer Token Login Module

334

335

```java

336

// Login module for bearer token authentication

337

public class BearerTokenLoginModule extends AbstractKeycloakLoginModule {

338

private static final Logger logger = LoggerFactory.getLogger(BearerTokenLoginModule.class);

339

private String bearerToken;

340

341

@Override

342

public void initialize(Subject subject, CallbackHandler callbackHandler,

343

Map<String, ?> sharedState, Map<String, ?> options) {

344

super.initialize(subject, callbackHandler, sharedState, options);

345

346

// Extract bearer token from shared state or callback

347

this.bearerToken = (String) sharedState.get("bearer.token");

348

}

349

350

@Override

351

protected Auth doAuth(String username, String password) throws Exception {

352

// This module doesn't use username/password

353

throw new UnsupportedOperationException("Use bearer token authentication");

354

}

355

356

@Override

357

public boolean login() throws LoginException {

358

if (bearerToken == null) {

359

// Try to get token via callback

360

try {

361

BearerTokenCallback tokenCallback = new BearerTokenCallback();

362

callbackHandler.handle(new Callback[]{tokenCallback});

363

bearerToken = tokenCallback.getToken();

364

} catch (Exception e) {

365

throw new LoginException("Failed to obtain bearer token: " + e.getMessage());

366

}

367

}

368

369

if (bearerToken == null) {

370

return false;

371

}

372

373

try {

374

auth = bearerAuth(bearerToken);

375

return auth != null;

376

} catch (VerificationException e) {

377

getLogger().warn("Bearer token verification failed", e);

378

throw new LoginException("Invalid bearer token: " + e.getMessage());

379

}

380

}

381

382

@Override

383

protected Logger getLogger() {

384

return logger;

385

}

386

}

387

388

// Custom callback for bearer token

389

public class BearerTokenCallback implements Callback {

390

private String token;

391

392

public String getToken() {

393

return token;

394

}

395

396

public void setToken(String token) {

397

this.token = token;

398

}

399

}

400

```

401

402

### Web Application Integration

403

404

```java

405

// Servlet filter integrating JAAS with web requests

406

public class JAASKeycloakFilter implements Filter {

407

private String jaasConfigName;

408

409

@Override

410

public void init(FilterConfig filterConfig) throws ServletException {

411

jaasConfigName = filterConfig.getInitParameter("jaas-config-name");

412

if (jaasConfigName == null) {

413

jaasConfigName = "KeycloakWeb";

414

}

415

}

416

417

@Override

418

public void doFilter(ServletRequest request, ServletResponse response,

419

FilterChain chain) throws IOException, ServletException {

420

HttpServletRequest httpRequest = (HttpServletRequest) request;

421

HttpServletResponse httpResponse = (HttpServletResponse) response;

422

423

String authHeader = httpRequest.getHeader("Authorization");

424

if (authHeader != null && authHeader.startsWith("Bearer ")) {

425

String token = authHeader.substring(7);

426

427

try {

428

// Authenticate using JAAS

429

LoginContext loginContext = new LoginContext(jaasConfigName, new BearerTokenCallbackHandler(token));

430

loginContext.login();

431

432

Subject subject = loginContext.getSubject();

433

434

// Continue with authenticated subject

435

Subject.doAs(subject, new PrivilegedExceptionAction<Void>() {

436

@Override

437

public Void run() throws Exception {

438

chain.doFilter(request, response);

439

return null;

440

}

441

});

442

443

loginContext.logout();

444

445

} catch (LoginException e) {

446

httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

447

httpResponse.getWriter().write("Authentication failed: " + e.getMessage());

448

return;

449

} catch (PrivilegedActionException e) {

450

throw new ServletException(e.getException());

451

}

452

} else {

453

httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

454

httpResponse.getWriter().write("Bearer token required");

455

}

456

}

457

458

private static class BearerTokenCallbackHandler implements CallbackHandler {

459

private final String token;

460

461

public BearerTokenCallbackHandler(String token) {

462

this.token = token;

463

}

464

465

@Override

466

public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

467

for (Callback callback : callbacks) {

468

if (callback instanceof BearerTokenCallback) {

469

((BearerTokenCallback) callback).setToken(token);

470

} else {

471

throw new UnsupportedCallbackException(callback);

472

}

473

}

474

}

475

}

476

}

477

```

478

479

### Enterprise Application Server Integration

480

481

```java

482

// JBoss/WildFly integration example

483

public class KeycloakSecurityDomain {

484

485

// Configure in standalone.xml or domain.xml

486

/*

487

<security-domain name="keycloak">

488

<authentication>

489

<login-module code="com.example.KeycloakLoginModule" flag="required">

490

<module-option name="keycloak-config-file" value="/opt/keycloak/keycloak.json"/>

491

<module-option name="role-principal-class" value="org.keycloak.adapters.jaas.RolePrincipal"/>

492

</login-module>

493

</authentication>

494

</security-domain>

495

*/

496

497

// EJB with security annotations

498

@Stateless

499

@SecurityDomain("keycloak")

500

public class SecureEJB {

501

502

@RolesAllowed({"admin", "manager"})

503

public void adminOperation() {

504

Subject subject = Subject.getSubject(AccessController.getContext());

505

// Access authenticated subject

506

}

507

508

@RolesAllowed("user")

509

public void userOperation() {

510

// User-level operation

511

}

512

}

513

}

514

```

515

516

### Configuration Examples

517

518

```java

519

// Complete JAAS configuration

520

/*

521

# jaas.conf

522

KeycloakApp {

523

com.example.CustomKeycloakLoginModule required

524

keycloak-config-file="/etc/keycloak/keycloak.json"

525

role-principal-class="org.keycloak.adapters.jaas.RolePrincipal";

526

};

527

528

KeycloakWeb {

529

com.example.BearerTokenLoginModule required

530

keycloak-config-file="/etc/keycloak/keycloak.json";

531

};

532

*/

533

534

// System properties setup

535

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

536

537

// Programmatic configuration

538

Configuration.setConfiguration(new Configuration() {

539

@Override

540

public AppConfigurationEntry[] getAppConfigurationEntry(String name) {

541

if ("KeycloakApp".equals(name)) {

542

Map<String, String> options = new HashMap<>();

543

options.put("keycloak-config-file", "/etc/keycloak/keycloak.json");

544

options.put("role-principal-class", "org.keycloak.adapters.jaas.RolePrincipal");

545

546

return new AppConfigurationEntry[] {

547

new AppConfigurationEntry(

548

"com.example.CustomKeycloakLoginModule",

549

AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,

550

options

551

)

552

};

553

}

554

return null;

555

}

556

});

557

```

558

559

### BearerTokenLoginModule

560

561

Concrete JAAS login module for authenticating bearer tokens in JAAS environments.

562

563

```java { .api }

564

/**

565

* JAAS login module for bearer token authentication

566

* Expects username (ignored) and password (bearer token)

567

*/

568

public class BearerTokenLoginModule extends AbstractKeycloakLoginModule {

569

/**

570

* Authenticate using bearer token passed as password parameter

571

* @param username Username (ignored for bearer token auth)

572

* @param password Bearer token string

573

* @return Authentication result

574

* @throws VerificationException If token verification fails

575

*/

576

protected Auth doAuth(String username, String password) throws VerificationException;

577

578

/**

579

* Get logger for this login module

580

* @return Logger instance

581

*/

582

protected Logger getLogger();

583

}

584

```

585

586

### DirectAccessGrantsLoginModule

587

588

JAAS login module implementing OAuth2 Resource Owner Password Credentials Grant for direct username/password authentication with Keycloak.

589

590

```java { .api }

591

/**

592

* JAAS login module for direct access grants (username/password authentication)

593

* Implements OAuth2 Resource Owner Password Credentials Grant

594

*/

595

public class DirectAccessGrantsLoginModule extends AbstractKeycloakLoginModule {

596

/**

597

* Configuration option key for OAuth2 scope parameter

598

*/

599

public static final String SCOPE_OPTION = "scope";

600

601

/**

602

* Authenticate using direct access grants (username/password)

603

* @param username User's username

604

* @param password User's password

605

* @return Authentication result with tokens

606

* @throws IOException If network communication fails

607

* @throws VerificationException If token verification fails

608

*/

609

protected Auth doAuth(String username, String password) throws IOException, VerificationException;

610

611

/**

612

* Perform direct grant authentication against Keycloak

613

* @param username User's username

614

* @param password User's password

615

* @return Authentication result with access and refresh tokens

616

* @throws IOException If HTTP request fails

617

* @throws VerificationException If token verification fails

618

*/

619

protected Auth directGrantAuth(String username, String password) throws IOException, VerificationException;

620

621

/**

622

* Commit authentication (save refresh token to subject's private credentials)

623

* @return true if commit succeeded

624

* @throws LoginException If commit fails

625

*/

626

public boolean commit() throws LoginException;

627

628

/**

629

* Logout and revoke refresh token with Keycloak

630

* @return true if logout succeeded

631

* @throws LoginException If logout fails

632

*/

633

public boolean logout() throws LoginException;

634

}

635

```