or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication-sessions.mdcomponent-framework.mdcore-models.mdcredential-management.mdindex.mdorganization-management.mdprovider-framework.mdsession-management.mduser-storage.mdvalidation-framework.mdvault-integration.md

authentication-sessions.mddocs/

0

# Authentication Sessions

1

2

Authentication sessions manage the state during authentication flows in Keycloak. They provide a way to track user progress through multi-step authentication processes and store temporary data during login.

3

4

## Core Session Interfaces

5

6

### AuthenticationSessionModel

7

8

Represents an authentication session for a specific client tab.

9

10

```java { .api }

11

public interface AuthenticationSessionModel extends CommonClientSessionModel {

12

/**

13

* Gets the tab ID for this authentication session.

14

*

15

* @return tab ID

16

*/

17

String getTabId();

18

19

/**

20

* Gets the parent root authentication session.

21

*

22

* @return parent session

23

*/

24

RootAuthenticationSessionModel getParentSession();

25

26

/**

27

* Gets the execution status map for authentication flow.

28

*

29

* @return map of authenticator ID to execution status

30

*/

31

Map<String, ExecutionStatus> getExecutionStatus();

32

33

/**

34

* Sets the execution status for a specific authenticator.

35

*

36

* @param authenticator the authenticator ID

37

* @param status the execution status

38

*/

39

void setExecutionStatus(String authenticator, ExecutionStatus status);

40

41

/**

42

* Clears all execution status entries.

43

*/

44

void clearExecutionStatus();

45

46

/**

47

* Gets the authenticated user for this session.

48

*

49

* @return authenticated user or null

50

*/

51

UserModel getAuthenticatedUser();

52

53

/**

54

* Sets the authenticated user for this session.

55

*

56

* @param user the authenticated user

57

*/

58

void setAuthenticatedUser(UserModel user);

59

60

/**

61

* Gets required actions that need to be performed.

62

*

63

* @return set of required action names

64

*/

65

Set<String> getRequiredActions();

66

67

/**

68

* Adds a required action.

69

*

70

* @param action the required action name

71

*/

72

void addRequiredAction(String action);

73

74

/**

75

* Adds a required action enum.

76

*

77

* @param action the required action enum

78

*/

79

void addRequiredAction(RequiredAction action);

80

81

/**

82

* Removes a required action.

83

*

84

* @param action the required action name

85

*/

86

void removeRequiredAction(String action);

87

88

/**

89

* Removes a required action enum.

90

*

91

* @param action the required action enum

92

*/

93

void removeRequiredAction(RequiredAction action);

94

95

/**

96

* Sets the user session note.

97

*

98

* @param name the note name

99

* @param value the note value

100

*/

101

void setUserSessionNote(String name, String value);

102

103

/**

104

* Gets user session notes.

105

*

106

* @return map of note names to values

107

*/

108

Map<String, String> getUserSessionNotes();

109

}

110

```

111

112

### RootAuthenticationSessionModel

113

114

Represents the root authentication session that can contain multiple client tabs.

115

116

```java { .api }

117

public interface RootAuthenticationSessionModel {

118

/**

119

* Gets the session ID.

120

*

121

* @return session ID

122

*/

123

String getId();

124

125

/**

126

* Gets the realm for this session.

127

*

128

* @return realm model

129

*/

130

RealmModel getRealm();

131

132

/**

133

* Gets the timestamp when the session was created.

134

*

135

* @return creation timestamp in seconds

136

*/

137

int getTimestamp();

138

139

/**

140

* Sets the timestamp for the session.

141

*

142

* @param timestamp timestamp in seconds

143

*/

144

void setTimestamp(int timestamp);

145

146

/**

147

* Gets authentication session for a specific client and tab.

148

*

149

* @param client the client

150

* @param tabId the tab ID

151

* @return authentication session or null

152

*/

153

AuthenticationSessionModel getAuthenticationSession(ClientModel client, String tabId);

154

155

/**

156

* Creates a new authentication session for a client and tab.

157

*

158

* @param client the client

159

* @param tabId the tab ID

160

* @return created authentication session

161

*/

162

AuthenticationSessionModel createAuthenticationSession(ClientModel client, String tabId);

163

164

/**

165

* Removes authentication session for a client and tab.

166

*

167

* @param client the client

168

* @param tabId the tab ID

169

*/

170

void removeAuthenticationSessionByTabId(ClientModel client, String tabId);

171

172

/**

173

* Restarts the session (clears all authentication sessions).

174

*/

175

void restartSession(RealmModel realm);

176

}

177

```

178

179

### CommonClientSessionModel

