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
```