0
# Credential Management
1
2
The credential management framework provides extensible support for various credential types including passwords, OTP tokens, WebAuthn, and custom credential types. It enables validation, storage, and lifecycle management of user credentials.
3
4
## Core Credential Interfaces
5
6
### CredentialProvider
7
8
Base interface for credential providers that manage specific credential types.
9
10
```java { .api }
11
public interface CredentialProvider extends Provider {
12
/**
13
* Gets the credential type handled by this provider.
14
*
15
* @return credential type string
16
*/
17
String getType();
18
19
/**
20
* Creates a credential for a user.
21
*
22
* @param realm the realm
23
* @param user the user
24
* @param credentialModel the credential to create
25
* @return created credential model
26
*/
27
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel credentialModel);
28
29
/**
30
* Deletes a credential by ID.
31
*
32
* @param realm the realm
33
* @param user the user
34
* @param credentialId the credential ID
35
* @return true if deleted successfully
36
*/
37
boolean deleteCredential(RealmModel realm, UserModel user, String credentialId);
38
39
/**
40
* Gets a credential from the stored model.
41
*
42
* @param model the stored credential model
43
* @return credential model
44
*/
45
CredentialModel getCredentialFromModel(CredentialModel model);
46
47
/**
48
* Gets the default credential for a user.
49
*
50
* @param realm the realm
51
* @param user the user
52
* @return default credential or null
53
*/
54
default CredentialModel getDefaultCredential(RealmModel realm, UserModel user) {
55
return null;
56
}
57
58
/**
59
* Checks if the credential type is configured for a user.
60
*
61
* @param realm the realm
62
* @param user the user
63
* @return true if configured
64
*/
65
default boolean isConfiguredFor(RealmModel realm, UserModel user) {
66
return false;
67
}
68
69
/**
70
* Gets metadata for credential types.
71
*
72
* @param user the user
73
* @return credential type metadata
74
*/
75
default CredentialTypeMetadata getCredentialTypeMetadata(CredentialTypeMetadataContext metadataContext) {
76
return CredentialTypeMetadata.builder()
77
.type(getType())
78
.category(CredentialTypeMetadata.Category.BASIC_AUTHENTICATION)
79
.helpText("credential." + getType() + ".helpText")
80
.build(session);
81
}
82
}
83
```
84
85
### CredentialInputValidator
86
87
Validates credential input for authentication.
88
89
```java { .api }
90
public interface CredentialInputValidator {
91
/**
92
* Checks if this validator supports the credential type.
93
*
94
* @param credentialType the credential type
95
* @return true if supported
96
*/
97
boolean supportsCredentialType(String credentialType);
98
99
/**
100
* Checks if the credential type is configured for a user.
101
*
102
* @param realm the realm
103
* @param user the user
104
* @param credentialType the credential type
105
* @return true if configured
106
*/
107
boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
108
109
/**
110
* Validates credential input.
111
*
112
* @param realm the realm
113
* @param user the user
114
* @param input the credential input
115
* @return true if valid
116
*/
117
boolean isValid(RealmModel realm, UserModel user, CredentialInput input);
118
}
119
```
120
121
### CredentialInputUpdater
122
123
Updates user credentials.
124
125
```java { .api }
126
public interface CredentialInputUpdater {
127
/**
128
* Checks if this updater supports the credential type.
129
*
130
* @param credentialType the credential type
131
* @return true if supported
132
*/
133
boolean supportsCredentialType(String credentialType);
134
135
/**
136
* Updates a user's credential.
137
*
138
* @param realm the realm
139
* @param user the user
140
* @param input the credential input
141
* @return true if updated successfully
142
*/
143
boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input);
144
145
/**
146
* Disables a credential type for a user.
147
*
148
* @param realm the realm
149
* @param user the user
150
* @param credentialType the credential type to disable
151
*/
152
void disableCredentialType(RealmModel realm, UserModel user, String credentialType);
153
154
/**
155
* Gets disabled credential types for a user.
156
*
157
* @param realm the realm
158
* @param user the user
159
* @return stream of disabled credential types
160
*/
161
default Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
162
return Stream.empty();
163
}
164
}
165
```
166
167
## Credential Models
168
169
### CredentialModel
170
171
Represents a stored credential.
172
173
```java { .api }
174
public class CredentialModel {
175
public static final String PASSWORD = "password";
176
public static final String PASSWORD_HISTORY = "password-history";
177
public static final String TOTP = "otp";
178
public static final String HOTP = "hotp";
179
public static final String CLIENT_CERT = "cert";
180
public static final String KERBEROS = "kerberos";
181
public static final String SECRET = "secret";
182
public static final String WEBAUTHN = "webauthn";
183
public static final String WEBAUTHN_PASSWORDLESS = "webauthn-passwordless";
184
public static final String RECOVERY_AUTHN_CODES = "recovery-authn-codes";
185
186
private String id;
187
private String type;
188
private String userLabel;
189
private Long createdDate;
190
private String secretData;
191
private String credentialData;
192
193
// Constructors
194
public CredentialModel() {}
195
196
// Getters and setters
197
public String getId() { return id; }
198
public void setId(String id) { this.id = id; }
199
200
public String getType() { return type; }
201
public void setType(String type) { this.type = type; }
202
203
public String getUserLabel() { return userLabel; }
204
public void setUserLabel(String userLabel) { this.userLabel = userLabel; }
205
206
public Long getCreatedDate() { return createdDate; }
207
public void setCreatedDate(Long createdDate) { this.createdDate = createdDate; }
208
209
public String getSecretData() { return secretData; }
210
public void setSecretData(String secretData) { this.secretData = secretData; }
211
212
public String getCredentialData() { return credentialData; }
213
public void setCredentialData(String credentialData) { this.credentialData = credentialData; }
214
215
// Utility methods
216
public boolean challengeResponse(String response) {
217
// Default implementation
218
return false;
219
}
220
221
@Override
222
public boolean equals(Object obj) {
223
if (this == obj) return true;
224
if (obj == null || getClass() != obj.getClass()) return false;
225
CredentialModel that = (CredentialModel) obj;
226
return Objects.equals(id, that.id);
227
}
228
229
@Override
230
public int hashCode() {
231
return Objects.hash(id);
232
}
233
}
234
```
235
236
### CredentialInput
237
238
Interface for credential input during authentication.
239
240
```java { .api }
241
public interface CredentialInput {
242
/**
243
* Gets the credential type.
244
*
245
* @return credential type
246
*/
247
String getType();
248
249
/**
250
* Gets the challenge response.
251
*
252
* @return challenge response
253
*/
254
String getChallengeResponse();
255
256
/**
257
* Gets the credential ID.
258
*
259
* @return credential ID
260
*/
261
default String getCredentialId() {
262
return null;
263
}
264
}
265
```
266
267
## Specific Credential Types
268
269
### Password Credentials
270
271
#### PasswordCredentialModel
272
273
Model for password credentials.
274
275
```java { .api }
276
public class PasswordCredentialModel extends CredentialModel {
277
public static final String TYPE = "password";
278
public static final String PASSWORD_HISTORY = "password-history";
279
280
private PasswordCredentialData credentialData;
281
private PasswordSecretData secretData;
282
283
private PasswordCredentialModel(String credentialId, PasswordCredentialData credentialData, PasswordSecretData secretData) {
284
setId(credentialId);
285
setType(TYPE);
286
setCredentialData(JsonSerialization.writeValueAsString(credentialData));
287
setSecretData(JsonSerialization.writeValueAsString(secretData));
288
setCreatedDate(Time.currentTimeMillis());
289
this.credentialData = credentialData;
290
this.secretData = secretData;
291
}
292
293
public static PasswordCredentialModel createFromValues(String algorithm, byte[] salt, int hashIterations, String encodedPassword) {
294
PasswordCredentialData credentialData = new PasswordCredentialData(hashIterations, algorithm);
295
PasswordSecretData secretData = new PasswordSecretData(encodedPassword, salt);
296
return new PasswordCredentialModel(generateId(), credentialData, secretData);
297
}
298
299
public static PasswordCredentialModel createFromCredentialModel(CredentialModel credential) {
300
try {
301
PasswordCredentialData credentialData = JsonSerialization.readValue(credential.getCredentialData(), PasswordCredentialData.class);
302
PasswordSecretData secretData = JsonSerialization.readValue(credential.getSecretData(), PasswordSecretData.class);
303
PasswordCredentialModel passwordCredentialModel = new PasswordCredentialModel(credential.getId(), credentialData, secretData);
304
passwordCredentialModel.setUserLabel(credential.getUserLabel());
305
passwordCredentialModel.setCreatedDate(credential.getCreatedDate());
306
return passwordCredentialModel;
307
} catch (IOException e) {
308
throw new ModelException(e);
309
}
310
}
311
312
public PasswordCredentialData getPasswordCredentialData() {
313
return credentialData;
314
}
315
316
public PasswordSecretData getPasswordSecretData() {
317
return secretData;
318
}
319
320
private static String generateId() {
321
return KeycloakModelUtils.generateId();
322
}
323
}
324
```
325
326
### OTP Credentials
327
328
#### OTPCredentialModel
329
330
Model for OTP (One-Time Password) credentials.
331
332
```java { .api }
333
public class OTPCredentialModel extends CredentialModel {
334
public static final String TYPE = "otp";
335
public static final String TOTP = "totp";
336
public static final String HOTP = "hotp";
337
338
private OTPCredentialData credentialData;
339
private OTPSecretData secretData;
340
341
private OTPCredentialModel(String credentialId, OTPCredentialData credentialData, OTPSecretData secretData) {
342
setId(credentialId);
343
setType(TYPE);
344
setCredentialData(JsonSerialization.writeValueAsString(credentialData));
345
setSecretData(JsonSerialization.writeValueAsString(secretData));
346
setCreatedDate(Time.currentTimeMillis());
347
this.credentialData = credentialData;
348
this.secretData = secretData;
349
}
350
351
public static OTPCredentialModel createFromPolicy(RealmModel realm, String secretEncodedBase32) {
352
return createFromPolicy(realm, secretEncodedBase32, null);
353
}
354
355
public static OTPCredentialModel createFromPolicy(RealmModel realm, String secretEncodedBase32, String userLabel) {
356
OTPPolicy policy = realm.getOTPPolicy();
357
OTPCredentialData credentialData = new OTPCredentialData(policy.getType(), policy.getDigits(),
358
policy.getPeriod(), policy.getInitialCounter(),
359
policy.getAlgorithm());
360
OTPSecretData secretData = new OTPSecretData(secretEncodedBase32);
361
362
OTPCredentialModel credential = new OTPCredentialModel(generateId(), credentialData, secretData);
363
if (userLabel != null) {
364
credential.setUserLabel(userLabel);
365
}
366
return credential;
367
}
368
369
public static OTPCredentialModel createFromCredentialModel(CredentialModel credential) {
370
try {
371
OTPCredentialData credentialData = JsonSerialization.readValue(credential.getCredentialData(), OTPCredentialData.class);
372
OTPSecretData secretData = JsonSerialization.readValue(credential.getSecretData(), OTPSecretData.class);
373
OTPCredentialModel otpCredentialModel = new OTPCredentialModel(credential.getId(), credentialData, secretData);
374
otpCredentialModel.setUserLabel(credential.getUserLabel());
375
otpCredentialModel.setCreatedDate(credential.getCreatedDate());
376
return otpCredentialModel;
377
} catch (IOException e) {
378
throw new ModelException(e);
379
}
380
}
381
382
public OTPCredentialData getOTPCredentialData() {
383
return credentialData;
384
}
385
386
public OTPSecretData getOTPSecretData() {
387
return secretData;
388
}
389
390
private static String generateId() {
391
return KeycloakModelUtils.generateId();
392
}
393
}
394
```
395
396
### WebAuthn Credentials
397
398
#### WebAuthnCredentialModel
399
400
Model for WebAuthn credentials.
401
402
```java { .api }
403
public class WebAuthnCredentialModel extends CredentialModel {
404
public static final String TYPE_TWOFACTOR = "webauthn";
405
public static final String TYPE_PASSWORDLESS = "webauthn-passwordless";
406
407
private WebAuthnCredentialData credentialData;
408
private WebAuthnSecretData secretData;
409
410
private WebAuthnCredentialModel(String credentialId, WebAuthnCredentialData credentialData, WebAuthnSecretData secretData) {
411
setId(credentialId);
412
setType(TYPE_TWOFACTOR);
413
setCredentialData(JsonSerialization.writeValueAsString(credentialData));
414
setSecretData(JsonSerialization.writeValueAsString(secretData));
415
setCreatedDate(Time.currentTimeMillis());
416
this.credentialData = credentialData;
417
this.secretData = secretData;
418
}
419
420
public static WebAuthnCredentialModel create(String type, String userLabel, String aaguid,
421
String credentialId, String attestationStatementFormat,
422
String attestationStatement, String credentialPublicKey,
423
long counter) {
424
WebAuthnCredentialData credentialData = new WebAuthnCredentialData(aaguid, credentialId,
425
attestationStatementFormat,
426
attestationStatement);
427
WebAuthnSecretData secretData = new WebAuthnSecretData(credentialPublicKey, counter);
428
429
WebAuthnCredentialModel credential = new WebAuthnCredentialModel(generateId(), credentialData, secretData);
430
credential.setType(type);
431
credential.setUserLabel(userLabel);
432
return credential;
433
}
434
435
public static WebAuthnCredentialModel createFromCredentialModel(CredentialModel credential) {
436
try {
437
WebAuthnCredentialData credentialData = JsonSerialization.readValue(credential.getCredentialData(), WebAuthnCredentialData.class);
438
WebAuthnSecretData secretData = JsonSerialization.readValue(credential.getSecretData(), WebAuthnSecretData.class);
439
WebAuthnCredentialModel webAuthnCredentialModel = new WebAuthnCredentialModel(credential.getId(), credentialData, secretData);
440
webAuthnCredentialModel.setType(credential.getType());
441
webAuthnCredentialModel.setUserLabel(credential.getUserLabel());
442
webAuthnCredentialModel.setCreatedDate(credential.getCreatedDate());
443
return webAuthnCredentialModel;
444
} catch (IOException e) {
445
throw new ModelException(e);
446
}
447
}
448
449
public WebAuthnCredentialData getWebAuthnCredentialData() {
450
return credentialData;
451
}
452
453
public WebAuthnSecretData getWebAuthnSecretData() {
454
return secretData;
455
}
456
457
private static String generateId() {
458
return KeycloakModelUtils.generateId();
459
}
460
}
461
```
462
463
## Credential Storage
464
465
### UserCredentialStore
466
467
Interface for credential storage operations.
468
469
```java { .api }
470
public interface UserCredentialStore {
471
/**
472
* Updates a credential for a user.
473
*
474
* @param realm the realm
475
* @param user the user
476
* @param cred the credential to update
477
*/
478
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
479
480
/**
481
* Creates a new credential for a user.
482
*
483
* @param realm the realm
484
* @param user the user
485
* @param cred the credential to create
486
* @return created credential
487
*/
488
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
489
490
/**
491
* Removes a credential for a user.
492
*
493
* @param realm the realm
494
* @param user the user
495
* @param id the credential ID
496
* @return true if removed
497
*/
498
boolean removeCredential(RealmModel realm, UserModel user, String id);
499
500
/**
501
* Gets a specific credential for a user.
502
*
503
* @param realm the realm
504
* @param user the user
505
* @param id the credential ID
506
* @return credential or null
507
*/
508
CredentialModel getUserCredentialById(RealmModel realm, UserModel user, String id);
509
510
/**
511
* Gets all credentials for a user.
512
*
513
* @param realm the realm
514
* @param user the user
515
* @return list of credentials
516
*/
517
List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user);
518
519
/**
520
* Gets credentials of a specific type for a user.
521
*
522
* @param realm the realm
523
* @param user the user
524
* @param type the credential type
525
* @return list of credentials
526
*/
527
List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type);
528
529
/**
530
* Gets the most recent credential of a type for a user.
531
*
532
* @param realm the realm
533
* @param user the user
534
* @param type the credential type
535
* @return credential or null
536
*/
537
CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
538
539
/**
540
* Moves a credential to a new position in the list.
541
*
542
* @param realm the realm
543
* @param user the user
544
* @param id the credential ID
545
* @param newPosition the new position
546
*/
547
void moveCredentialTo(RealmModel realm, UserModel user, String id, String newPosition);
548
}
549
```
550
551
### SubjectCredentialManager
552
553
Manages credentials for a specific subject (user).
554
555
```java { .api }
556
public interface SubjectCredentialManager {
557
/**
558
* Checks if a credential type is configured.
559
*
560
* @param type the credential type
561
* @return true if configured
562
*/
563
boolean isConfiguredFor(String type);
564
565
/**
566
* Updates a credential.
567
*
568
* @param input the credential input
569
* @return true if updated
570
*/
571
boolean updateCredential(CredentialInput input);
572
573
/**
574
* Updates a stored credential.
575
*
576
* @param cred the credential model
577
*/
578
void updateStoredCredential(CredentialModel cred);
579
580
/**
581
* Creates a stored credential.
582
*
583
* @param cred the credential model
584
* @return created credential
585
*/
586
CredentialModel createStoredCredential(CredentialModel cred);
587
588
/**
589
* Removes a stored credential.
590
*
591
* @param id the credential ID
592
* @return true if removed
593
*/
594
boolean removeStoredCredential(String id);
595
596
/**
597
* Gets a stored credential by ID.
598
*
599
* @param id the credential ID
600
* @return credential or null
601
*/
602
CredentialModel getStoredCredentialById(String id);
603
604
/**
605
* Gets stored credentials by type.
606
*
607
* @param type the credential type
608
* @return stream of credentials
609
*/
610
Stream<CredentialModel> getStoredCredentialsStream(String type);
611
612
/**
613
* Gets all stored credentials.
614
*
615
* @return stream of credentials
616
*/
617
Stream<CredentialModel> getStoredCredentialsStream();
618
619
/**
620
* Disables a credential type.
621
*
622
* @param credentialType the credential type
623
*/
624
void disableCredentialType(String credentialType);
625
626
/**
627
* Gets disableable credential types.
628
*
629
* @return stream of credential types
630
*/
631
Stream<String> getDisableableCredentialTypesStream();
632
633
/**
634
* Checks if a credential input is valid.
635
*
636
* @param input the credential input
637
* @return true if valid
638
*/
639
boolean isValid(CredentialInput input);
640
641
/**
642
* Gets the credential authentication object.
643
*
644
* @return credential authentication
645
*/
646
CredentialAuthentication authenticate(CredentialInput input);
647
648
/**
649
* Moves a credential to a new position.
650
*
651
* @param credentialId the credential ID
652
* @param newPreviousCredentialId the ID of credential that should be before this one
653
*/
654
void moveStoredCredentialTo(String credentialId, String newPreviousCredentialId);
655
656
/**
657
* Creates a credential from user input.
658
*
659
* @param input the credential input
660
* @return created credential
661
*/
662
boolean createCredentialThroughProvider(CredentialInput input);
663
}
664
```
665
666
## Usage Examples
667
668
### Creating a Custom Credential Provider
669
670
```java
671
public class CustomTokenCredentialProvider implements CredentialProvider, CredentialInputValidator, CredentialInputUpdater {
672
public static final String TYPE = "custom-token";
673
674
private final KeycloakSession session;
675
676
public CustomTokenCredentialProvider(KeycloakSession session) {
677
this.session = session;
678
}
679
680
@Override
681
public String getType() {
682
return TYPE;
683
}
684
685
@Override
686
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel credentialModel) {
687
if (!TYPE.equals(credentialModel.getType())) {
688
return null;
689
}
690
691
// Validate and create the credential
692
CredentialModel credential = new CredentialModel();
693
credential.setId(KeycloakModelUtils.generateId());
694
credential.setType(TYPE);
695
credential.setCreatedDate(Time.currentTimeMillis());
696
credential.setSecretData(credentialModel.getSecretData());
697
credential.setCredentialData(credentialModel.getCredentialData());
698
credential.setUserLabel(credentialModel.getUserLabel());
699
700
return session.userCredentialManager().createStoredCredential(realm, user, credential);
701
}
702
703
@Override
704
public boolean deleteCredential(RealmModel realm, UserModel user, String credentialId) {
705
return session.userCredentialManager().removeStoredCredential(realm, user, credentialId);
706
}
707
708
@Override
709
public CredentialModel getCredentialFromModel(CredentialModel model) {
710
return model;
711
}
712
713
@Override
714
public boolean supportsCredentialType(String credentialType) {
715
return TYPE.equals(credentialType);
716
}
717
718
@Override
719
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
720
if (!supportsCredentialType(credentialType)) {
721
return false;
722
}
723
return !session.userCredentialManager()
724
.getStoredCredentialsByTypeStream(realm, user, TYPE)
725
.findFirst().isEmpty();
726
}
727
728
@Override
729
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
730
if (!supportsCredentialType(input.getType())) {
731
return false;
732
}
733
734
String token = input.getChallengeResponse();
735
if (token == null) {
736
return false;
737
}
738
739
// Validate against stored credential
740
List<CredentialModel> credentials = session.userCredentialManager()
741
.getStoredCredentialsByTypeStream(realm, user, TYPE)
742
.collect(Collectors.toList());
743
744
for (CredentialModel credential : credentials) {
745
if (validateToken(token, credential)) {
746
return true;
747
}
748
}
749
750
return false;
751
}
752
753
@Override
754
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
755
if (!supportsCredentialType(input.getType())) {
756
return false;
757
}
758
759
// Remove existing credentials of this type
760
session.userCredentialManager()
761
.getStoredCredentialsByTypeStream(realm, user, TYPE)
762
.forEach(cred -> session.userCredentialManager().removeStoredCredential(realm, user, cred.getId()));
763
764
// Create new credential
765
CredentialModel credential = new CredentialModel();
766
credential.setType(TYPE);
767
credential.setSecretData(input.getChallengeResponse());
768
credential.setUserLabel("Custom Token");
769
770
return createCredential(realm, user, credential) != null;
771
}
772
773
@Override
774
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
775
if (!supportsCredentialType(credentialType)) {
776
return;
777
}
778
779
session.userCredentialManager()
780
.getStoredCredentialsByTypeStream(realm, user, TYPE)
781
.forEach(cred -> session.userCredentialManager().removeStoredCredential(realm, user, cred.getId()));
782
}
783
784
@Override
785
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
786
if (isConfiguredFor(realm, user, TYPE)) {
787
return Stream.of(TYPE);
788
}
789
return Stream.empty();
790
}
791
792
@Override
793
public void close() {
794
// Cleanup resources
795
}
796
797
private boolean validateToken(String inputToken, CredentialModel credential) {
798
String storedToken = credential.getSecretData();
799
return inputToken.equals(storedToken);
800
}
801
}
802
```
803
804
### Using Credential Management
805
806
```java
807
// Working with user credentials
808
try (KeycloakSession session = sessionFactory.create()) {
809
RealmModel realm = session.realms().getRealmByName("myrealm");
810
UserModel user = session.users().getUserByUsername(realm, "john");
811
812
// Get user's credential manager
813
SubjectCredentialManager credManager = user.credentialManager();
814
815
// Check if password is configured
816
boolean hasPassword = credManager.isConfiguredFor(PasswordCredentialModel.TYPE);
817
818
// Validate password
819
PasswordUserCredentialModel passwordInput = new PasswordUserCredentialModel("password123");
820
boolean isValid = credManager.isValid(passwordInput);
821
822
// Update password
823
boolean updated = credManager.updateCredential(passwordInput);
824
825
// Get all stored credentials
826
List<CredentialModel> allCredentials = credManager.getStoredCredentialsStream()
827
.collect(Collectors.toList());
828
829
// Get OTP credentials
830
List<CredentialModel> otpCredentials = credManager
831
.getStoredCredentialsStream(OTPCredentialModel.TYPE)
832
.collect(Collectors.toList());
833
834
// Remove a specific credential
835
if (!otpCredentials.isEmpty()) {
836
credManager.removeStoredCredential(otpCredentials.get(0).getId());
837
}
838
}
839
```
840
841
### Creating OTP Credentials
842
843
```java
844
// Setup OTP for a user
845
try (KeycloakSession session = sessionFactory.create()) {
846
RealmModel realm = session.realms().getRealmByName("myrealm");
847
UserModel user = session.users().getUserByUsername(realm, "alice");
848
849
// Generate secret
850
String secret = Base32.random();
851
852
// Create OTP credential from realm policy
853
OTPCredentialModel otpCredential = OTPCredentialModel.createFromPolicy(realm, secret, "My Authenticator");
854
855
// Store the credential
856
user.credentialManager().createStoredCredential(otpCredential);
857
858
// Generate QR code URL for user setup
859
String issuer = realm.getDisplayName() != null ? realm.getDisplayName() : realm.getName();
860
String qrUrl = String.format("otpauth://totp/%s:%s?secret=%s&issuer=%s",
861
issuer, user.getUsername(), secret, issuer);
862
}
863
```
864
865
### Creating Password Credentials
866
867
```java
868
// Set user password
869
try (KeycloakSession session = sessionFactory.create()) {
870
RealmModel realm = session.realms().getRealmByName("myrealm");
871
UserModel user = session.users().getUserByUsername(realm, "bob");
872
873
// Use the password credential input to set password
874
PasswordUserCredentialModel passwordInput = new PasswordUserCredentialModel("newPassword123");
875
boolean success = user.credentialManager().updateCredential(passwordInput);
876
877
if (success) {
878
System.out.println("Password updated successfully");
879
}
880
}
881
```
882
883
### Custom Password Hashing
884
885
```java
886
// Create password credential with custom hashing
887
try (KeycloakSession session = sessionFactory.create()) {
888
RealmModel realm = session.realms().getRealmByName("myrealm");
889
UserModel user = session.users().getUserByUsername(realm, "charlie");
890
891
// Hash password with custom parameters
892
String algorithm = "pbkdf2-sha256";
893
int iterations = 100000;
894
byte[] salt = SecretGenerator.getInstance().randomBytes(16);
895
String hashedPassword = Pbkdf2PasswordEncoder.encode("password123", salt, iterations);
896
897
// Create password credential
898
PasswordCredentialModel passwordCredential = PasswordCredentialModel.createFromValues(
899
algorithm, salt, iterations, hashedPassword);
900
901
// Store the credential
902
user.credentialManager().createStoredCredential(passwordCredential);
903
}
904
```