180

181

Base interface for client sessions with common functionality.

182

183

```java { .api }

184

public interface CommonClientSessionModel {

185

/**

186

* Gets the session ID.

187

*

188

* @return session ID

189

*/

190

String getId();

191

192

/**

193

* Gets the realm for this session.

194

*

195

* @return realm model

196

*/

197

RealmModel getRealm();

198

199

/**

200

* Gets the client for this session.

201

*

202

* @return client model

203

*/

204

ClientModel getClient();

205

206

/**

207

* Gets the redirect URI.

208

*

209

* @return redirect URI

210

*/

211

String getRedirectUri();

212

213

/**

214

* Sets the redirect URI.

215

*

216

* @param uri the redirect URI

217

*/

218

void setRedirectUri(String uri);

219

220

/**

221

* Gets a client note.

222

*

223

* @param name the note name

224

* @return note value or null

225

*/

226

String getNote(String name);

227

228

/**

229

* Sets a client note.

230

*

231

* @param name the note name

232

* @param value the note value

233

*/

234

void setNote(String name, String value);

235

236

/**

237

* Removes a client note.

238

*

239

* @param name the note name

240

*/

241

void removeNote(String name);

242

243

/**

244

* Gets all client notes.

245

*

246

* @return map of note names to values

247

*/

248

Map<String, String> getNotes();

249

250

/**

251

* Gets the authentication method.

252

*

253

* @return authentication method

254

*/

255

String getAuthMethod();

256

257

/**

258

* Sets the authentication method.

259

*

260

* @param method the authentication method

261

*/

262

void setAuthMethod(String method);

263

}

264

```

265

266

## Authentication Session Provider

267

268

### AuthenticationSessionProvider

269

270

Provider for managing authentication sessions.

271

272

```java { .api }

273

public interface AuthenticationSessionProvider extends Provider {

274

/**

275

* Creates a new root authentication session.

276

*

277

* @param realm the realm

278

* @return created root session

279

*/

280

RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm);

281

282

/**

283

* Creates a root authentication session with specific ID.

284

*

285

* @param realm the realm

286

* @param id the session ID

287

* @return created root session

288

*/

289

RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm, String id);

290

291

/**

292

* Gets a root authentication session by ID.

293

*

294

* @param realm the realm

295

* @param authenticationSessionId the session ID

296

* @return root session or null

297

*/

298

RootAuthenticationSessionModel getRootAuthenticationSession(RealmModel realm, String authenticationSessionId);

299

300

/**

301

* Removes a root authentication session.

302

*

303

* @param realm the realm

304

* @param authenticationSession the session to remove

305

*/

306

void removeRootAuthenticationSession(RealmModel realm, RootAuthenticationSessionModel authenticationSession);

307

308

/**

309

* Removes expired authentication sessions.

310

*

311

* @param realm the realm

312

*/

313

void removeExpired(RealmModel realm);

314

315

/**

316

* Removes all authentication sessions for a realm.

317

*

318

* @param realm the realm

319

*/

320

void onRealmRemoved(RealmModel realm);

321

322

/**

323

* Removes all authentication sessions for a client.

324

*

325

* @param realm the realm

326

* @param client the client

327

*/

328

void onClientRemoved(RealmModel realm, ClientModel client);

329

330

/**

331

* Updates ownership when a client is renamed.

332

*

333

* @param realm the realm

334

* @param client the client

335

* @param newClientId the new client ID

336

*/

337

void updateNonlocalSessionAuthNotes(RealmModel realm, ClientModel client, String newClientId);

338

}

339

```

340

341

## Authentication Session Compound ID

342

343

### AuthenticationSessionCompoundId

344

345

Utility class for parsing authentication session compound IDs.

346

347

