Skip to content

Commit

Permalink
kyc exchange accepted claims datatype changes (mosip#882)
Browse files Browse the repository at this point in the history
Signed-off-by: ase-101 <sunkadaeanusha@gmail.com>
  • Loading branch information
ase-101 authored Sep 12, 2024
1 parent c0dc7d8 commit 050e628
Show file tree
Hide file tree
Showing 16 changed files with 180 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ public class Constants {
public static final String SERVER_NONCE_SEPARATOR = "~###~";
public static final String RESUMED = "RESUMED";
public static final String RESUME_NOT_APPLICABLE = "RESUME_NOT_APPLICABLE";
public static final String VERIFIED_CLAIMS = "verified_claims";
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package io.mosip.esignet.core.dto;

import com.fasterxml.jackson.databind.JsonNode;
import io.mosip.esignet.api.dto.claim.Claims;
import io.mosip.esignet.api.dto.claim.VerificationDetail;
import io.mosip.esignet.api.util.ConsentAction;
Expand All @@ -26,7 +27,7 @@ public class OIDCTransaction implements Serializable {
String clientId;
String relyingPartyId;
String redirectUri;
Claims requestedClaims;
Claims resolvedClaims;
List<String> essentialClaims;
List<String> voluntaryClaims;
List<String> requestedAuthorizeScopes;
Expand Down Expand Up @@ -68,4 +69,5 @@ public class OIDCTransaction implements Serializable {

boolean isInternalAuthSuccess;
Map<String, List<VerificationDetail>> claimMetadata;
Map<String, JsonNode> requestedClaimDetails;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static AuditDTO buildAuditDto(String transactionId, String idType, OIDCTr
if(transaction != null) {
auditDTO.setRelyingPartyId(transaction.getRelyingPartyId());
auditDTO.setClientId(transaction.getClientId());
auditDTO.setRequestedClaims(transaction.getRequestedClaims());
auditDTO.setRequestedClaims(transaction.getResolvedClaims());
auditDTO.setRequestedAuthorizeScopes(transaction.getRequestedAuthorizeScopes());
auditDTO.setRedirectUri(transaction.getRedirectUri());
auditDTO.setClaimsLocales(transaction.getClaimsLocales());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package io.mosip.esignet.api.dto;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;

import java.util.List;
Expand All @@ -13,5 +14,5 @@
@Data
public class VerifiedKycExchangeDto extends KycExchangeDto {

private Map<String, List<Map<String, Object>>> acceptedVerifiedClaims;
private Map<String, JsonNode> acceptedClaimDetails;
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ protected SendOtpResult delegateSendOtpRequest(OtpRequest otpRequest, OIDCTransa

protected Set<List<AuthenticationFactor>> getProvidedAuthFactors(OIDCTransaction transaction, List<AuthChallenge> challengeList) throws EsignetException {
List<List<AuthenticationFactor>> resolvedAuthFactors = authenticationContextClassRefUtil.getAuthFactors(
(String[]) transaction.getRequestedClaims().getId_token().get(ACR).get("values"));
(String[]) transaction.getResolvedClaims().getId_token().get(ACR).get("values"));
List<String> providedAuthFactors = challengeList.stream()
.map(AuthChallenge::getAuthFactorType)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,12 @@ public ClaimDetailResponse getClaimDetails(String transactionId) {
List<ClaimStatus> list = new ArrayList<>();

log.debug("Get claims status based on stored claim metadata : {}", transaction.getClaimMetadata());
for(Map.Entry<String, List<Map<String, Object>>> entry : transaction.getRequestedClaims().getUserinfo().entrySet()) {
for(Map.Entry<String, List<Map<String, Object>>> entry : transaction.getResolvedClaims().getUserinfo().entrySet()) {
list.add(claimsHelperService.getClaimStatus(entry.getKey(), entry.getValue(), transaction.getClaimMetadata()));
}

//Profile update is mandated only if any essential verified claim is requested
boolean isEssentialVerifiedClaimRequested = transaction.getRequestedClaims().getUserinfo()
boolean isEssentialVerifiedClaimRequested = transaction.getResolvedClaims().getUserinfo()
.entrySet()
.stream()
.anyMatch( entry -> entry.getValue().stream()
Expand Down Expand Up @@ -399,7 +399,7 @@ private Pair<OAuthDetailResponse, OIDCTransaction> checkAndBuildOIDCTransaction(
IdentityProviderUtil.validateRedirectURI(clientDetailDto.getRedirectUris(), oauthDetailReqDto.getRedirectUri());

//Resolve the final set of claims based on registered and request parameter.
Claims resolvedClaims = claimsHelperService.getRequestedClaims(oauthDetailReqDto, clientDetailDto);
Claims resolvedClaims = claimsHelperService.resolveRequestedClaims(oauthDetailReqDto, clientDetailDto);
//Resolve and set ACR claim
resolvedClaims.getId_token().put(ACR, resolveACRClaim(clientDetailDto.getAcrValues(),
oauthDetailReqDto.getAcrValues(),
Expand Down Expand Up @@ -427,7 +427,7 @@ private Pair<OAuthDetailResponse, OIDCTransaction> checkAndBuildOIDCTransaction(
oidcTransaction.setRedirectUri(oauthDetailReqDto.getRedirectUri());
oidcTransaction.setRelyingPartyId(clientDetailDto.getRpId());
oidcTransaction.setClientId(clientDetailDto.getId());
oidcTransaction.setRequestedClaims(resolvedClaims);
oidcTransaction.setResolvedClaims(resolvedClaims);
oidcTransaction.setRequestedAuthorizeScopes(oAuthDetailResponse.getAuthorizeScopes());
oidcTransaction.setNonce(oauthDetailReqDto.getNonce());
oidcTransaction.setState(oauthDetailReqDto.getState());
Expand All @@ -438,6 +438,7 @@ private Pair<OAuthDetailResponse, OIDCTransaction> checkAndBuildOIDCTransaction(
oidcTransaction.setServerNonce(UUID.randomUUID().toString());
oidcTransaction.setRequestedCredentialScopes(authorizationHelperService.getCredentialScopes(oauthDetailReqDto.getScope()));
oidcTransaction.setInternalAuthSuccess(false);
oidcTransaction.setRequestedClaimDetails(oauthDetailReqDto.getClaims()!=null? oauthDetailReqDto.getClaims().getUserinfo() : null);
return Pair.of(oAuthDetailResponse, oidcTransaction);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
@Component
public class ClaimsHelperService {

private static final String VERIFIED_CLAIMS = "verified_claims";

@Value("#{${mosip.esignet.openid.scope.claims}}")
private Map<String, List<String>> claims;
Expand All @@ -61,7 +60,7 @@ protected Map<String, List<String>> getClaimNames(Claims resolvedClaims) {
}


protected Claims getRequestedClaims(OAuthDetailRequest oauthDetailRequest, ClientDetail clientDetailDto)
protected Claims resolveRequestedClaims(OAuthDetailRequest oauthDetailRequest, ClientDetail clientDetailDto)
throws EsignetException {
Claims resolvedClaims = new Claims();
resolvedClaims.setUserinfo(new HashMap<>());
Expand Down Expand Up @@ -119,8 +118,8 @@ else if(claimBasedOnScope.contains(claimName))
}

protected boolean isVerifiedClaimRequested(OIDCTransaction transaction) {
return transaction.getRequestedClaims().getUserinfo() != null &&
transaction.getRequestedClaims().getUserinfo()
return transaction.getResolvedClaims().getUserinfo() != null &&
transaction.getResolvedClaims().getUserinfo()
.entrySet()
.stream()
.anyMatch( entry -> entry.getValue().stream().anyMatch( m-> m.get("verification") != null));
Expand All @@ -142,7 +141,7 @@ protected boolean isVerifiedClaimRequested(OIDCTransaction transaction) {
*
*/
protected void validateAcceptedClaims(OIDCTransaction transaction, List<String> acceptedClaims) throws EsignetException {
Map<String, List<Map<String,Object>>> userinfo = Optional.ofNullable(transaction.getRequestedClaims())
Map<String, List<Map<String,Object>>> userinfo = Optional.ofNullable(transaction.getResolvedClaims())
.map(Claims::getUserinfo)
.orElse(Collections.emptyMap());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void updateUserConsent(OIDCTransaction transaction, String signature) {
UserConsent userConsent = new UserConsent();
userConsent.setClientId(transaction.getClientId());
userConsent.setPsuToken(transaction.getPartnerSpecificUserToken());
Claims claims = transaction.getRequestedClaims();
Claims claims = transaction.getResolvedClaims();
List<String> acceptedClaims = transaction.getAcceptedClaims();
Map<String, Object> normalizedClaims = new HashMap<>();
normalizedClaims.put("userinfo", normalizeUserInfoClaims(claims.getUserinfo()));
Expand Down Expand Up @@ -196,8 +196,8 @@ private ConsentAction evaluateConsentAction(OIDCTransaction transaction, Consent
Map<String, Boolean> authorizeScopes = authorizeScope != null ? authorizeScope.stream()
.collect(Collectors.toMap(Function.identity(), s->false)) : Collections.emptyMap();
Map<String, Object> normalizedClaims = new HashMap<>();
normalizedClaims.put("userinfo", normalizeUserInfoClaims(transaction.getRequestedClaims().getUserinfo()));
normalizedClaims.put("id_token", normalizeIdTokenClaims(transaction.getRequestedClaims().getId_token()));
normalizedClaims.put("userinfo", normalizeUserInfoClaims(transaction.getResolvedClaims().getUserinfo()));
normalizedClaims.put("id_token", normalizeIdTokenClaims(transaction.getResolvedClaims().getId_token()));
hash = hashUserConsent(normalizedClaims, authorizeScopes);
} catch (JsonProcessingException e) {
log.error("Failed to hash the user consent", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ private Pair<LinkTransactionResponse, ClientDetail> checkAndPublishLinkedTransac

linkTransactionResponse.setLinkTransactionId(linkedTransactionId);
linkTransactionResponse.setAuthFactors(authenticationContextClassRefUtil.getAuthFactors(
(String[]) transaction.getRequestedClaims().getId_token().get(ACR).get("values")));
(String[]) transaction.getResolvedClaims().getId_token().get(ACR).get("values")));
linkTransactionResponse.setEssentialClaims(transaction.getEssentialClaims());
linkTransactionResponse.setVoluntaryClaims(transaction.getVoluntaryClaims());
linkTransactionResponse.setAuthorizeScopes(transaction.getRequestedAuthorizeScopes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
*/
package io.mosip.esignet.services;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.JWK;
import io.mosip.esignet.api.dto.KycExchangeDto;
Expand Down Expand Up @@ -72,6 +76,9 @@ public class OAuthServiceImpl implements OAuthService {
@Autowired
private SecurityHelperService securityHelperService;

@Autowired
private ObjectMapper objectMapper;

@Value("${mosip.esignet.access-token-expire-seconds:60}")
private int accessTokenExpireSeconds;

Expand Down Expand Up @@ -253,30 +260,41 @@ private KycExchangeResult doKycExchange(OIDCTransaction transaction) {
kycExchangeDto.setAcceptedClaims(transaction.getAcceptedClaims());
kycExchangeDto.setClaimsLocales(transaction.getClaimsLocales());
kycExchangeDto.setIndividualId(authorizationHelperService.getIndividualId(transaction));
kycExchangeDto.setAcceptedVerifiedClaims(new HashMap<>());

if(!CollectionUtils.isEmpty(transaction.getAcceptedClaims()) && transaction.getRequestedClaims().getUserinfo() != null) {
Map<String, JsonNode> acceptedClaimDetails = new HashMap<>();
if(!CollectionUtils.isEmpty(transaction.getAcceptedClaims())) {
for(String claim : transaction.getAcceptedClaims()) {
List<Map<String, Object>> claimDetails = transaction.getRequestedClaims().getUserinfo().get(claim);

List<Map<String, Object>> result = claimDetails == null ? null : claimDetails.stream()
.filter( m -> m.get("verification") != null)
.map( m -> (Map<String, Object>)m.get("verification"))
.collect(Collectors.toList());
if(result != null) {
kycExchangeDto.getAcceptedVerifiedClaims().put(claim, result);
acceptedClaimDetails.put(claim, transaction.getRequestedClaimDetails() != null ?
transaction.getRequestedClaimDetails().get(claim) : null);
}

JsonNode verifiedClaims = transaction.getRequestedClaimDetails() != null ? transaction.getRequestedClaimDetails().get(VERIFIED_CLAIMS) : null;
if (verifiedClaims != null) {
if(verifiedClaims.isArray()) {
ArrayNode arrayNode = objectMapper.createArrayNode();
Iterator<JsonNode> itr = verifiedClaims.iterator();
while(itr.hasNext()) {
JsonNode jsonNode = removeDeniedClaims(transaction.getAcceptedClaims(), itr.next());
if(jsonNode != null) { arrayNode.add(jsonNode); }
}
acceptedClaimDetails.put(VERIFIED_CLAIMS, arrayNode);
}
else {
JsonNode jsonNode = removeDeniedClaims(transaction.getAcceptedClaims(), verifiedClaims);
if(jsonNode != null) { acceptedClaimDetails.put(VERIFIED_CLAIMS, verifiedClaims); }
}
}
}
kycExchangeDto.setAcceptedClaimDetails(acceptedClaimDetails);

if(transaction.isInternalAuthSuccess()) {
log.info("Internal kyc exchange is invoked as the transaction is marked as internal auth success");
kycExchangeResult = doInternalKycExchange(kycExchangeDto);
} else {
kycExchangeResult = kycExchangeDto.getAcceptedVerifiedClaims().isEmpty() ?
authenticationWrapper.doKycExchange(transaction.getRelyingPartyId(),
transaction.getClientId(), kycExchangeDto) :
kycExchangeResult = acceptedClaimDetails.containsKey(VERIFIED_CLAIMS) ?
authenticationWrapper.doVerifiedKycExchange(transaction.getRelyingPartyId(),
transaction.getClientId(), kycExchangeDto) :
authenticationWrapper.doKycExchange(transaction.getRelyingPartyId(),
transaction.getClientId(), kycExchangeDto);
}

Expand Down Expand Up @@ -306,4 +324,18 @@ private boolean isTransactionVCScoped(OIDCTransaction transaction) {
transaction.getPermittedScopes().stream()
.anyMatch(scope -> transaction.getRequestedCredentialScopes().contains(scope)));
}

private JsonNode removeDeniedClaims(List<String> acceptedClaims, JsonNode verifiedClaim) {
if(verifiedClaim.hasNonNull("claims")) {
Iterator<String> requestedClaims = verifiedClaim.get("claims").deepCopy().fieldNames();
while(requestedClaims.hasNext()) {
String claimName = requestedClaims.next();
if(!acceptedClaims.contains(claimName)) {
((ObjectNode)verifiedClaim.get("claims")).remove(claimName);
}
}
return verifiedClaim.get("claims").isEmpty() ? null : verifiedClaim;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import io.mosip.esignet.api.dto.*;
import io.mosip.esignet.api.dto.claim.ClaimDetail;
import io.mosip.esignet.api.dto.claim.Claims;
import io.mosip.esignet.api.exception.KycAuthException;
import io.mosip.esignet.api.exception.SendOtpException;
Expand Down Expand Up @@ -225,7 +224,7 @@ public void delegateAuthenticateRequest_withValidDetails_thenPass() throws KycAu
oidcTransaction.setRelyingPartyId("rp-id");
oidcTransaction.setClientId("client-id");
oidcTransaction.setAuthTransactionId("auth-transaction-id");
oidcTransaction.setRequestedClaims(new Claims());
oidcTransaction.setResolvedClaims(new Claims());
KycAuthResult kycAuthResult = new KycAuthResult();
kycAuthResult.setKycToken("kyc-token");
kycAuthResult.setPartnerSpecificUserToken("psut");
Expand All @@ -245,7 +244,7 @@ public void delegateAuthenticateRequest_withInvalidResult_thenFail() throws KycA
oidcTransaction.setRelyingPartyId("rp-id");
oidcTransaction.setClientId("client-id");
oidcTransaction.setAuthTransactionId("auth-transaction-id");
oidcTransaction.setRequestedClaims(new Claims());
oidcTransaction.setResolvedClaims(new Claims());

Mockito.when(authenticationWrapper.doKycAuth(Mockito.anyString(), Mockito.anyString(), anyBoolean(), any(KycAuthDto.class))).thenReturn(null);
try {
Expand Down Expand Up @@ -294,7 +293,7 @@ public void delegateAuthenticateRequest_ThrowsKycAuthException_thenFail() throws
oidcTransaction.setRelyingPartyId("rp-id");
oidcTransaction.setClientId("client-id");
oidcTransaction.setAuthTransactionId("auth-transaction-id");
oidcTransaction.setRequestedClaims(new Claims());
oidcTransaction.setResolvedClaims(new Claims());
Mockito.when(authenticationWrapper.doKycAuth(Mockito.anyString(), Mockito.anyString(), anyBoolean(), any(KycAuthDto.class))).thenThrow(KycAuthException.class);
try{
authorizationHelperService.delegateAuthenticateRequest(transactionId, individualId, challengeList, oidcTransaction);
Expand Down Expand Up @@ -397,7 +396,7 @@ public void getProvidedAuthFactors_withValidInput_thenPass() {
map.put("values", new String [] {"generated-code", "static-code"});
resolvedClaims.getId_token().put(ACR, map);
OIDCTransaction oidcTransaction = new OIDCTransaction();
oidcTransaction.setRequestedClaims(resolvedClaims);
oidcTransaction.setResolvedClaims(resolvedClaims);

List<AuthChallenge> challengeList = new ArrayList<>();
AuthChallenge authChallenge = new AuthChallenge();
Expand Down Expand Up @@ -426,7 +425,7 @@ public void getProvidedAuthFactors_withAuthFactorMismatch_thenFail() {
map.put("values", new String [] {"generated-code", "static-code"});
resolvedClaims.getId_token().put(ACR, map);
OIDCTransaction oidcTransaction = new OIDCTransaction();
oidcTransaction.setRequestedClaims(resolvedClaims);
oidcTransaction.setResolvedClaims(resolvedClaims);

List<AuthChallenge> challengeList = new ArrayList<>();
AuthChallenge authChallenge = new AuthChallenge();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,7 @@ public void getAuthCode_withValidInput_thenPass() {
nameMap.put("essential", true);
userinfo.put("fullName", Arrays.asList(nameMap));
requestedClaims.setUserinfo(userinfo);
transaction.setRequestedClaims(requestedClaims);
transaction.setResolvedClaims(requestedClaims);
Mockito.when(cacheUtilService.getAuthenticatedTransaction(Mockito.anyString())).thenReturn(transaction);
Mockito.when(cacheUtilService.setAuthCodeGeneratedTransaction(Mockito.anyString(), Mockito.any())).thenReturn(transaction);
Assert.assertEquals(authorizationServiceImpl.getAuthCode(authCodeRequest).getNonce(), "test-nonce");
Expand Down Expand Up @@ -1378,7 +1378,7 @@ public void getClaimDetails_withValidTransaction_thenPass(){
map.put("verification", new HashMap<>());
resolvedClaims.getUserinfo().put("name", Arrays.asList(map));

transaction.setRequestedClaims(resolvedClaims);
transaction.setResolvedClaims(resolvedClaims);
transaction.setEssentialClaims(List.of("name", "email"));
transaction.setVoluntaryClaims(List.of("phone_number"));
transaction.setConsentAction(ConsentAction.NOCAPTURE);
Expand Down Expand Up @@ -1484,7 +1484,7 @@ private OIDCTransaction createIdpTransaction(String[] acrs) {
idClaims.put(ACR, map);
Claims requestedClaims = new Claims();
requestedClaims.setId_token(idClaims);
oidcTransaction.setRequestedClaims(requestedClaims);
oidcTransaction.setResolvedClaims(requestedClaims);
oidcTransaction.setClientId("test-client");
oidcTransaction.setRelyingPartyId("test-rp-client");
return oidcTransaction;
Expand Down
Loading

0 comments on commit 050e628

Please sign in to comment.