Skip to content

Commit

Permalink
[ES-1091] added new kyc auth V2 API to return the verified claims.
Browse files Browse the repository at this point in the history
Signed-off-by: Mahammed Taheer <[email protected]>
  • Loading branch information
mahammedtaheer committed Sep 11, 2024
1 parent dc0a4d0 commit 9a9b860
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,6 @@ public class IDAMappingConfig implements MappingConfig {

/** The password. */
private List<String> password;

private List<String> verifiedAttributes;
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, Object> idResDTO = idService.processIdType(idvIdType, idvid, idInfoHelper.isBiometricDataNeeded(authRequestDTO),
markVidConsumed, filterAttributes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ public BiFunction<MappingConfig, MatchType, List<String>> getMappingFunction() {

PASSWORD("password", MappingConfig::getPassword),

VERIFIEDATTRIBUTES("verifiedAttributes", MappingConfig::getVerifiedAttributes),

/** The dynamic demographics ID Mapping. */
DYNAMIC("demographics") {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
@@ -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<KeyBindedTokenDTO> keyBindedTokens;

private String password;

private boolean claimsMetadataRequired;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -82,7 +81,6 @@ AuthResponseDTO authenticateIndividual(AuthRequestDTO authRequest, boolean reque
KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestDTO, AuthResponseDTO authResponseDTO,
String partnerId, String oidcClientId, Map<String, Object> metadata) throws IdAuthenticationBusinessException;


/**
* Process the KycExchangeRequestDTO to integrate with KYCService.
*
Expand All @@ -98,4 +96,16 @@ KycAuthResponseDTO processKycAuth(@Nonnull AuthRequestDTO kycAuthRequestDTO, Aut
KycExchangeResponseDTO processKycExchange(KycExchangeRequestDTO kycExchangeRequestDTO,
String partnerId, String oidcClientId, Map<String, Object> 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<String, Object> metadata) throws IdAuthenticationBusinessException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -199,7 +204,7 @@ public Map<String, Entry<String, List<IdentityInfoDTO>>> getCbeffValues(Map<Stri
* @throws IdAuthenticationBusinessException the id authentication business exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map<String, List<IdentityInfoDTO>> getIdInfo(Map<String, Object> idResponseDTO) {
public static Map<String, List<IdentityInfoDTO>> getIdInfo(Map<String, Object> idResponseDTO, ObjectMapper objectMapper) {
return idResponseDTO.entrySet().stream().flatMap(entry -> {
if (entry.getValue() instanceof Map) {
return ((Map<String, Object>) entry.getValue()).entrySet().stream();
Expand Down Expand Up @@ -244,14 +249,29 @@ public static Map<String, List<IdentityInfoDTO>> getIdInfo(Map<String, Object> 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<String, String> map = (Map<String, String>) 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<String, List<IdentityInfoDTO>> getIdInfo(Map<String, Object> idResponseDTO) {
return getIdInfo(idResponseDTO, null);
}

/**
* To Get match Password function.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,11 @@ public interface MappingConfig {
* @return the password
*/
public List<String> getPassword();

/**
* List of value to hold Verified Claims Attributes.
*
* @return the Verified Claims Attributes
*/
public List<String> getVerifiedAttributes();
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,16 @@ String generateAndSaveKycToken(String idHash, String authToken, String oidcClien
*/
String buildKycExchangeResponse(String subject, Map<String, List<IdentityInfoDTO>> idInfo,
List<String> consentedAttributes, List<String> 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<PartnerDTO> 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<String, Object> 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);
}
}
}
Loading

0 comments on commit 9a9b860

Please sign in to comment.