```java { .api }

348

public class AuthenticationSessionCompoundId {

349

private final String rootSessionId;

350

private final String clientUUID;

351

private final String tabId;

352

353

public AuthenticationSessionCompoundId(String rootSessionId, String clientUUID, String tabId) {

354

this.rootSessionId = rootSessionId;

355

this.clientUUID = clientUUID;

356

this.tabId = tabId;

357

}

358

359

/**

360

* Parses a compound ID string.

361

*

362

* @param compoundId the compound ID string

363

* @return parsed compound ID

364

*/

365

public static AuthenticationSessionCompoundId fromSessionId(String compoundId) {

366

String[] parts = compoundId.split("\\.");

367

if (parts.length >= 3) {

368

return new AuthenticationSessionCompoundId(parts[0], parts[1], parts[2]);

369

}

370

throw new IllegalArgumentException("Invalid compound session ID: " + compoundId);

371

}

372

373

/**

374

* Generates the compound ID string.

375

*

376

* @return compound ID string

377

*/

378

public String getEncodedId() {

379

return rootSessionId + "." + clientUUID + "." + tabId;

380

}

381

382

public String getRootSessionId() { return rootSessionId; }

383

public String getClientUUID() { return clientUUID; }

384

public String getTabId() { return tabId; }

385

386

@Override

387

public boolean equals(Object obj) {

388

if (this == obj) return true;

389

if (obj == null || getClass() != obj.getClass()) return false;

390

AuthenticationSessionCompoundId that = (AuthenticationSessionCompoundId) obj;

391

return Objects.equals(rootSessionId, that.rootSessionId) &&

392

Objects.equals(clientUUID, that.clientUUID) &&

393

Objects.equals(tabId, that.tabId);

394

}

395

396

@Override

397

public int hashCode() {

398

return Objects.hash(rootSessionId, clientUUID, tabId);

399

}

400

401

@Override

402

public String toString() {

403

return getEncodedId();

404

}

405

}

406

```

407

408

## Execution Status Enum

409

410

```java { .api }

411

public enum ExecutionStatus {

412

SUCCESS,

413

FAILED,

414

SETUP_REQUIRED,

415

ATTEMPTED,

416

SKIPPED,

417

CHALLENGED,

418

FLOW_RESET

419

}

420

```

421

422

## Usage Examples

423

424

### Working with Authentication Sessions

425

426

```java

427

// Create and manage authentication sessions

428

try (KeycloakSession session = sessionFactory.create()) {

429

RealmModel realm = session.realms().getRealmByName("myrealm");

430

ClientModel client = realm.getClientByClientId("my-app");

431

AuthenticationSessionProvider authSessionProvider = session.authenticationSessions();

432

433

// Create root authentication session

434

RootAuthenticationSessionModel rootSession = authSessionProvider.createRootAuthenticationSession(realm);

435

436

// Create authentication session for specific client tab

437

String tabId = "tab1";

438

AuthenticationSessionModel authSession = rootSession.createAuthenticationSession(client, tabId);

439

440

// Set redirect URI

441

authSession.setRedirectUri("https://myapp.com/callback");

442

443

// Set authentication flow notes

444

authSession.setNote("login_hint", "john@example.com");

445

authSession.setNote("kc_locale", "en");

446

447

// Track authentication progress

448

authSession.setExecutionStatus("username-password-form", ExecutionStatus.SUCCESS);

449

authSession.setExecutionStatus("otp-form", ExecutionStatus.CHALLENGED);

450

451

// Set authenticated user after successful authentication

452

UserModel user = session.users().getUserByUsername(realm, "john");

453

authSession.setAuthenticatedUser(user);

454

455

// Add required actions

456

authSession.addRequiredAction(RequiredAction.UPDATE_PASSWORD);

457

authSession.addRequiredAction(RequiredAction.VERIFY_EMAIL);

458

459

// Set user session notes for later use

460

authSession.setUserSessionNote("login_ip", "192.168.1.100");

461

authSession.setUserSessionNote("login_time", String.valueOf(System.currentTimeMillis()));

462

}

463

```

464

465

### Custom Authenticator Using Authentication Session

466

467

