Skip to content

Commit

Permalink
Merge pull request #1074 from mahammedtaheer/develop
Browse files Browse the repository at this point in the history
[ES-186] Added new Vci Exchange API to add support for VCI.
  • Loading branch information
mahammedtaheer authored Sep 1, 2023
2 parents 94794fb + 611400a commit b3b29fb
Show file tree
Hide file tree
Showing 40 changed files with 1,942 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.mosip.authentication.common.service.entity;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Data
@Table(name = "cred_subject_id_store", schema = "ida")
@Entity
public class CredSubjectIdStore {

@Id
@NotNull
@Column(name = "id")
private String id;

@NotNull
@Column(name = "id_vid_hash")
private String idVidHash;

@NotNull
@Column(name = "token_id")
private String tokenId;

@NotNull
@Column(name = "cred_subject_id")
private String credSubjectId;

@NotNull
@Column(name = "csid_key_hash")
private String csidKeyHash;

@NotNull
@Column(name = "oidc_client_id")
private String oidcClientId;

@NotNull
@Column(name = "csid_status")
private String csidStatus;

@NotNull
@Column(name = "cr_by")
private String createdBy;

@NotNull
@Column(name = "cr_dtimes")
private LocalDateTime crDTimes;

@Column(name = "upd_by")
private String updatedBy;

@Column(name = "upd_dtimes")
private LocalDateTime updDTimes;

@Column(name = "is_deleted")
private boolean isDeleted;

@Column(name = "del_dtimes")
private LocalDateTime delDTimes;
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public JSONObject getPolicy() {
return OBJECT_MAPPER.readValue(CryptoUtil.decodeBase64Url(new String(this.policy)), JSONObject.class);
} catch (IOException e) {
// This block will never be executed
e.printStackTrace();
//e.printStackTrace();
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.mosip.authentication.core.indauth.dto.IdType;
import io.mosip.authentication.core.indauth.dto.IdentityKeyBindingRequestDTO;
import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTO;
import io.mosip.authentication.core.indauth.dto.VciExchangeRequestDTO;
import io.mosip.authentication.core.logger.IdaLogger;
import io.mosip.authentication.core.otp.dto.OtpRequestDTO;
import io.mosip.authentication.core.partner.dto.PartnerDTO;
Expand Down Expand Up @@ -268,7 +269,12 @@ private AuthTransactionBuilder createAuthTxnBuilder(ObjectWithMetadata requestDT
IdentityKeyBindingRequestDTO keyBindingRequestDTO = (IdentityKeyBindingRequestDTO) requestDTO;
authTransactionBuilder.withRequest(keyBindingRequestDTO);
authTransactionBuilder.addRequestType(RequestType.IDENTITY_KEY_BINDING);
}
} else if(requestDTO instanceof VciExchangeRequestDTO) {
VciExchangeRequestDTO vciExchangeRequestDTO = (VciExchangeRequestDTO) requestDTO;
authTransactionBuilder.withRequest(vciExchangeRequestDTO);
authTransactionBuilder.addRequestType(RequestType.VCI_EXCHANGE_REQUEST);
}


return authTransactionBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package io.mosip.authentication.common.service.helper;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import io.mosip.authentication.common.service.entity.KycTokenData;
import io.mosip.authentication.common.service.entity.OIDCClientData;
import io.mosip.authentication.common.service.repository.KycTokenDataRepository;
import io.mosip.authentication.common.service.repository.OIDCClientDataRepository;
import io.mosip.authentication.common.service.util.EnvUtil;
import io.mosip.authentication.common.service.util.IdaRequestResponsConsumerUtil;
import io.mosip.authentication.core.constant.IdAuthCommonConstants;
import io.mosip.authentication.core.constant.IdAuthenticationErrorConstants;
import io.mosip.authentication.core.constant.KycTokenStatusType;
import io.mosip.authentication.core.exception.IdAuthenticationBusinessException;
import io.mosip.authentication.core.indauth.dto.AuthRequestDTO;
import io.mosip.authentication.core.indauth.dto.BaseRequestDTO;
import io.mosip.authentication.core.indauth.dto.KycExchangeRequestDTO;
import io.mosip.authentication.core.logger.IdaLogger;
import io.mosip.authentication.core.spi.indauth.service.KycService;
import io.mosip.kernel.core.logger.spi.Logger;

/**
* Helper class to Validate Token returned in kyc-auth.
*
* @author Mahammed Taheer
*/

public class TokenValidationHelper {

/** The mosip logger. */
private static Logger mosipLogger = IdaLogger.getLogger(TokenValidationHelper.class);

@Value("${ida.idp.consented.individual_id.attribute.name:individual_id}")
private String consentedIndividualIdAttributeName;


/** The Kyc Service */
@Autowired
private KycService kycService;

@Autowired
private KycTokenDataRepository kycTokenDataRepo;

@Autowired
private IdInfoHelper idInfoHelper;

@Autowired
private OIDCClientDataRepository oidcClientDataRepo;


public KycTokenData findAndValidateIssuedToken(String tokenData, String oidcClientId, String reqTransactionId,
String idvidHash) throws IdAuthenticationBusinessException {

mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "processVciExchange",
"Check Token Exists or not, associated with oidc client and active status.");

Optional<KycTokenData> tokenDataOpt = kycTokenDataRepo.findByKycToken(tokenData);
if (!tokenDataOpt.isPresent()) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "findAndValidateIssuedToken",
"KYC Token not found: " + tokenData);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_NOT_FOUND.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_NOT_FOUND.getErrorMessage());
}
KycTokenData tokenDataObj = tokenDataOpt.get();
validateToken(tokenDataObj, oidcClientId, reqTransactionId, idvidHash);
return tokenDataObj;
}

