Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.keycloak.authentication.requiredactions;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

import jakarta.ws.rs.core.MultivaluedMap;
Expand Down Expand Up @@ -145,7 +146,22 @@ public void processAction(RequiredActionContext context) {
}

try {
PasswordCredentialProvider passwordProvider = (PasswordCredentialProvider) context.getSession().getProvider(CredentialProvider.class, PasswordCredentialProviderFactory.PROVIDER_ID);
PasswordCredentialModel oldPassword = passwordProvider.getPassword(context.getRealm(), user);

user.credentialManager().updateCredential(UserCredentialModel.password(passwordNew, false));

// updateCredential returns only a boolean, so it cannot tell whether the update was handled by a
// federated CredentialInputUpdater (e.g. LDAP in WRITABLE edit mode) or by the local store. Emit the
// credential ID only when the local password row actually changed: a local write creates a new row
// or re-stamps createdDate, while a purely federated write leaves any local row untouched.
PasswordCredentialModel password = passwordProvider.getPassword(context.getRealm(), user);
if (password != null && (oldPassword == null
|| !password.getId().equals(oldPassword.getId())
|| !Objects.equals(password.getCreatedDate(), oldPassword.getCreatedDate()))) {
event.detail(Details.CREDENTIAL_ID, password.getId());
}

context.success();
deprecatedEvent.success();
} catch (ModelException me) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ public void processAction(RequiredActionContext context) {
}

context.getAuthenticationSession().removeAuthNote(Constants.TOTP_SECRET_KEY);
if (credentialModel.getId() != null) {
event.detail(Details.CREDENTIAL_ID, credentialModel.getId());
}
context.success();
deprecatedEvent.success();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.common.util.UriUtils;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.CredentialProvider;
import org.keycloak.credential.WebAuthnCredentialModelInput;
import org.keycloak.credential.WebAuthnCredentialProvider;
Expand Down Expand Up @@ -304,14 +305,15 @@ public void processAction(RequiredActionContext context) {
WebAuthnCredentialProvider webAuthnCredProvider = (WebAuthnCredentialProvider) this.session.getProvider(CredentialProvider.class, getCredentialProviderId());
WebAuthnCredentialModel newCredentialModel = webAuthnCredProvider.getCredentialModelFromCredentialInput(credential, label);

webAuthnCredProvider.createCredential(context.getRealm(), context.getUser(), newCredentialModel);
CredentialModel createdCredential = webAuthnCredProvider.createCredential(context.getRealm(), context.getUser(), newCredentialModel);

String aaguid = newCredentialModel.getWebAuthnCredentialData().getAaguid();
logger.debugv("WebAuthn credential registration success for user {0}. credentialType = {1}, publicKeyCredentialId = {2}, publicKeyCredentialLabel = {3}, publicKeyCredentialAAGUID = {4}",
context.getUser().getUsername(), getCredentialType(), publicKeyCredentialId, label, aaguid);
webAuthnCredProvider.dumpCredentialModel(newCredentialModel, credential);

context.getEvent()
.detail(Details.CREDENTIAL_ID, createdCredential.getId())
.detail(WebAuthnConstants.PUBKEY_CRED_ID_ATTR, publicKeyCredentialId)
.detail(WebAuthnConstants.PUBKEY_CRED_LABEL_ATTR, label)
.detail(WebAuthnConstants.PUBKEY_CRED_AAGUID_ATTR, aaguid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public CredentialModel createCredential(RealmModel realm, UserModel user, OTPCre
if (credentialModel.getCreatedDate() == null) {
credentialModel.setCreatedDate(Time.currentTimeMillis());
}
return user.credentialManager().createStoredCredential(credentialModel);
CredentialModel createdCredential = user.credentialManager().createStoredCredential(credentialModel);
credentialModel.setId(createdCredential.getId());
return createdCredential;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,13 @@ public void resetPassword() throws Exception {
});

EventAssertion.expectRequiredAction(events.poll()).type(EventType.UPDATE_PASSWORD);
EventAssertion.expectRequiredAction(events.poll()).type(EventType.UPDATE_CREDENTIAL).details(Details.CREDENTIAL_TYPE, PasswordCredentialModel.TYPE);
EventRepresentation updateCredentialEvent = EventAssertion.expectRequiredAction(events.poll()).type(EventType.UPDATE_CREDENTIAL).details(Details.CREDENTIAL_TYPE, PasswordCredentialModel.TYPE).getEvent();
String storedCredentialId = AdminApiUtil.findUserByUsernameId(managedRealm.admin(), "test-user@localhost").credentials().stream()
.filter(credential -> PasswordCredentialModel.TYPE.equals(credential.getType()))
.findFirst()
.orElseThrow()
.getId();
assertEquals(storedCredentialId, updateCredentialEvent.getDetails().get(Details.CREDENTIAL_ID));

MimeMessage[] receivedMessages = mail.getReceivedMessages();
Assertions.assertEquals(2, receivedMessages.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,16 @@ public void setupTotpRegister() {

Assertions.assertTrue(oauth.parseLoginResponse().isSuccess());

EventAssertion.expectRequiredAction(events.poll()).type(EventType.UPDATE_CREDENTIAL)
EventRepresentation updateCredentialEvent = EventAssertion.expectRequiredAction(events.poll()).type(EventType.UPDATE_CREDENTIAL)
.userId(userId)
.details(Details.CREDENTIAL_TYPE, OTPCredentialModel.TYPE)
.details(Details.USERNAME, "setuptotp");
.details(Details.USERNAME, "setuptotp").getEvent();
String storedCredentialId = managedRealm.admin().users().get(userId).credentials().stream()
.filter(credential -> OTPCredentialModel.TYPE.equals(credential.getType()))
.findFirst()
.orElseThrow()
.getId();
assertEquals(storedCredentialId, updateCredentialEvent.getDetails().get(Details.CREDENTIAL_ID));

Assertions.assertTrue(oauth.parseLoginResponse().isSuccess());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,13 @@ protected String usernamePasswordAuthWithAuthSetup(String username, boolean isPa
.filter(cred -> cred.getType().equals(credType))
.filter(cred -> cred.getUserLabel().equals(authenticatorLabel))
.collect(Collectors.toList()).size(), is(1));

String storedCredentialId = userRes.credentials().stream()
.filter(cred -> cred.getType().equals(credType))
.filter(cred -> cred.getUserLabel().equals(authenticatorLabel))
.map(CredentialRepresentation::getId)
.findFirst().orElseThrow();
assertThat(eventRep2.getDetails().get(Details.CREDENTIAL_ID), equalTo(storedCredentialId));
assertThat(getVirtualAuthManager().getCurrent().getAuthenticator().getCredentials().stream()
.filter(cred -> cred.isResidentCredential() == withResidentKey)
.collect(Collectors.toList()).size(), is(1));
Expand Down