```java

468

public class CustomAuthenticator implements Authenticator {

469

470

@Override

471

public void authenticate(AuthenticationFlowContext context) {

472

AuthenticationSessionModel authSession = context.getAuthenticationSession();

473

474

// Check if user is already identified

475

UserModel user = authSession.getAuthenticatedUser();

476

if (user == null) {

477

// Redirect to identification step

478

context.failure(AuthenticationFlowError.UNKNOWN_USER);

479

return;

480

}

481

482

// Check for previous attempts

483

String attempts = authSession.getNote("custom_auth_attempts");

484

int attemptCount = attempts != null ? Integer.parseInt(attempts) : 0;

485

486

if (attemptCount >= 3) {

487

context.failure(AuthenticationFlowError.TOO_MANY_FAILURES);

488

return;

489

}

490

491

// Generate challenge

492

String challenge = generateChallenge();

493

authSession.setNote("custom_challenge", challenge);

494

495

// Create challenge form

496

Response challengeForm = createChallengeForm(context, challenge);

497

context.challenge(challengeForm);

498

}

499

500

@Override

501

public void action(AuthenticationFlowContext context) {

502

AuthenticationSessionModel authSession = context.getAuthenticationSession();

503

MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();

504

505

String challenge = authSession.getNote("custom_challenge");

506

String response = formData.getFirst("challenge_response");

507

508

if (validateResponse(challenge, response)) {

509

// Success - clear attempt count

510

authSession.removeNote("custom_auth_attempts");

511

authSession.removeNote("custom_challenge");

512

context.success();

513

} else {

514

// Failed - increment attempt count

515

String attempts = authSession.getNote("custom_auth_attempts");

516

int attemptCount = attempts != null ? Integer.parseInt(attempts) : 0;

517

attemptCount++;

518

authSession.setNote("custom_auth_attempts", String.valueOf(attemptCount));

519

520

if (attemptCount >= 3) {

521

context.failure(AuthenticationFlowError.TOO_MANY_FAILURES);

522

} else {

523

Response errorForm = createChallengeForm(context, challenge, "Invalid response. Attempts: " + attemptCount);

524

context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, errorForm);

525

}

526

}

527

}

528

529

private String generateChallenge() {

530

// Generate random challenge

531

return UUID.randomUUID().toString();

532

}

533

534

private boolean validateResponse(String challenge, String response) {

535

// Validate the response against the challenge

536

return challenge != null && challenge.equals(response);

537

}

538

539

private Response createChallengeForm(AuthenticationFlowContext context, String challenge) {

540

return createChallengeForm(context, challenge, null);

541

}

542

543

private Response createChallengeForm(AuthenticationFlowContext context, String challenge, String error) {

544

// Create and return form response

545

return Response.ok().build(); // Simplified

546

}

547

}

548

```

549

550

### Session Management During Authentication Flow

551

552

```java

553

public class MultiStepAuthenticator implements Authenticator {

554

555

@Override

556

public void authenticate(AuthenticationFlowContext context) {

557

AuthenticationSessionModel authSession = context.getAuthenticationSession();

558

559

// Check current step

560

String currentStep = authSession.getNote("auth_step");

561

if (currentStep == null) {

562

currentStep = "step1";

563

}

564

565

switch (currentStep) {

566

case "step1":

567

handleStep1(context);

568

break;

569

case "step2":

570

handleStep2(context);

571

break;

572

case "step3":

573

handleStep3(context);

574

break;

575

default:

576

context.success();

577

}

578

}

579

580

private void handleStep1(AuthenticationFlowContext context) {

581

// First step - username/password

582

AuthenticationSessionModel authSession = context.getAuthenticationSession();

583

584

// Store step information

585

authSession.setNote("auth_step", "step1");

586

authSession.setNote("step1_start_time", String.valueOf(System.currentTimeMillis()));

587

588

// Challenge user for credentials

589

Response form = createUsernamePasswordForm(context);

590

context.challenge(form);

591

}

592

593

private void handleStep2(AuthenticationFlowContext context) {

594

// Second step - OTP verification

595

AuthenticationSessionModel authSession = context.getAuthenticationSession();

596

597

// Verify step1 was completed

598

if (!"step1_completed".equals(authSession.getNote("step1_status"))) {

599

context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);

600

return;

601

}

602

603

authSession.setNote("auth_step", "step2");

604

authSession.setNote("step2_start_time", String.valueOf(System.currentTimeMillis()));

605

606

Response form = createOtpForm(context);

607

context.challenge(form);

608

}

609

610

private void handleStep3(AuthenticationFlowContext context) {

611

// Final step - additional verification

612

AuthenticationSessionModel authSession = context.getAuthenticationSession();

613

614

// Verify previous steps

615

if (!"step2_completed".equals(authSession.getNote("step2_status"))) {

616

context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);

617

return;

618

}

619

620

// Set completion notes for user session

621

authSession.setUserSessionNote("multi_step_auth", "completed");

622

authSession.setUserSessionNote("auth_duration",

623

String.valueOf(System.currentTimeMillis() - Long.parseLong(authSession.getNote("step1_start_time"))));

624

625

context.success();

626

}

627

628

@Override

629

public void action(AuthenticationFlowContext context) {

630

AuthenticationSessionModel authSession = context.getAuthenticationSession();

631

String currentStep = authSession.getNote("auth_step");

632

633

switch (currentStep) {

634

case "step1":

635

if (processStep1Action(context)) {

636

authSession.setNote("step1_status", "step1_completed");

637

authSession.setNote("auth_step", "step2");

638

handleStep2(context);

639

}

640

break;

641

case "step2":

642

if (processStep2Action(context)) {

643

authSession.setNote("step2_status", "step2_completed");

644

authSession.setNote("auth_step", "step3");

645

handleStep3(context);

646

}

647

break;

648

}

649

}

650

}

651

```