private void validateToken(KycTokenData kycTokenData, String oidcClientId, String reqTransactionId, String idvidHash)
throws IdAuthenticationBusinessException {
String kycToken = kycTokenData.getKycToken();
if (kycTokenData.getKycTokenStatus().equals(KycTokenStatusType.PROCESSED.getStatus())) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token already processed: " + kycToken);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_ALREADY_PROCESSED.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_ALREADY_PROCESSED.getErrorMessage());
}

if (kycTokenData.getKycTokenStatus().equals(KycTokenStatusType.EXPIRED.getStatus())) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token expired: " + kycToken);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_EXPIRED.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_EXPIRED.getErrorMessage());
}

if (!kycTokenData.getOidcClientId().equals(oidcClientId)) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token does not belongs to the provided OIDC Client Id: " + kycToken);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_OIDC_CLIENT_ID.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_OIDC_CLIENT_ID.getErrorMessage());
}

if (!kycTokenData.getIdVidHash().equals(idvidHash)) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token does not belongs to the provided UIN/VID: " + kycToken);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_UIN_VID.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_UIN_VID.getErrorMessage());
}

if (!kycTokenData.getRequestTransactionId().equals(reqTransactionId)) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Auth & KYC Exchange Transaction Ids are not same: " + kycToken);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_TRANSACTION_ID.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_INVALID_TRANSACTION_ID.getErrorMessage());
}

mosipLogger.info(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token found, Check Token expire.");
LocalDateTime tokenIssuedDateTime = kycTokenData.getTokenIssuedDateTime();
boolean isExpired = kycService.isKycTokenExpire(tokenIssuedDateTime, kycToken);

if (isExpired) {
mosipLogger.error(IdAuthCommonConstants.SESSION_ID, this.getClass().getSimpleName(), "validateKycToken",
"KYC Token expired.");
kycTokenData.setKycTokenStatus(KycTokenStatusType.EXPIRED.getStatus());
kycTokenDataRepo.saveAndFlush(kycTokenData);
throw new IdAuthenticationBusinessException(
IdAuthenticationErrorConstants.KYC_TOKEN_EXPIRED.getErrorCode(),
IdAuthenticationErrorConstants.KYC_TOKEN_EXPIRED.getErrorMessage());
}
}

public void mapConsentedAttributesToIdSchemaAttributes(List<String> consentAttributes, Set<String> filterAttributes,
List<String> policyAllowedKycAttribs) throws IdAuthenticationBusinessException {

if(consentAttributes != null && !consentAttributes.isEmpty()) {
for (String attrib : consentAttributes) {
Collection<? extends String> idSchemaAttribute = idInfoHelper.getIdentityAttributesForIdName(attrib);
filterAttributes.addAll(idSchemaAttribute);
}
// removing individual id from consent if the claim is not allowed in policy.
if (!policyAllowedKycAttribs.contains(consentedIndividualIdAttributeName)) {
consentAttributes.remove(consentedIndividualIdAttributeName);
}
}
}

public Set<String> filterByPolicyAllowedAttributes(Set<String> filterAttributes, List<String> policyAllowedKycAttribs) {
return policyAllowedKycAttribs.stream()
.filter(attribute -> filterAttributes.contains(attribute))
.collect(Collectors.toSet());
}

public String getKycExchangeResponseTime(BaseRequestDTO authRequestDTO) {
String dateTimePattern = EnvUtil.getDateTimePattern();
return IdaRequestResponsConsumerUtil.getResponseTime(authRequestDTO.getRequestTime(), dateTimePattern);
}

