diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java index 59d3ca494a9..3e67909c7e6 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/config/IDAMappingConfig.java @@ -129,4 +129,6 @@ public class IDAMappingConfig implements MappingConfig { /** The password. */ private List password; + + private List verifiedAttributes; } diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/entity/OIDCClientData.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/entity/OIDCClientData.java index 2ac3ddcc34c..fbeacec4153 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/entity/OIDCClientData.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/entity/OIDCClientData.java @@ -1,7 +1,6 @@ package io.mosip.authentication.common.service.entity; import java.time.LocalDateTime; -import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/facade/AuthFacadeImpl.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/facade/AuthFacadeImpl.java index 3a3b27878cf..e059591cab5 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/facade/AuthFacadeImpl.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/facade/AuthFacadeImpl.java @@ -49,6 +49,7 @@ import io.mosip.authentication.core.indauth.dto.IdType; import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTOV2; import io.mosip.authentication.core.indauth.dto.EkycAuthRequestDTO; import io.mosip.authentication.core.logger.IdaLogger; import io.mosip.authentication.core.partner.dto.PartnerPolicyResponseDTO; @@ -175,6 +176,9 @@ public AuthResponseDTO authenticateIndividual(AuthRequestDTO authRequestDTO, boo filterAttributes.add(IdaIdMapping.PASSWORD.getIdname()); } } + if(authRequestDTO instanceof KycAuthRequestDTOV2) { + filterAttributes.add(IdaIdMapping.VERIFIEDATTRIBUTES.getIdname()); + } Map idResDTO = idService.processIdType(idvIdType, idvid, idInfoHelper.isBiometricDataNeeded(authRequestDTO), markVidConsumed, filterAttributes); diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/match/IdaIdMapping.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/match/IdaIdMapping.java index 4e3ccd80650..b7a7081747c 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/match/IdaIdMapping.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/impl/match/IdaIdMapping.java @@ -185,6 +185,8 @@ public BiFunction> getMappingFunction() { PASSWORD("password", MappingConfig::getPassword), + VERIFIEDATTRIBUTES("verifiedAttributes", MappingConfig::getVerifiedAttributes), + /** The dynamic demographics ID Mapping. */ DYNAMIC("demographics") { diff --git a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java index 02b630f5cca..a1e07556892 100644 --- a/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java +++ b/authentication/authentication-common/src/main/java/io/mosip/authentication/common/service/validator/AuthRequestValidator.java @@ -30,6 +30,7 @@ import io.mosip.authentication.core.indauth.dto.DataDTO; import io.mosip.authentication.core.indauth.dto.DigitalId; import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthRequestDTOV2; import io.mosip.authentication.core.indauth.dto.RequestDTO; import io.mosip.authentication.core.logger.IdaLogger; import io.mosip.kernel.core.exception.ParseException; @@ -99,7 +100,8 @@ public void initialize() { */ @Override public boolean supports(Class clazz) { - return AuthRequestDTO.class.equals(clazz) || KycAuthRequestDTO.class.equals(clazz); + return AuthRequestDTO.class.equals(clazz) || KycAuthRequestDTO.class.equals(clazz) + || KycAuthRequestDTOV2.class.equals(clazz); } /** diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditEvents.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditEvents.java index 566996881ca..2a78ebc28b7 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditEvents.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditEvents.java @@ -51,6 +51,8 @@ public enum AuditEvents { PASSWORD_BASED_AUTH_REQUEST("IDA_018", "System", "Password Based Auth Request"), + KYC_REQUEST_RESPONSE_V2("IDA_019", "System", "Kyc Auth V2 Request"), + /** Static_Pin_Storage_Request_Response. */ STATIC_PIN_STORAGE_REQUEST_RESPONSE("IDA-EVT-OLD-006","BUSINESS", ""),//not applicable for release v1 diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditModules.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditModules.java index 0069e5c6867..0b576a22b21 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditModules.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/AuditModules.java @@ -31,6 +31,8 @@ public enum AuditModules { KYC_AUTH("IDA-KAT", "KYC Authentication Request", "KYC Authenticator"), + KYC_AUTH_V2("IDA-KAT-V2", "KYC Authentication Request for V2", "KYC Authenticator V2"), + KYC_EXCHANGE("IDA-KEX", "KYC Exchange Request", "KYC Exchange"), VCI_EXCHANGE("IDA-VCI", "VCI Exchange Request", "VCI Exchange"), diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthCommonConstants.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthCommonConstants.java index 96ec2319a51..8eaa307d01f 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthCommonConstants.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/constant/IdAuthCommonConstants.java @@ -469,6 +469,13 @@ public final class IdAuthCommonConstants { public static final String MISP_LIC_DATA = "misp_lic_data"; + public static final String VERIFIED_CLAIMS_ATTRIBS = "verifiedAttributes"; + + public static final String NULL_CONST = "=null"; + + public static final String COMMA_STRING = ","; + + private IdAuthCommonConstants() { } } diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRequestDTOV2.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRequestDTOV2.java new file mode 100644 index 00000000000..e0af3dd7ae0 --- /dev/null +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRequestDTOV2.java @@ -0,0 +1,11 @@ +package io.mosip.authentication.core.indauth.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class KycAuthRequestDTOV2 extends AuthRequestDTO { + + private KycRequestDTOV2 request; +} diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRespDTOV2.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRespDTOV2.java new file mode 100644 index 00000000000..4426c93b2bf --- /dev/null +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthRespDTOV2.java @@ -0,0 +1,16 @@ +package io.mosip.authentication.core.indauth.dto; + +import lombok.Data; + +/** + * The class for KycAuthRespDTOV2 Holds the values for Kyc Token and Auth Token (PSU Token). + * + * @author Mahammed Taheer + */ + +@Data +public class KycAuthRespDTOV2 extends KycAuthRespDTO{ + + /** The Variable to hold value of verified claims */ + private String verifiedClaimsMetadata; +} diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthResponseDTOV2.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthResponseDTOV2.java new file mode 100644 index 00000000000..61e89d576a4 --- /dev/null +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycAuthResponseDTOV2.java @@ -0,0 +1,19 @@ +package io.mosip.authentication.core.indauth.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * The Class For KycAuthResponseDTO extending {@link BaseAuthResponseDTO} + * + * @author Mahammed Taheer + */ + +@Data +@EqualsAndHashCode(callSuper=true) +public class KycAuthResponseDTOV2 extends BaseAuthResponseDTO { + + /** The KycResponseDTO */ + private KycAuthRespDTOV2 response; + +} diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycRequestDTOV2.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycRequestDTOV2.java new file mode 100644 index 00000000000..cf97a28c430 --- /dev/null +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/indauth/dto/KycRequestDTOV2.java @@ -0,0 +1,17 @@ +package io.mosip.authentication.core.indauth.dto; + + +import lombok.Data; + +import java.util.List; + +@Data +public class KycRequestDTOV2 extends RequestDTO { + + /** H/W or S/W token */ + private List keyBindedTokens; + + private String password; + + private boolean claimsMetadataRequired; +} diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/facade/KycFacade.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/facade/KycFacade.java index 061275376af..c38f36f77ae 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/facade/KycFacade.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/facade/KycFacade.java @@ -12,6 +12,7 @@ import io.mosip.authentication.core.indauth.dto.EKycAuthResponseDTO; import io.mosip.authentication.core.indauth.dto.EkycAuthRequestDTO; import io.mosip.authentication.core.indauth.dto.KycAuthResponseDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthResponseDTOV2; import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTO; import io.mosip.authentication.core.indauth.dto.KycExchangeResponseDTO; @@ -65,8 +66,6 @@ AuthResponseDTO authenticateIndividual(AuthRequestDTO authRequest, boolean reque AuthResponseDTO authenticateIndividual(AuthRequestDTO authRequest, boolean request, String partnerId, String partnerApiKey, ObjectWithMetadata requestWithMetadata, boolean markVidConsumed) throws IdAuthenticationBusinessException, IdAuthenticationDaoException; - - /** * Process the KycAuthRequestDTO to integrate with KYCService. @@ -82,7 +81,6 @@ AuthResponseDTO authenticateIndividual(AuthRequestDTO authRequest, boolean reque KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestDTO, AuthResponseDTO authResponseDTO, String partnerId, String oidcClientId, Map metadata) throws IdAuthenticationBusinessException; - /** * Process the KycExchangeRequestDTO to integrate with KYCService. * @@ -98,4 +96,16 @@ KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestDTO, Aut KycExchangeResponseDTO processKycExchange(KycExchangeRequestDTO kycExchangeRequestDTO, String partnerId, String oidcClientId, Map metadata, ObjectWithMetadata requestWithMetadata) throws IdAuthenticationBusinessException; + /** + * Process the KycAuthRequestDTOV2 to integrate with KYCService. + * + * @param kycAuthRequestDtoV2 is DTO of KycAuthRequestDTOV2 + * @param authResponseDTO the auth response DTO + * @param partnerId the partner id + * @param metadata the metadata + * @return the kyc auth response V2 DTO + * @throws IdAuthenticationBusinessException the id authentication business exception + */ + KycAuthResponseDTOV2 processKycAuthV2(@Nonnull AuthRequestDTO kycAuthRequestDtoV2, AuthResponseDTO authResponseDTO, + String partnerId, String oidcClientId, Map metadata) throws IdAuthenticationBusinessException; } diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/IdInfoFetcher.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/IdInfoFetcher.java index 237e97b6a22..727e6253492 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/IdInfoFetcher.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/IdInfoFetcher.java @@ -3,10 +3,12 @@ import static io.mosip.authentication.core.constant.IdAuthCommonConstants.PASSWORD; import static io.mosip.authentication.core.constant.IdAuthCommonConstants.SEMI_COLON; import static io.mosip.authentication.core.constant.IdAuthCommonConstants.COLON; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.VERIFIED_CLAIMS_ATTRIBS; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; @@ -15,6 +17,9 @@ import org.springframework.core.env.Environment; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + import io.mosip.authentication.core.exception.IdAuthenticationBusinessException; import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; import io.mosip.authentication.core.indauth.dto.RequestDTO; @@ -199,7 +204,7 @@ public Map>> getCbeffValues(Map> getIdInfo(Map idResponseDTO) { + public static Map> getIdInfo(Map idResponseDTO, ObjectMapper objectMapper) { return idResponseDTO.entrySet().stream().flatMap(entry -> { if (entry.getValue() instanceof Map) { return ((Map) entry.getValue()).entrySet().stream(); @@ -244,14 +249,29 @@ public static Map> getIdInfo(Map i .map(mapEntry -> mapEntry.getKey().trim() + String.valueOf(COLON) + mapEntry.getValue().trim()) .collect(Collectors.joining(SEMI_COLON)); IdentityInfoDTO idInfo = new IdentityInfoDTO(); - idInfo.setValue(String.valueOf(passwordData)); + idInfo.setValue(passwordData); + return Stream.of(idInfo).collect(Collectors.toList()); + } else if (entry.getKey().equals(VERIFIED_CLAIMS_ATTRIBS) && val instanceof Map) { + Map map = (Map) val; + IdentityInfoDTO idInfo = new IdentityInfoDTO(); + try { + idInfo.setValue(Objects.nonNull(objectMapper)? + objectMapper.writeValueAsString(map) : + String.valueOf(map)); + } catch (JsonProcessingException e) { + // Ignore this exception. + idInfo.setValue(String.valueOf(map)); + } return Stream.of(idInfo).collect(Collectors.toList()); } - return Collections.emptyList(); })); } + public static Map> getIdInfo(Map idResponseDTO) { + return getIdInfo(idResponseDTO, null); + } + /** * To Get match Password function. * diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/MappingConfig.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/MappingConfig.java index b7281c5acb8..79f3272790b 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/MappingConfig.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/match/MappingConfig.java @@ -249,4 +249,11 @@ public interface MappingConfig { * @return the password */ public List getPassword(); + + /** + * List of value to hold Verified Claims Attributes. + * + * @return the Verified Claims Attributes + */ + public List getVerifiedAttributes(); } diff --git a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/service/KycService.java b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/service/KycService.java index 01dda454c43..2c1e778647d 100644 --- a/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/service/KycService.java +++ b/authentication/authentication-core/src/main/java/io/mosip/authentication/core/spi/indauth/service/KycService.java @@ -78,4 +78,16 @@ String generateAndSaveKycToken(String idHash, String authToken, String oidcClien */ String buildKycExchangeResponse(String subject, Map> idInfo, List consentedAttributes, List locales, String idVid, KycExchangeRequestDTO kycExchangeRequestDTO) throws IdAuthenticationBusinessException; + + + /** + * Method to build kyc auth version 2 verified claims meta data. + * + * @param verifiedClaimsData Verified Claims data of the identity + * @param oidcClientId OIDC Client Id. + * @return String + * @throws IdAuthenticationBusinessException the id authentication business + * exception + */ + String buildVerifiedClaimsMetadata(String verifiedClaimsData, String oidcClientId) throws IdAuthenticationBusinessException; } diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java index 9028c16a55d..bcb4ab6029f 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/controller/KycAuthController.java @@ -121,6 +121,16 @@ private void initKycAuthRequestBinder(WebDataBinder binder) { binder.setValidator(authRequestValidator); } + /** + * + * @param binder the binder + */ + @InitBinder("kycAuthRequestDTOV2") + private void initKycAuthRequestV2Binder(WebDataBinder binder) { + binder.setValidator(authRequestValidator); + } + + /** * * @param binder the binder @@ -383,4 +393,87 @@ public KycExchangeResponseDTO processKycExchange(@Validated @RequestBody KycExch throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS); } } + + /** + * Controller Method to Initiate IdP Auth (kyc-auth). + * + * @param authRequestDTO the kyc auth request DTO + * @param errors the errors + * @return kycAuthResponseDTO the kyc auth response DTO + * @throws IdAuthenticationBusinessException the id authentication business + * exception + * @throws IdAuthenticationAppException the id authentication app exception + * @throws IdAuthenticationDaoException the id authentication dao exception + */ + @PostMapping(path = "/kyc-auth/v2/delegated/{IdP-LK}/{Auth-Partner-ID}/{OIDC-Client-Id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Kyc Auth Request", description = "Kyc Auth Request", tags = { "kyc-auth-controller" }) + @SecurityRequirement(name = "Authorization") + @Parameter(in = ParameterIn.HEADER, name = "signature") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request authenticated successfully", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = IdAuthenticationAppException.class)))), + @ApiResponse(responseCode = "201", description = "Created" ,content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", description = "Unauthorized" ,content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", description = "Forbidden" ,content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", description = "Not Found" ,content = @Content(schema = @Schema(hidden = true)))}) + public KycAuthResponseDTOV2 processKycAuthV2(@Validated @RequestBody KycAuthRequestDTOV2 authRequestDTO, + @ApiIgnore Errors errors, @PathVariable("IdP-LK") String mispLK, @PathVariable("Auth-Partner-ID") String partnerId, + @PathVariable("OIDC-Client-Id") String oidcClientId, HttpServletRequest request) + throws IdAuthenticationBusinessException, IdAuthenticationAppException, IdAuthenticationDaoException { + if(request instanceof ObjectWithMetadata) { + ObjectWithMetadata requestWrapperWithMetadata = (ObjectWithMetadata) request; + + boolean isAuth = true; + Optional partner = partnerService.getPartner(partnerId, authRequestDTO.getMetadata()); + AuthTransactionBuilder authTxnBuilder = authTransactionHelper + .createAndSetAuthTxnBuilderMetadataToRequest(authRequestDTO, !isAuth, partner); + try { + String idType = Objects.nonNull(authRequestDTO.getIndividualIdType()) ? authRequestDTO.getIndividualIdType() + : idTypeUtil.getIdType(authRequestDTO.getIndividualId()).getType(); + authRequestDTO.setIndividualIdType(idType); + authRequestValidator.validateIdvId(authRequestDTO.getIndividualId(), idType, errors); + if(AuthTypeUtil.isBio(authRequestDTO)) { + kycReqValidator.validateDeviceDetails(authRequestDTO, errors); + } + DataValidationUtil.validate(errors); + boolean externalAuthRequest = true; + AuthResponseDTO authResponseDTO = kycFacade.authenticateIndividual(authRequestDTO, externalAuthRequest, partnerId, + oidcClientId, requestWrapperWithMetadata, IdAuthCommonConstants.KYC_AUTH_CONSUME_VID_DEFAULT); + KycAuthResponseDTOV2 kycAuthResponseDtoV2 = new KycAuthResponseDTOV2(); + Map metadata = requestWrapperWithMetadata.getMetadata(); + if (authResponseDTO != null && + metadata != null && + metadata.get(IdAuthCommonConstants.IDENTITY_DATA) != null && + metadata.get(IdAuthCommonConstants.IDENTITY_INFO) != null) { + kycAuthResponseDtoV2 = kycFacade.processKycAuthV2(authRequestDTO, authResponseDTO, partnerId, oidcClientId, metadata); + } + return kycAuthResponseDtoV2; + } catch (IDDataValidationException e) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "processKycAuthV2", + e.getErrorTexts().isEmpty() ? "" : e.getErrorText()); + + auditHelper.auditExceptionForAuthRequestedModules(AuditEvents.KYC_REQUEST_RESPONSE_V2, authRequestDTO, e); + IdaRequestResponsConsumerUtil.setIdVersionToObjectWithMetadata(requestWrapperWithMetadata, e); + e.putMetadata(IdAuthCommonConstants.TRANSACTION_ID, authRequestDTO.getTransactionID()); + throw authTransactionHelper.createDataValidationException(authTxnBuilder, e, requestWrapperWithMetadata); + } catch (IdAuthenticationBusinessException e) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "processKycAuthV2", + e.getErrorTexts().isEmpty() ? "" : e.getErrorText()); + + if (isEventingEnabled) { + if (IdAuthenticationErrorConstants.ID_NOT_AVAILABLE.getErrorCode().equals(e.getErrorCode())) { + authenticationErrorEventingPublisher.notify(authRequestDTO, request.getHeader("signature"), + partner, e, authRequestDTO.getMetadata()); + } + } + auditHelper.auditExceptionForAuthRequestedModules(AuditEvents.KYC_REQUEST_RESPONSE_V2, authRequestDTO, e); + IdaRequestResponsConsumerUtil.setIdVersionToObjectWithMetadata(requestWrapperWithMetadata, e); + e.putMetadata(IdAuthCommonConstants.TRANSACTION_ID, authRequestDTO.getTransactionID()); + throw authTransactionHelper.createUnableToProcessException(authTxnBuilder, e, requestWrapperWithMetadata); + } + } else { + mosipLogger.error("Technical error. HttpServletRequest is not instanceof ObjectWithMetada."); + throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.UNABLE_TO_PROCESS); + } + } } \ No newline at end of file diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java index c8c5c8db3c1..f22d4a0bef3 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/facade/KycFacadeImpl.java @@ -3,7 +3,9 @@ */ package io.mosip.authentication.service.kyc.facade; -import java.time.LocalDateTime; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.EMPTY; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.VERIFIED_CLAIMS_ATTRIBS; + import java.util.AbstractMap.SimpleEntry; import java.util.Collection; import java.util.HashSet; @@ -20,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,13 +30,11 @@ import io.mosip.authentication.common.service.builder.AuthTransactionBuilder; import io.mosip.authentication.common.service.entity.AutnTxn; import io.mosip.authentication.common.service.entity.KycTokenData; -import io.mosip.authentication.common.service.entity.OIDCClientData; import io.mosip.authentication.common.service.helper.AuditHelper; import io.mosip.authentication.common.service.helper.TokenValidationHelper; import io.mosip.authentication.common.service.integration.TokenIdManager; import io.mosip.authentication.common.service.repository.IdaUinHashSaltRepo; import io.mosip.authentication.common.service.repository.KycTokenDataRepository; -import io.mosip.authentication.common.service.repository.OIDCClientDataRepository; import io.mosip.authentication.common.service.transaction.manager.IdAuthSecurityManager; import io.mosip.authentication.common.service.util.EnvUtil; import io.mosip.authentication.common.service.util.IdaRequestResponsConsumerUtil; @@ -57,7 +58,9 @@ import io.mosip.authentication.core.indauth.dto.IdType; import io.mosip.authentication.core.indauth.dto.IdentityInfoDTO; import io.mosip.authentication.core.indauth.dto.KycAuthRespDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthRespDTOV2; import io.mosip.authentication.core.indauth.dto.KycAuthResponseDTO; +import io.mosip.authentication.core.indauth.dto.KycAuthResponseDTOV2; import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTO; import io.mosip.authentication.core.indauth.dto.KycExchangeResponseDTO; import io.mosip.authentication.core.indauth.dto.ResponseDTO; @@ -317,16 +320,14 @@ public KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestD String partnerId, String oidcClientId, Map metadata) throws IdAuthenticationBusinessException { boolean status; String token = null; - String idHash = null; KycAuthResponseDTO kycAuthResponseDTO = null; try { Map idResDTO = (Map) metadata.get(IdAuthCommonConstants.IDENTITY_DATA); token = idService.getToken(idResDTO); - idHash = idService.getIdHash(idResDTO); - Entry kycAuthResponse = doProcessKycAuth(kycAuthRequestDTO, authResponseDTO, partnerId, - oidcClientId, idHash); - kycAuthResponseDTO = kycAuthResponse.getKey(); + Entry kycAuthResponse = doProcessKycAuth(kycAuthRequestDTO, authResponseDTO, partnerId, + oidcClientId, idResDTO, false); + kycAuthResponseDTO = (KycAuthResponseDTO) kycAuthResponse.getKey(); status = kycAuthResponse.getValue(); saveToTxnTable(kycAuthRequestDTO, status, partnerId, token, authResponseDTO, kycAuthResponseDTO, metadata, true); auditHelper.audit(AuditModules.KYC_AUTH, AuditEvents.KYC_REQUEST_RESPONSE, @@ -342,14 +343,14 @@ public KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestD } } - private Entry doProcessKycAuth(AuthRequestDTO kycAuthRequestDTO, AuthResponseDTO authResponseDTO, - String partnerId, String oidcClientId, String idHash) throws IdAuthenticationBusinessException, IDDataValidationException { + private Entry doProcessKycAuth(AuthRequestDTO kycAuthRequestDTO, AuthResponseDTO authResponseDTO, + String partnerId, String oidcClientId, Map idResDTO, boolean v2Resp) throws IdAuthenticationBusinessException, IDDataValidationException { - KycAuthResponseDTO kycAuthResponseDTO = new KycAuthResponseDTO(); + BaseAuthResponseDTO baseAuthResponseDTO = !v2Resp ? new KycAuthResponseDTO() : new KycAuthResponseDTOV2(); if (kycAuthRequestDTO != null) { - KycAuthRespDTO response = new KycAuthRespDTO(); + String idHash = idService.getIdHash(idResDTO); ResponseDTO authResponse = authResponseDTO.getResponse(); String responseTime = authResponseDTO.getResponseTime(); if(Objects.isNull(responseTime)) { @@ -360,28 +361,41 @@ private Entry doProcessKycAuth(AuthRequestDTO kycAu String kycToken = null; if (Objects.nonNull(authResponse) && authResponse.isAuthStatus()) { kycToken = kycService.generateAndSaveKycToken(idHash, authResponse.getAuthToken(), oidcClientId, requestTime, responseTime, kycAuthRequestDTO.getTransactionID()); - response.setKycToken(kycToken); } if (Objects.nonNull(authResponse) && Objects.nonNull(authResponseDTO)) { - response.setKycStatus(authResponse.isAuthStatus()); - response.setAuthToken(authResponse.getAuthToken()); - kycAuthResponseDTO.setResponse(response); - kycAuthResponseDTO.setId(authResponseDTO.getId()); - kycAuthResponseDTO.setTransactionID(authResponseDTO.getTransactionID()); - kycAuthResponseDTO.setVersion(authResponseDTO.getVersion()); - kycAuthResponseDTO.setErrors(authResponseDTO.getErrors()); - kycAuthResponseDTO.setResponseTime(responseTime); + baseAuthResponseDTO.setId(authResponseDTO.getId()); + baseAuthResponseDTO.setTransactionID(authResponseDTO.getTransactionID()); + baseAuthResponseDTO.setVersion(authResponseDTO.getVersion()); + baseAuthResponseDTO.setErrors(authResponseDTO.getErrors()); + baseAuthResponseDTO.setResponseTime(responseTime); + if (!v2Resp){ + KycAuthRespDTO response = new KycAuthRespDTO(); + response.setKycToken(kycToken); + response.setKycStatus(authResponse.isAuthStatus()); + response.setAuthToken(authResponse.getAuthToken()); + ((KycAuthResponseDTO)baseAuthResponseDTO).setResponse(response); + } else { + KycAuthRespDTOV2 response = new KycAuthRespDTOV2(); + Map> idInfo = IdInfoFetcher.getIdInfo(idResDTO, mapper); + List idInfoList = idInfo.get(VERIFIED_CLAIMS_ATTRIBS); + response.setKycToken(kycToken); + response.setKycStatus(authResponse.isAuthStatus()); + response.setAuthToken(authResponse.getAuthToken()); + String verifiedClaimsMetadata = !CollectionUtils.isEmpty(idInfoList) ? idInfoList.get(0).getValue() : EMPTY; + response.setVerifiedClaimsMetadata(kycService.buildVerifiedClaimsMetadata(verifiedClaimsMetadata, oidcClientId)); + ((KycAuthResponseDTOV2)baseAuthResponseDTO).setResponse(response); + } } - return new SimpleEntry<>(kycAuthResponseDTO, response.isKycStatus()); + return new SimpleEntry<>(baseAuthResponseDTO, authResponse.isAuthStatus()); } - return new SimpleEntry<>(kycAuthResponseDTO, false); + return new SimpleEntry<>(baseAuthResponseDTO, false); } @Override public KycExchangeResponseDTO processKycExchange(KycExchangeRequestDTO kycExchangeRequestDTO, String partnerId, String oidcClientId, Map metadata, ObjectWithMetadata requestWithMetadata) throws IdAuthenticationBusinessException { - String idHash = null; + try { mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "processKycExchange", "Processing Kyc Exchange request."); @@ -462,7 +476,6 @@ public KycExchangeResponseDTO processKycExchange(KycExchangeRequestDTO kycExchan } } - // Need to move below duplicate code to common to be used by OTPService and KycExchange. private void saveToTxnTable(KycExchangeRequestDTO kycExchangeRequestDTO, boolean isInternal, boolean status, String partnerId, String token, KycExchangeResponseDTO kycExchangeResponseDTO, ObjectWithMetadata requestWithMetadata) @@ -496,5 +509,32 @@ private void saveTxn(KycExchangeRequestDTO kycExchangeRequestDTO, String token, } } + @SuppressWarnings("unchecked") + @Override + public KycAuthResponseDTOV2 processKycAuthV2(@Nonnull AuthRequestDTO kycAuthRequestDTO, AuthResponseDTO authResponseDTO, + String partnerId, String oidcClientId, Map metadata) throws IdAuthenticationBusinessException { + boolean status; + String token = null; + KycAuthResponseDTOV2 kycAuthResponseDTOV2 = null; + try { + Map idResDTO = (Map) metadata.get(IdAuthCommonConstants.IDENTITY_DATA); + token = idService.getToken(idResDTO); + Entry kycAuthResponse = doProcessKycAuth(kycAuthRequestDTO, authResponseDTO, partnerId, + oidcClientId, idResDTO, true); + kycAuthResponseDTOV2 = (KycAuthResponseDTOV2) kycAuthResponse.getKey(); + status = kycAuthResponse.getValue(); + saveToTxnTable(kycAuthRequestDTO, status, partnerId, token, authResponseDTO, kycAuthResponseDTOV2, metadata, true); + auditHelper.audit(AuditModules.KYC_AUTH, AuditEvents.KYC_REQUEST_RESPONSE, + kycAuthRequestDTO.getTransactionID(), IdType.getIDTypeOrDefault(kycAuthRequestDTO.getIndividualIdType()), + "kycAuthentication status : " + status); + return kycAuthResponseDTOV2; + } catch (IdAuthenticationBusinessException e) { + status = false; + saveToTxnTable(kycAuthRequestDTO, status, partnerId, token, authResponseDTO, kycAuthResponseDTOV2, metadata, true); + auditHelper.audit(AuditModules.KYC_AUTH, AuditEvents.KYC_REQUEST_RESPONSE, + kycAuthRequestDTO.getTransactionID(), IdType.getIDTypeOrDefault(kycAuthRequestDTO.getIndividualIdType()), e); + throw e; + } + } } diff --git a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/KycServiceImpl.java b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/KycServiceImpl.java index a2bf7d0196f..728b89bef7d 100644 --- a/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/KycServiceImpl.java +++ b/authentication/authentication-service/src/main/java/io/mosip/authentication/service/kyc/impl/KycServiceImpl.java @@ -1,12 +1,18 @@ package io.mosip.authentication.service.kyc.impl; + +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.COMMA_STRING; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.EMPTY; import static io.mosip.authentication.core.constant.IdAuthCommonConstants.LANG_CODE_SEPARATOR; +import static io.mosip.authentication.core.constant.IdAuthCommonConstants.NULL_CONST; import java.nio.ByteBuffer; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.time.temporal.ValueRange; +import java.util.AbstractMap; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -18,21 +24,29 @@ import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.Spliterators; +import java.util.stream.StreamSupport; import org.apache.commons.codec.DecoderException; +import org.json.JSONArray; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.gson.JsonArray; import io.mosip.authentication.common.service.entity.KycTokenData; +import io.mosip.authentication.common.service.entity.OIDCClientData; import io.mosip.authentication.common.service.helper.IdInfoHelper; import io.mosip.authentication.common.service.impl.match.BioMatchType; import io.mosip.authentication.common.service.impl.match.DemoMatchType; import io.mosip.authentication.common.service.impl.match.IdaIdMapping; import io.mosip.authentication.common.service.repository.KycTokenDataRepository; +import io.mosip.authentication.common.service.repository.OIDCClientDataRepository; import io.mosip.authentication.common.service.transaction.manager.IdAuthSecurityManager; import io.mosip.authentication.common.service.util.EnvUtil; import io.mosip.authentication.common.service.util.IdaRequestResponsConsumerUtil; @@ -53,7 +67,6 @@ import io.mosip.biometrics.util.face.FaceDecoder; import io.mosip.kernel.biometrics.entities.BIR; import io.mosip.kernel.biometrics.spi.CbeffUtil; -import io.mosip.kernel.core.cbeffutil.jaxbclasses.BIRType; import io.mosip.kernel.core.logger.spi.Logger; import io.mosip.kernel.core.util.DateUtils; @@ -100,6 +113,9 @@ public class KycServiceImpl implements KycService { @Value("${ida.idp.jwe.response.type.constant:JWE}") private String jweResponseType; + @Value("${ida.oidc4ida.ignore.standard.claims.list}") + private String[] ignoreClaims; + /** The env. */ @Autowired EnvUtil env; @@ -124,6 +140,10 @@ public class KycServiceImpl implements KycService { @Autowired private CbeffUtil cbeffUtil; + + @Autowired + private OIDCClientDataRepository oidcClientDataRepo; + /** * Retrieve kyc info. * @@ -133,13 +153,6 @@ public class KycServiceImpl implements KycService { * @return the kyc response DTO * @throws IdAuthenticationBusinessException the id authentication business exception */ - /* - * (non-Javadoc) - * - * @see - * io.mosip.authentication.core.spi.indauth.service.KycService#retrieveKycInfo( - * java.lang.String, java.util.List, java.lang.String, java.util.Map) - */ @Override public EKycResponseDTO retrieveKycInfo(List allowedkycAttributes, Set langCodes, Map> identityInfo) throws IdAuthenticationBusinessException { @@ -786,4 +799,57 @@ private String getFaceBDB(String faceCbeff) throws Exception { } return CryptoUtil.encodeBase64(birDataFromXMLType.get(0).getBdb()); } + + public String buildVerifiedClaimsMetadata(String verifiedClaimsData, String oidcClientId) + throws IdAuthenticationBusinessException { + Optional oidcClientData = oidcClientDataRepo.findByClientId(oidcClientId); + if(oidcClientData.isEmpty()) { + return EMPTY; + } + List oidcClientAllowedVerifiedClaims = Stream.of(oidcClientData.get().getUserClaims()) + .filter(t -> !Arrays.asList(ignoreClaims).contains(t)) + .collect(Collectors.toList()); + + if (verifiedClaimsData.equals(EMPTY)) { + mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + "buildVerifiedClaimsMetadata", "No Verified Claims data found for the id."); + return oidcClientAllowedVerifiedClaims.stream() + .map(md -> md.concat(NULL_CONST)) + .collect(Collectors.joining(COMMA_STRING)); + } + + JSONObject verifiedClaimJson = new JSONObject(verifiedClaimsData); + Set verifiedClaimKeys = StreamSupport.stream(Spliterators.spliteratorUnknownSize( + verifiedClaimJson.keys(), 0), + false).collect(Collectors.toSet()); + + Map idAttribsMap = oidcClientAllowedVerifiedClaims.stream().flatMap(claim -> { + try { + return idInfoHelper.getIdentityAttributesForIdName(claim) + .stream() + .map(attrib -> new AbstractMap.SimpleEntry<>(attrib, claim)); + } catch (IdAuthenticationBusinessException exp) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), + "buildVerifiedClaimsMetadata", "Error Fatching the attibutes. " + exp.getMessage(), exp); + } + return Stream.empty(); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + String verifiedClaimsStr = null; + Map verifiedClaimsMap = idAttribsMap.entrySet().stream().filter(e -> verifiedClaimKeys.contains(e.getKey())) + .map(entry -> new Object[] {(Object)entry.getValue(), + ((JSONArray)verifiedClaimJson.get(entry.getKey())).toList()}) + .collect(Collectors.toMap(strArr -> strArr[0].toString(), strArr -> strArr[1])); + + oidcClientAllowedVerifiedClaims.stream().filter(claim -> !verifiedClaimsMap.keySet().contains(claim)) + .forEach(claim -> verifiedClaimsMap.put(claim, "null")); + + try { + verifiedClaimsStr = mapper.writeValueAsString(verifiedClaimsMap); + } catch (JsonProcessingException exp) { + mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "buildVerifiedClaimsMetadata", + "Error converting map to string. " + exp.getMessage(), exp); + } + return verifiedClaimsStr; + } }