From d22756b75e54ebadbd2628a2fee00088ba060847 Mon Sep 17 00:00:00 2001 From: khang Date: Mon, 21 Apr 2025 17:33:59 +0700 Subject: [PATCH] Initial Keycloak integrantion --- .../profile/repository/IdentityClient.java | 23 ++++++++ .../profile/service/ProfileService.java | 58 +++++++++++++++++++ .../src/main/resources/application.yaml | 6 +- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 profile-service/src/main/java/com/devteria/profile/repository/IdentityClient.java diff --git a/profile-service/src/main/java/com/devteria/profile/repository/IdentityClient.java b/profile-service/src/main/java/com/devteria/profile/repository/IdentityClient.java new file mode 100644 index 0000000..bd79349 --- /dev/null +++ b/profile-service/src/main/java/com/devteria/profile/repository/IdentityClient.java @@ -0,0 +1,23 @@ +package com.devteria.profile.repository; + +import com.devteria.profile.dto.identity.TokenExchangeParam; +import com.devteria.profile.dto.identity.TokenExchangeResponse; +import com.devteria.profile.dto.identity.UserCreationParam; +import feign.QueryMap; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "identity-client", url = "${idp.url}") +public interface IdentityClient { + @PostMapping(value = "/realms/khangdjnh/protocol/openid-connect/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + TokenExchangeResponse exchangeToken(@QueryMap TokenExchangeParam tokenExchangeParam); + + @PostMapping(value = "/admin/realms/khangdjnh/users", consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity createUser( + @RequestHeader("Authorization") String token, + @RequestBody UserCreationParam userCreationParam); +} diff --git a/profile-service/src/main/java/com/devteria/profile/service/ProfileService.java b/profile-service/src/main/java/com/devteria/profile/service/ProfileService.java index 5acfb20..a7b36a4 100644 --- a/profile-service/src/main/java/com/devteria/profile/service/ProfileService.java +++ b/profile-service/src/main/java/com/devteria/profile/service/ProfileService.java @@ -1,13 +1,21 @@ package com.devteria.profile.service; +import com.devteria.profile.dto.identity.Credential; +import com.devteria.profile.dto.identity.TokenExchangeParam; +import com.devteria.profile.dto.identity.UserCreationParam; import com.devteria.profile.dto.request.RegistrationRequest; import com.devteria.profile.dto.response.ProfileResponse; import com.devteria.profile.mapper.ProfileMapper; +import com.devteria.profile.repository.IdentityClient; import com.devteria.profile.repository.ProfileRepository; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.FieldDefaults; +import lombok.experimental.NonFinal; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import java.util.List; @@ -19,6 +27,15 @@ public class ProfileService { ProfileRepository profileRepository; ProfileMapper profileMapper; + IdentityClient identityClient; + + @Value("${idp.client-id}") + @NonFinal + String clientId; + + @Value("${idp.client-secret}") + @NonFinal + String clientSecret; public List getAllProfiles(){ var profiles = profileRepository.findAll(); @@ -26,9 +43,50 @@ public List getAllProfiles(){ } public ProfileResponse register(RegistrationRequest request){ + // Create account in Keycloak + var token = identityClient.exchangeToken(TokenExchangeParam.builder() + .grant_type("client_credentials") + .client_id(clientId) + .client_secret(clientSecret) + .scope("openid") + .build()); + log.info("TokenInfo: {}", token); + //Exchange client Token + + //Create user with client Token and given info + var creationResponse = identityClient.createUser( + "Bearer " + token.getAccessToken(), + UserCreationParam.builder() + .username(request.getUsername()) + .firstName(request.getFirstName()) + .lastName(request.getLastName()) + .email(request.getEmail()) + .enabled(true) + .emailVerified(false) + .credentials(List.of(Credential.builder() + .type("password") + .value(request.getPassword()) + .temporary(false) + + .build())) + .build()); + + String userId = extractUserId(creationResponse); + log.info("UserId: {}", userId); + + + //Get userId of Keycloak account var profile = profileMapper.toProfile(request); + //set userId in UserDatabase + profile.setUserId(userId); profile = profileRepository.save(profile); return profileMapper.toProfileResponse(profile); } + + private String extractUserId(ResponseEntity responseEntity){ + String location = responseEntity.getHeaders().get("Location").getFirst(); + String[] splittedString = location.split("/"); + return splittedString[splittedString.length - 1]; + } } diff --git a/profile-service/src/main/resources/application.yaml b/profile-service/src/main/resources/application.yaml index dcb001d..59c67c1 100644 --- a/profile-service/src/main/resources/application.yaml +++ b/profile-service/src/main/resources/application.yaml @@ -7,4 +7,8 @@ spring: name: profile-service data: mongodb: - uri: mongodb://root:root@localhost:27017/profile-service?authSource=admin + uri: mongodb://admin:secret@localhost:27017/profile-service?authSource=admin +idp: + url: http://localhost:8180 + client-id: khangdjnh_app + client-secret: ZXqSgq273twSMrmx2uRsnWHK7boE8FMS \ No newline at end of file