public List<String> filterAllowedUserClaims(String oidcClientId, List<String> consentAttributes) {
mosipLogger.info(IdAuthCommonConstants.IDA, this.getClass().getSimpleName(), "filterAllowedUserClaims",
"Checking for OIDC client allowed userclaims");
Optional<OIDCClientData> oidcClientData = oidcClientDataRepo.findByClientId(oidcClientId);

List<String> oidcClientAllowedUserClaims = List.of(oidcClientData.get().getUserClaims())
.stream()
.map(String::toLowerCase)
.collect(Collectors.toList());
if (consentAttributes.isEmpty()) {
return oidcClientAllowedUserClaims;
}

return consentAttributes.stream()
.filter(claim -> oidcClientAllowedUserClaims.contains(claim.toLowerCase()))
.collect(Collectors.toList());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private AuthMethodsRefValues createAuthMethodsRefValuesObject() throws IdAuthent
logger.error(IdAuthCommonConstants.IDA, this.getClass().getSimpleName(), "createAuthMethodsRefValuesObject",
"Not able to download the AMR-ACR Json config file. URI: " + amracrMappingUri, e);
throw new IdAuthenticationBusinessException(IdAuthenticationErrorConstants.DOWNLOAD_ERROR.getErrorCode(),
IdAuthenticationErrorConstants.DOWNLOAD_ERROR.getErrorMessage());
IdAuthenticationErrorConstants.DOWNLOAD_ERROR.getErrorMessage());
}

/* ClientResponse clientResponse = webClient.get().uri(amracrMappingUri).accept(MediaType.APPLICATION_JSON).exchange().block();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.mosip.authentication.common.service.repository;

import java.util.List;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import io.mosip.authentication.common.service.entity.CredSubjectIdStore;

/**
* The Interface CredSubjectIdStoreRepository.
*
* @author Mahammed Taheer
*/

@Repository
public interface CredSubjectIdStoreRepository extends JpaRepository<CredSubjectIdStore, String> {

List<CredSubjectIdStore> findAllByCsidKeyHash(String keyHash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import io.mosip.kernel.keymanagerservice.service.KeymanagerService;
import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil;
import io.mosip.kernel.signature.constant.SignatureConstant;
import io.mosip.kernel.signature.dto.JWSSignatureRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureVerifyRequestDto;
import io.mosip.kernel.signature.dto.JWTSignatureVerifyResponseDto;
Expand Down Expand Up @@ -147,6 +148,10 @@ public class IdAuthSecurityManager {
@Value("${mosip.kernel.certificate.sign.algorithm:SHA256withRSA}")
private String signAlgorithm;

/** The sign applicationid. */
@Value("${mosip.ida.vci.exchange.sign.applicationid:IDA_VCI_EXCHANGE}")
private String vciExchSignApplicationId;

/** The uin hash salt repo. */
@Autowired
private IdaUinHashSaltRepo uinHashSaltRepo;
Expand Down Expand Up @@ -657,6 +662,20 @@ public String signWithPayload(String data) {
return signatureService.jwtSign(request).getJwtSignedData();
}

@WithRetry
public String jwsSignWithPayload(String data) {
JWSSignatureRequestDto request = new JWSSignatureRequestDto();
request.setApplicationId(vciExchSignApplicationId);
request.setDataToSign(CryptoUtil.encodeBase64Url(data.getBytes()));
request.setIncludeCertHash(false);
request.setIncludeCertificate(includeCertificate);
request.setIncludePayload(false);
request.setReferenceId(IdAuthCommonConstants.EMPTY);
request.setB64JWSHeaderParam(false);
request.setValidateJson(false);
return signatureService.jwsSign(request).getJwtSignedData();
}

@WithRetry
public Entry<String, String> generateKeyBindingCertificate(PublicKey publicKey, CertificateParameters certParams)
throws CertificateEncodingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public enum AuditEvents {
KYC_EXCHANGE_REQUEST_RESPONSE("IDA_015", "System", "Kyc Exchange Request"),

KEY_BINDIN_REQUEST_RESPONSE("IDA_016", "System", "Identity Key Binding Request"),

VCI_EXCHANGE_REQUEST_RESPONSE("IDA_017", "System", "Vci Exchange 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_EXCHANGE("IDA-KEX", "KYC Exchange Request", "KYC Exchange"),

VCI_EXCHANGE("IDA-VCI", "VCI Exchange Request", "VCI Exchange"),

IDENTITY_KEY_BINDING("IDA-IKB", "Identity Key Binding Request", "Key Binding"),

/** The otp request. */
Expand Down
Loading

0 comments on commit b3b29fb

Please sign in to comment.