Add clientName and clientUrl to challenge

This commit is contained in:
Giuliano Mele 2023-11-30 11:28:24 +01:00
parent 1aaaee60da
commit 432021aa6e
Signed by: MelGi
GPG key ID: E790C1211F6DEE5E
8 changed files with 75 additions and 10 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ dependency-reduced-pom.xml
.classpath
.project
.settings
.jpb

View file

@ -19,6 +19,7 @@ import org.keycloak.device.DeviceRepresentationProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.representations.account.DeviceRepresentation;
@ -98,7 +99,7 @@ public class AppAuthenticator implements Authenticator, CredentialValidator<AppC
logger.infov("App authentication signature string\n\n{0}\n", AuthenticationUtil.getSignatureString(signatureStringMap));
}
MessagingServiceFactory.get(authConfig).send(appCredentialData.getDevicePushId(), ChallengeConverter.getChallengeDto(challenge));
MessagingServiceFactory.get(authConfig).send(appCredentialData.getDevicePushId(), ChallengeConverter.getChallengeDto(challenge, context.getSession()));
Response response = context.form()
.setAttribute("appAuthStatusUrl", String.format(
@ -123,6 +124,7 @@ public class AppAuthenticator implements Authenticator, CredentialValidator<AppC
EntityManager em = getEntityManager(context.getSession());
RealmEntity realm = em.getReference(RealmEntity.class, context.getRealm().getId());
UserEntity user = em.getReference(UserEntity.class, context.getUser().getId());
ClientEntity client = em.getReference(ClientEntity.class, context.getAuthenticationSession().getClient().getId());
try {
TypedQuery<Challenge> query = em.createNamedQuery("Challenge.findByRealmAndDeviceId", Challenge.class);
@ -154,6 +156,7 @@ public class AppAuthenticator implements Authenticator, CredentialValidator<AppC
challenge.setOsVersion(deviceRepresentation.getOsVersion());
challenge.setIpAddress(deviceRepresentation.getIpAddress());
challenge.setUpdatedTimestamp(Time.currentTimeMillis());
challenge.setClient(client);
em.persist(challenge);
em.flush();

View file

@ -1,9 +1,17 @@
package netzbegruenung.keycloak.app.dto;
import netzbegruenung.keycloak.app.jpa.Challenge;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.util.ResolveRelative;
import org.keycloak.theme.Theme;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
public class ChallengeConverter {
public static ChallengeDto getChallengeDto(Challenge challenge) {
public static ChallengeDto getChallengeDto(Challenge challenge, KeycloakSession session) {
return new ChallengeDto(
challenge.getUser().getUsername(),
challenge.getUser().getFirstName(),
@ -15,7 +23,21 @@ public class ChallengeConverter {
challenge.getDevice(),
challenge.getBrowser(),
challenge.getOs(),
challenge.getOsVersion()
challenge.getOsVersion(),
resolveClientName(challenge.getClient().getName(), session),
ResolveRelative.resolveRelativeUri(session, challenge.getClient().getRootUrl(), challenge.getClient().getBaseUrl())
);
}
private static String resolveClientName(String clientName, KeycloakSession session) {
return StringPropertyReplacer.replaceProperties(clientName, getProperties(session));
}
private static Properties getProperties(KeycloakSession session) {
try {
return session.theme().getTheme(Theme.Type.ACCOUNT).getMessages(Locale.ENGLISH);
} catch (IOException e) {
return null;
}
}
}

View file

@ -18,8 +18,10 @@ public class ChallengeDto implements Serializable {
private final String browser;
private final String os;
private final String osVersion;
private final String clientName;
private final String clientUrl;
public ChallengeDto(String userName, String userFirstName, String userLastName, String targetUrl, String codeChallenge, Long updatedTimestamp, String ipAddress, String device, String browser, String os, String osVersion) {
public ChallengeDto(String userName, String userFirstName, String userLastName, String targetUrl, String codeChallenge, Long updatedTimestamp, String ipAddress, String device, String browser, String os, String osVersion, String clientName, String clientUrl) {
this.userName = userName;
this.userFirstName = userFirstName;
this.userLastName = userLastName;
@ -31,6 +33,8 @@ public class ChallengeDto implements Serializable {
this.browser = browser;
this.os = os;
this.osVersion = osVersion;
this.clientName = clientName;
this.clientUrl = clientUrl;
}
public String getUserName() {
@ -77,6 +81,14 @@ public class ChallengeDto implements Serializable {
return osVersion;
}
public String getClientName() {
return clientName;
}
public String getClientUrl() {
return clientUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -92,11 +104,13 @@ public class ChallengeDto implements Serializable {
Objects.equals(this.device, entity.device) &&
Objects.equals(this.browser, entity.browser) &&
Objects.equals(this.os, entity.os) &&
Objects.equals(this.osVersion, entity.osVersion);
Objects.equals(this.osVersion, entity.osVersion) &&
Objects.equals(this.clientName, entity.clientName) &&
Objects.equals(this.clientUrl, entity.clientUrl);
}
@Override
public int hashCode() {
return Objects.hash(userName, userFirstName, userLastName, targetUrl, codeChallenge, updatedTimestamp, ipAddress, device, browser, os, osVersion);
return Objects.hash(userName, userFirstName, userLastName, targetUrl, codeChallenge, updatedTimestamp, ipAddress, device, browser, os, osVersion, clientName, clientUrl);
}
}

View file

@ -2,6 +2,7 @@ package netzbegruenung.keycloak.app.jpa;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.UserEntity;
@ -35,6 +36,11 @@ public class Challenge {
@OnDelete(action = OnDeleteAction.CASCADE)
private UserEntity user;
@ManyToOne(optional = false)
@JoinColumn(name = "client_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private ClientEntity client;
@Column(name = "target_url", nullable = false, length = 1023)
private String targetUrl;
@ -154,4 +160,11 @@ public class Challenge {
this.osVersion = osVersion;
}
public ClientEntity getClient() {
return client;
}
public void setClient(ClientEntity client) {
this.client = client;
}
}

View file

@ -118,7 +118,7 @@ public class ChallengeResource {
}
return Response
.ok(Arrays.asList(ChallengeConverter.getChallengeDto(challenge)))
.ok(Arrays.asList(ChallengeConverter.getChallengeDto(challenge, session)))
.build();
} catch (IllegalStateException e) {

View file

@ -40,4 +40,16 @@
<addForeignKeyConstraint baseColumnNames="user_id" baseTableName="app_auth_challenge" constraintName="FK_APP_AUTH_CHALLENGE_ON_USER" onDelete="CASCADE" referencedColumnNames="id" referencedTableName="user_entity"/>
</changeSet>
</databaseChangeLog>
<changeSet id="app-auth-challenge-23.0.2" author="giuliano.mele@verdigado.com">
<delete tableName="app_auth_challenge">
</delete>
<addColumn tableName="app_auth_challenge">
<column name="client_id" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</addColumn>
<addForeignKeyConstraint baseTableName="app_auth_challenge" baseColumnNames="client_id" onDelete="CASCADE"
referencedTableName="client" referencedColumnNames="ID" constraintName="FK_APP_AUTH_CHALLENGE_ON_CLIENT"/>
</changeSet>
</databaseChangeLog>

View file

@ -1,2 +1,2 @@
INSERT INTO APP_AUTH_CHALLENGE (id, realm_id, user_id, target_url, device_id, secret, updated_timestamp, ip_address, device, browser, os, os_version)
VALUES (random_uuid(), 'baeldung', 'a5461470-33eb-4b2d-82d4-b0484e96ad7f', 'target_url', 'test_device_id', 'secret', DATEDIFF('SECOND', DATE '1970-01-01', CURRENT_TIMESTAMP()) * 1000, 'ip_address', 'device', 'browser', 'os', 'os_version');
INSERT INTO APP_AUTH_CHALLENGE (id, realm_id, user_id, client_id, target_url, device_id, secret, updated_timestamp, ip_address, device, browser, os, os_version)
VALUES (random_uuid(), 'baeldung', 'a5461470-33eb-4b2d-82d4-b0484e96ad7f', '12eebf0b-a3eb-49f8-9ecf-173cf8a00145', 'target_url', 'test_device_id', 'secret', DATEDIFF('SECOND', DATE '1970-01-01', CURRENT_TIMESTAMP()) * 1000, 'ip_address', 'device', 'browser', 'os', 'os_version');