diff --git a/mock-certify-plugin/pom.xml b/mock-certify-plugin/pom.xml index 949d21fb..86f697de 100644 --- a/mock-certify-plugin/pom.xml +++ b/mock-certify-plugin/pom.xml @@ -10,7 +10,7 @@ io.inji.certify mock-certify-plugin - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT jar mock-certify-integration-impl @@ -63,15 +63,15 @@ provided - io.mosip.certify + io.inji.certify certify-core - 0.12.0-SNAPSHOT + 0.14.0-SNAPSHOT provided io.mosip.esignet esignet-core - 1.5.1 + 1.6.2 * @@ -82,7 +82,7 @@ io.mosip.esignet esignet-integration-api - 1.5.1 + 1.6.2 * diff --git a/mosip-identity-certify-plugin/pom.xml b/mosip-identity-certify-plugin/pom.xml index 27dbd1aa..5ba256ee 100644 --- a/mosip-identity-certify-plugin/pom.xml +++ b/mosip-identity-certify-plugin/pom.xml @@ -8,7 +8,7 @@ 4.0.0 io.inji.certify mosip-identity-certify-plugin - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT jar mosipid-certify-integration-impl @@ -96,15 +96,15 @@ provided - io.mosip.certify + io.inji.certify certify-core - 0.12.0-SNAPSHOT + 0.14.0-SNAPSHOT provided - io.mosip.certify + io.inji.certify certify-integration-api - 0.12.0-SNAPSHOT + 0.14.0-SNAPSHOT provided @@ -146,7 +146,7 @@ io.mosip.esignet esignet-core - 1.5.1 + 1.6.2 * @@ -157,7 +157,7 @@ io.mosip.esignet esignet-integration-api - 1.5.1 + 1.6.2 * @@ -165,6 +165,11 @@ + + com.fasterxml.jackson.core + jackson-annotations + 2.15.4 + diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeRequest.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeRequest.java new file mode 100644 index 00000000..0c1a2339 --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeRequest.java @@ -0,0 +1,41 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.mosipid.integration.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class IdaKycExchangeRequest { + + private String id; + private String version; + private String requestTime; + private String transactionID; + private String kycToken; + private List consentObtained; + private List locales; + private String respType; + private String individualId; + + /** + * claims metadata - Not set/un used for now from IDA + */ + private Map metadata; + /** + * User consented verified claims list. + */ + List> verifiedConsentedClaims; + /** + * User consented unverified claims list. + */ + Map unVerifiedConsentedClaims; + +} diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeResponse.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeResponse.java new file mode 100644 index 00000000..f2cb273d --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/dto/IdaKycExchangeResponse.java @@ -0,0 +1,14 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.mosipid.integration.dto; + +import lombok.Data; + +@Data +public class IdaKycExchangeResponse { + + private String encryptedKyc; +} diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/AuthTransactionHelper.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/AuthTransactionHelper.java index fde6367f..ef9ce498 100644 --- a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/AuthTransactionHelper.java +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/AuthTransactionHelper.java @@ -27,7 +27,7 @@ @Component @Slf4j -@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@ConditionalOnProperty(value = "mosip.certify.integration.data-provider-plugin", havingValue = "IdaDataProviderPluginImpl") public class AuthTransactionHelper { private static final String AUTH_TOKEN_CACHE = "authtokens"; diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/TransactionHelper.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/TransactionHelper.java new file mode 100644 index 00000000..a6e8928a --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/TransactionHelper.java @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.mosipid.integration.helper; + +import io.mosip.esignet.core.dto.OIDCTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(value = "mosip.certify.integration.data-provider-plugin", havingValue = "IdaDataProviderPluginImpl") +public class TransactionHelper { + + @Autowired + CacheManager cacheManager; + + @Value("${mosip.certify.ida.vci-user-info-cache}") + private String userinfoCache; + + @SuppressWarnings("unchecked") + public OIDCTransaction getOAuthTransaction(String accessTokenHash) throws Exception { + if (cacheManager.getCache(userinfoCache) != null) { + return cacheManager.getCache(userinfoCache).get(accessTokenHash, OIDCTransaction.class); //NOSONAR getCache() will not be returning null here. + } + throw new Exception("cache_missing"); + } + +} diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCIAuthTransactionHelper.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCIAuthTransactionHelper.java new file mode 100644 index 00000000..ed7cfb98 --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCIAuthTransactionHelper.java @@ -0,0 +1,81 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.mosipid.integration.helper; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.certify.mosipid.integration.dto.ClientIdSecretKeyRequest; +import io.mosip.kernel.core.http.RequestWrapper; +import io.mosip.kernel.core.http.ResponseWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.time.LocalDateTime; + +@Component +@Slf4j +@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@Deprecated +public class VCIAuthTransactionHelper { + + private static final String AUTH_TOKEN_CACHE = "authtokens"; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RestTemplate restTemplate; + + @Value("${mosip.certify.authenticator.ida.auth-token-url}") + private String authTokenUrl; + + @Value("${mosip.certify.authenticator.ida.client-id}") + private String clientId; + + @Value("${mosip.certify.authenticator.ida.secret-key}") + private String secretKey; + + @Value("${mosip.certify.authenticator.ida.app-id}") + private String appId; + + @Cacheable(value = AUTH_TOKEN_CACHE, key = "#root.target.AUTH_TOKEN_CACHE_KEY") + public String getAuthToken() throws Exception { + log.info("Started to get auth-token with appId : {} && clientId : {}", + appId, clientId); + + RequestWrapper authRequest = new RequestWrapper<>(); + authRequest.setRequesttime(LocalDateTime.now()); + ClientIdSecretKeyRequest clientIdSecretKeyRequest = new ClientIdSecretKeyRequest(clientId, secretKey, appId); + authRequest.setRequest(clientIdSecretKeyRequest); + + String requestBody = objectMapper.writeValueAsString(authRequest); + RequestEntity requestEntity = RequestEntity + .post(UriComponentsBuilder.fromUriString(authTokenUrl).build().toUri()) + .contentType(MediaType.APPLICATION_JSON) + .body(requestBody); + ResponseEntity responseEntity = restTemplate.exchange(requestEntity, + new ParameterizedTypeReference() {}); + + String authToken = responseEntity.getHeaders().getFirst("authorization"); + return authToken; + } + + @CacheEvict(value = AUTH_TOKEN_CACHE, allEntries = true) + public void purgeAuthTokenCache() { + log.info("Evicting entry from AUTH_TOKEN_CACHE"); + } + +} diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCITransactionHelper.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCITransactionHelper.java index 0f02b57d..1081cbf3 100644 --- a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCITransactionHelper.java +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/helper/VCITransactionHelper.java @@ -14,6 +14,7 @@ @Component @ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@Deprecated public class VCITransactionHelper { @Autowired diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/HelperService.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/HelperService.java index e86bd951..c3bc36f1 100644 --- a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/HelperService.java +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/HelperService.java @@ -13,17 +13,20 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + import java.nio.charset.StandardCharsets; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Base64; +import java.util.*; +import java.util.stream.Collectors; @Service @Slf4j -@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@ConditionalOnProperty(value = "mosip.certify.integration.data-provider-plugin", havingValue = "IdaDataProviderPluginImpl") public class HelperService { public static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; @@ -42,7 +45,7 @@ protected String getRequestSignature(String request) { JWTSignatureRequestDto jwtSignatureRequestDto = new JWTSignatureRequestDto(); jwtSignatureRequestDto.setApplicationId(OIDC_PARTNER_APP_ID); jwtSignatureRequestDto.setReferenceId(""); - jwtSignatureRequestDto.setIncludePayload(false); + jwtSignatureRequestDto.setIncludePayload(true); jwtSignatureRequestDto.setIncludeCertificate(true); jwtSignatureRequestDto.setDataToSign(HelperService.b64Encode(request)); JWTSignatureResponseDto responseDto = signatureService.jwtSign(jwtSignatureRequestDto); @@ -60,4 +63,18 @@ protected static String b64Encode(String value) { return urlSafeEncoder.encodeToString(value.getBytes(StandardCharsets.UTF_8)); } + //Converts an array of two-letter language codes to their corresponding ISO 639-2/T language codes. + protected List convertLangCodesToISO3LanguageCodes(String[] langCodes) { + if(langCodes == null || langCodes.length == 0) + return List.of(); + return Arrays.stream(langCodes) + .map(langCode -> { + try { + return StringUtils.isEmpty(langCode) ? null : new Locale(langCode).getISO3Language(); + } catch (MissingResourceException ex) {} + return null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } } diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaDataProviderPluginImpl.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaDataProviderPluginImpl.java new file mode 100644 index 00000000..dc215743 --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaDataProviderPluginImpl.java @@ -0,0 +1,245 @@ +package io.mosip.certify.mosipid.integration.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.certify.api.exception.DataProviderExchangeException; +import io.mosip.certify.api.spi.DataProviderPlugin; +import io.mosip.certify.mosipid.integration.dto.*; +import io.mosip.certify.mosipid.integration.helper.TransactionHelper; +import io.mosip.esignet.api.dto.*; +import io.mosip.esignet.api.exception.KycExchangeException; +import io.mosip.esignet.core.dto.OIDCTransaction; +import io.mosip.kernel.core.keymanager.spi.KeyStore; +import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant; +import io.mosip.kernel.keymanagerservice.entity.KeyAlias; +import io.mosip.kernel.keymanagerservice.helper.KeymanagerDBHelper; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.crypto.Cipher; +import java.security.Key; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.*; + +@Component +@Slf4j +@ConditionalOnProperty(value = "mosip.certify.integration.data-provider-plugin", havingValue = "IdaDataProviderPluginImpl") +public class IdaDataProviderPluginImpl implements DataProviderPlugin { + // TODO: Clean up code + // TODO: Write unit tests + + private static final String ACCESS_TOKEN_HASH = "accessTokenHash"; + public static final String SIGNATURE_HEADER_NAME = "signature"; + public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + public static final String OIDC_SERVICE_APP_ID = "CERTIFY_SERVICE"; + public static final String AES_CIPHER_FAILED = "aes_cipher_failed"; + public static final String NO_UNIQUE_ALIAS = "no_unique_alias"; + + @Value("${mosip.certify.authenticator.ida-version:1.0}") + private String idaVersion; + + @Value("${mosip.certify.authenticator.ida.kyc-exchange-url}") + private String kycExchangeUrl; + + @Value("${mosip.certify.ida.kyc-exchange-id:mosip.identity.kycexchange}") + private String kycExchangeId; + + @Value("${mosip.certify.cache.secure.individual-id}") + private boolean secureIndividualId; + + @Value("${mosip.certify.cache.store.individual-id}") + private boolean storeIndividualId; + + @Value("${mosip.certify.cache.security.algorithm-name}") + private String aesECBTransformation; + + @Value("${mosip.certify.cache.security.secretkey.reference-id}") + private String cacheSecretKeyRefId; + + @Value("#{'${mosip.certify.ida.kyc-exchange.accepted-claims}'.split(',')}") + private List kycExchangeAcceptedClaims; + + @Value("${mosip.certify.ida.kyc-exchange.accepted-locales:en}") + private String[] kycAcceptedLocales; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RestTemplate restTemplate; + + @Autowired + HelperService helperService; + + @Autowired + private KeyStore keyStore; + + @Autowired + private KeymanagerDBHelper dbHelper; + + @Autowired + TransactionHelper transactionHelper; + + private Base64.Decoder urlSafeDecoder = Base64.getUrlDecoder(); + + @Override + public JSONObject fetchData(Map identityDetails) throws DataProviderExchangeException { + try { + KycExchangeResult kycExchangeResult = doKycExchange(identityDetails); + if(kycExchangeResult != null) { + log.info("Kyc Exchange Success."); + String encryptedKyc = kycExchangeResult.getEncryptedKyc(); + Map claims = decodeClaimsFromJwt(encryptedKyc); + + return new JSONObject(claims); + } + } + catch (JSONException | JsonProcessingException e) { + log.error("Error occurred during json processing: " + e.getMessage()); + throw new DataProviderExchangeException("JSON_PARSING_FAILED", e.getMessage()); + } + catch (Exception e) { + log.error("ERROR_FETCHING_KYC_DATA. " + e.getMessage()); + throw new DataProviderExchangeException("ERROR_FETCHING_KYC_DATA", e.getMessage()); + } + throw new DataProviderExchangeException("ERROR_FETCHING_KYC_DATA", "No data found for kyc exchange"); + } + + private KycExchangeDto buildKycExchangeDto(OIDCTransaction transaction) throws Exception { + KycExchangeDto kycExchangeDto = new KycExchangeDto(); + + String individualId = getIndividualId(transaction.getIndividualId()); + kycExchangeDto.setIndividualId(individualId); + kycExchangeDto.setTransactionId(transaction.getAuthTransactionId()); + kycExchangeDto.setKycToken(transaction.getKycToken()); + kycExchangeDto.setAcceptedClaims(kycExchangeAcceptedClaims); + kycExchangeDto.setClaimsLocales(kycAcceptedLocales); + kycExchangeDto.setUserInfoResponseType(null); + + log.info("Built the KYC exchange DTO"); + + return kycExchangeDto; + } + + protected String getIndividualId(String encryptedIndividualId) throws Exception { + if (!storeIndividualId) + return null; + return secureIndividualId ? decryptIndividualId(encryptedIndividualId) : encryptedIndividualId; + } + + private String decryptIndividualId(String encryptedIndividualId) throws Exception { + try { + Cipher cipher = Cipher.getInstance(aesECBTransformation); + byte[] decodedBytes = b64Decode(encryptedIndividualId); + cipher.init(Cipher.DECRYPT_MODE, getSecretKeyFromHSM()); + return new String(cipher.doFinal(decodedBytes, 0, decodedBytes.length)); + } catch (Exception e) { + log.error("Error Cipher Operations of provided secret data.", e); + throw new Exception(AES_CIPHER_FAILED); + } + } + + private Key getSecretKeyFromHSM() throws Exception { + String keyAlias = getKeyAlias(OIDC_SERVICE_APP_ID, cacheSecretKeyRefId); + if (Objects.nonNull(keyAlias)) { + return keyStore.getSymmetricKey(keyAlias); + } + throw new Exception(NO_UNIQUE_ALIAS); + } + + private String getKeyAlias(String keyAppId, String keyRefId) throws Exception { + Map> keyAliasMap = dbHelper.getKeyAliases(keyAppId, keyRefId, + LocalDateTime.now(ZoneOffset.UTC)); + List currentKeyAliases = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS); + if (!currentKeyAliases.isEmpty() && currentKeyAliases.size() == 1) { + return currentKeyAliases.get(0).getAlias(); + } + log.error("CurrentKeyAlias is not unique. KeyAlias count: {}", currentKeyAliases.size()); + throw new Exception(NO_UNIQUE_ALIAS); + } + + private byte[] b64Decode(String value) { + return urlSafeDecoder.decode(value); + }; + + public KycExchangeResult doKycExchange(Map identityDetails) + throws Exception { + OIDCTransaction transaction = transactionHelper + .getOAuthTransaction(identityDetails.get(ACCESS_TOKEN_HASH).toString()); + KycExchangeDto kycExchangeDto = buildKycExchangeDto(transaction); + String relyingPartyId = transaction.getRelyingPartyId(); + String clientId = transaction.getClientId(); + return kycExchange(relyingPartyId, clientId, kycExchangeDto); + } + + private KycExchangeResult kycExchange(String relyingPartyId, String clientId, KycExchangeDto kycExchangeDto) + throws KycExchangeException { + log.info("Started to build kyc-exchange request with transactionId : {} && clientId : {}", + kycExchangeDto.getTransactionId(), clientId); + try { + IdaKycExchangeRequest idaKycExchangeRequest = new IdaKycExchangeRequest(); + idaKycExchangeRequest.setId(kycExchangeId); + idaKycExchangeRequest.setVersion(idaVersion); + idaKycExchangeRequest.setRequestTime(HelperService.getUTCDateTime()); + idaKycExchangeRequest.setTransactionID(kycExchangeDto.getTransactionId()); + idaKycExchangeRequest.setKycToken(kycExchangeDto.getKycToken()); + idaKycExchangeRequest.setConsentObtained(kycExchangeDto.getAcceptedClaims()); + idaKycExchangeRequest.setLocales(helperService.convertLangCodesToISO3LanguageCodes(kycExchangeDto.getClaimsLocales())); + idaKycExchangeRequest.setRespType(kycExchangeDto.getUserInfoResponseType()); // Setting the Response Type to null will give the final result as a JWT + idaKycExchangeRequest.setIndividualId(kycExchangeDto.getIndividualId()); + + log.info("Built the kyc exchange request"); + + //set signature header, body and invoke kyc exchange endpoint + String requestBody = objectMapper.writeValueAsString(idaKycExchangeRequest); + RequestEntity requestEntity = RequestEntity + .post(UriComponentsBuilder.fromUriString(kycExchangeUrl).pathSegment(relyingPartyId, + clientId).build().toUri()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .header(SIGNATURE_HEADER_NAME, helperService.getRequestSignature(requestBody)) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_NAME) + .body(requestBody); + ResponseEntity> responseEntity = restTemplate.exchange(requestEntity, + new ParameterizedTypeReference>() {}); + + if(responseEntity.getStatusCode().is2xxSuccessful() && responseEntity.getBody() != null) { + IdaResponseWrapper responseWrapper = responseEntity.getBody(); + if(responseWrapper.getResponse() != null && responseWrapper.getResponse().getEncryptedKyc() != null) { + return new KycExchangeResult(responseWrapper.getResponse().getEncryptedKyc()); + } + log.error("Errors in response received from IDA Kyc Exchange: {}", responseWrapper.getErrors()); + throw new KycExchangeException(CollectionUtils.isEmpty(responseWrapper.getErrors()) ? + io.mosip.esignet.api.util.ErrorConstants.DATA_EXCHANGE_FAILED : responseWrapper.getErrors().get(0).getErrorCode()); + } + + log.error("Error response received from IDA (Kyc-exchange) with status : {}", responseEntity.getStatusCode()); + } catch (KycExchangeException e) { throw e; } catch (Exception e) { + log.error("IDA Kyc-exchange failed with clientId : {}", clientId, e); + } + throw new KycExchangeException(); + } + + private Map decodeClaimsFromJwt(String jwtToken) throws JsonProcessingException, DataProviderExchangeException { + String[] parts = jwtToken.split("\\."); + if(parts.length < 3) { + throw new DataProviderExchangeException("Invalid KYC Exchange response."); + } + String payload = new String(urlSafeDecoder.decode(parts[1])); + Map claims = objectMapper.readValue(payload, Map.class); + + return claims; + } +} diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImpl.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImpl.java index 5f7120c5..0dadcb34 100644 --- a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImpl.java +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImpl.java @@ -48,6 +48,7 @@ @Component @Slf4j @ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@Deprecated public class IdaVCIssuancePluginImpl implements VCIssuancePlugin { private static final String CLIENT_ID = "client_id"; private static final String ACCESS_TOKEN_HASH = "accessTokenHash"; @@ -64,7 +65,7 @@ public class IdaVCIssuancePluginImpl implements VCIssuancePlugin { private RestTemplate restTemplate; @Autowired - HelperService helperService; + VCIHelperService helperService; @Autowired private KeyStore keyStore; diff --git a/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/VCIHelperService.java b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/VCIHelperService.java new file mode 100644 index 00000000..ae40ee29 --- /dev/null +++ b/mosip-identity-certify-plugin/src/main/java/io/mosip/certify/mosipid/integration/service/VCIHelperService.java @@ -0,0 +1,66 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +package io.mosip.certify.mosipid.integration.service; + + +import io.mosip.kernel.signature.dto.JWTSignatureRequestDto; +import io.mosip.kernel.signature.dto.JWTSignatureResponseDto; +import io.mosip.kernel.signature.service.SignatureService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.nio.charset.StandardCharsets; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + + +@Service +@Slf4j +@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "IdaVCIssuancePluginImpl") +@Deprecated +public class VCIHelperService { + + public static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + public static final String OIDC_PARTNER_APP_ID = "CERTIFY_PARTNER"; + private static Base64.Encoder urlSafeEncoder; + + static { + urlSafeEncoder = Base64.getUrlEncoder().withoutPadding(); + } + + + @Autowired + private SignatureService signatureService; + + protected String getRequestSignature(String request) { + JWTSignatureRequestDto jwtSignatureRequestDto = new JWTSignatureRequestDto(); + jwtSignatureRequestDto.setApplicationId(OIDC_PARTNER_APP_ID); + jwtSignatureRequestDto.setReferenceId(""); + jwtSignatureRequestDto.setIncludePayload(false); + jwtSignatureRequestDto.setIncludeCertificate(true); + jwtSignatureRequestDto.setDataToSign(HelperService.b64Encode(request)); + JWTSignatureResponseDto responseDto = signatureService.jwtSign(jwtSignatureRequestDto); + log.debug("Request signature ---> {}", responseDto.getJwtSignedData()); + return responseDto.getJwtSignedData(); + } + + protected static String getUTCDateTime() { + return ZonedDateTime + .now(ZoneOffset.UTC) + .format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN)); + } + + protected static String b64Encode(String value) { + return urlSafeEncoder.encodeToString(value.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/mosip-identity-certify-plugin/src/test/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImplTest.java b/mosip-identity-certify-plugin/src/test/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImplTest.java index f94861c4..b727645a 100644 --- a/mosip-identity-certify-plugin/src/test/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImplTest.java +++ b/mosip-identity-certify-plugin/src/test/java/io/mosip/certify/mosipid/integration/service/IdaVCIssuancePluginImplTest.java @@ -58,7 +58,7 @@ public class IdaVCIssuancePluginImplTest { RestTemplate restTemplate; @Mock - HelperService helperService; + VCIHelperService helperService; @Mock KeymanagerDBHelper keymanagerDBHelper; diff --git a/postgres-dataprovider-plugin/pom.xml b/postgres-dataprovider-plugin/pom.xml index 8ae69f38..5d8dea2c 100644 --- a/postgres-dataprovider-plugin/pom.xml +++ b/postgres-dataprovider-plugin/pom.xml @@ -4,7 +4,7 @@ io.inji.certify postgres-dataprovider-plugin - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT jar postgres-dataprovider-plugin @@ -56,9 +56,9 @@ provided - io.mosip.certify + io.inji.certify certify-core - 0.12.0-SNAPSHOT + 0.14.0-SNAPSHOT provided diff --git a/sunbird-rc-certify-integration-impl/pom.xml b/sunbird-rc-certify-integration-impl/pom.xml index c3856215..53a92da4 100644 --- a/sunbird-rc-certify-integration-impl/pom.xml +++ b/sunbird-rc-certify-integration-impl/pom.xml @@ -8,7 +8,7 @@ 4.0.0 io.inji.certify.sunbirdrc sunbird-rc-certify-integration-impl - 0.5.0-SNAPSHOT + 0.6.0-SNAPSHOT jar sunbird-rc-certify-integration-impl @@ -52,9 +52,9 @@ - io.mosip.certify + io.inji.certify certify-integration-api - 0.12.0-SNAPSHOT + 0.14.0-SNAPSHOT provided