KeycloakUserLoader.java
package com.project.auth.application.auth.identity.internal;
import com.project.auth.application.auth.exception.KeycloakAccountConflictException;
import com.project.auth.application.auth.identity.KeycloakUserClaims;
import com.project.auth.application.auth.identity.LoadKeycloakUserUseCase;
import com.project.auth.application.auth.identity.LoadedKeycloakUser;
import com.project.auth.application.auth.identity.port.out.LoadKeycloakUserPort;
import com.project.auth.application.auth.identity.port.out.RegisterKeycloakUserPort;
import com.project.auth.application.support.audit.AuthAuditEvent;
import com.project.auth.application.support.audit.AuthAuditEventPublisher;
import com.project.auth.application.support.audit.AuthAuditEventType;
import com.project.auth.application.support.audit.AuthAuditFields;
import com.project.auth.domain.user.model.AuthProvider;
import com.project.auth.domain.user.model.User;
import org.springframework.transaction.annotation.Transactional;
import java.time.Clock;
import java.time.Instant;
import java.util.Objects;
import java.util.UUID;
public class KeycloakUserLoader implements LoadKeycloakUserUseCase {
private final LoadKeycloakUserPort loadKeycloakUserPort;
private final RegisterKeycloakUserPort registerKeycloakUserPort;
private final AuthAuditEventPublisher authAuditEventPublisher;
private final Clock clock;
public KeycloakUserLoader(
LoadKeycloakUserPort loadKeycloakUserPort,
RegisterKeycloakUserPort registerKeycloakUserPort,
AuthAuditEventPublisher authAuditEventPublisher,
Clock clock
) {
this.loadKeycloakUserPort = Objects.requireNonNull(loadKeycloakUserPort, "loadKeycloakUserPort must not be null");
this.registerKeycloakUserPort = Objects.requireNonNull(
registerKeycloakUserPort,
"registerKeycloakUserPort must not be null"
);
this.authAuditEventPublisher = Objects.requireNonNull(
authAuditEventPublisher,
"authAuditEventPublisher must not be null"
);
this.clock = Objects.requireNonNull(clock, "clock must not be null");
}
@Override
@Transactional
public LoadedKeycloakUser load(KeycloakUserClaims claims) {
ValidatedKeycloakUserClaims validatedClaims = KeycloakUserClaimsValidator.validate(claims);
User user = loadKeycloakUserPort.findByProviderAndProviderSubject(AuthProvider.KEYCLOAK, validatedClaims.subject())
.orElseGet(() -> autoRegister(validatedClaims));
return LoadedKeycloakUser.from(user);
}
private User autoRegister(ValidatedKeycloakUserClaims claims) {
if (loadKeycloakUserPort.existsByEmail(claims.email())) {
throw new KeycloakAccountConflictException();
}
User newUser = User.registerKeycloak(
UUID.randomUUID(),
claims.email(),
claims.name(),
claims.subject(),
Instant.now(clock)
);
User saved = registerKeycloakUserPort.register(newUser);
authAuditEventPublisher.publish(AuthAuditEvent.info(
AuthAuditEventType.KEYCLOAK_USER_AUTO_REGISTERED.code(),
AuthAuditFields.PROVIDER, AuthProvider.KEYCLOAK,
AuthAuditFields.SUBJECT, claims.subject()
));
return saved;
}
}