Skip to content

Commit

Permalink
Hotfix v1.2.0 (#43)
Browse files Browse the repository at this point in the history
* Hotfix for dome verifier

* Update build.gradle

* Update ApplicationRegexPattern.java

* Fix url mapping

* Url mapping fix

* fix(broker): solve confusion between internal and external

* test(broker): fix tests to check get internal url and entitites path

* test(broker): refactor test

* test(util): tests to format url

* Fix Vp for dome

* Fix verifiable presentation for dome flow

* Added signed VP for dome

* Remove log

* refactor(broker): improve naming

* Update PresentationServiceImpl.java

---------

Co-authored-by: Albert Rodríguez <albert.rodriguez@in2.es>
  • Loading branch information
rubenmodamioin2 and albertrodriguezin2 committed Jun 17, 2024
1 parent 2b4a51a commit 4779ed5
Show file tree
Hide file tree
Showing 24 changed files with 326 additions and 170 deletions.
4 changes: 1 addition & 3 deletions src/main/java/es/in2/wallet/domain/model/QrType.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package es.in2.wallet.domain.model;

public enum QrType {
VC_LOGIN_REQUEST,
CREDENTIAL_OFFER_URI,
OPENID_CREDENTIAL_OFFER,
EBSI_CREDENTIAL_OFFER,
OPENID_AUTHENTICATION_REQUEST,
DOME_VC_LOGIN_REQUEST,
VP_TOKEN_AUTHENTICATION_REQUEST,
UNKNOWN
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public record VcSelectorRequest(
@JsonProperty("redirectUri") String redirectUri,
@JsonProperty("state") String state,
@JsonProperty("nonce") String nonce,
@JsonProperty("selectableVcList") List<CredentialsBasicInfo> selectableVcList
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
public record VcSelectorResponse(
@JsonProperty("redirectUri") String redirectUri,
@JsonProperty("state") String state,
@JsonProperty("nonce") String nonce,
@JsonProperty("selectedVcList") List<CredentialsBasicInfo> selectedVcList

) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import com.nimbusds.jwt.SignedJWT;
import es.in2.wallet.application.port.AppConfig;
import es.in2.wallet.application.port.BrokerService;
import es.in2.wallet.domain.exception.ParseErrorException;
import es.in2.wallet.domain.model.CredentialsBasicInfo;
import es.in2.wallet.domain.model.DomeVerifiablePresentation;
import es.in2.wallet.domain.model.VcSelectorResponse;
import es.in2.wallet.domain.model.VerifiablePresentation;
import es.in2.wallet.domain.service.DataService;
Expand Down Expand Up @@ -69,7 +67,7 @@ public Mono<String> createSignedVerifiablePresentation(String processId, String

@Override
public Mono<String> createEncodedVerifiablePresentationForDome(String processId, String authorizationToken, VcSelectorResponse vcSelectorResponse) {
return createVerifiablePresentationForDome(processId, authorizationToken,vcSelectorResponse.selectedVcList());
return createVerifiablePresentationForDome(processId, authorizationToken,vcSelectorResponse);
}

private Mono<String> createSignedVerifiablePresentation(String processId, String authorizationToken,String nonce, String audience, List<CredentialsBasicInfo> selectedVcList, String format) {
Expand All @@ -79,7 +77,8 @@ private Mono<String> createSignedVerifiablePresentation(String processId, String
.flatMap(did -> getVerifiableCredentials(processId,userId,selectedVcList, format)
.flatMap(verifiableCredentialsList -> // Create the unsigned verifiable presentation
createUnsignedPresentation(verifiableCredentialsList, did,nonce,audience)
.flatMap(document -> signerService.buildJWTSFromJsonNode(document,did,"vp")))
.flatMap(document -> signerService.buildJWTSFromJsonNode(document,did,"vp"))
.flatMap(this::encodePresentation))
)
)
// Log success
Expand All @@ -92,17 +91,22 @@ private Mono<String> createSignedVerifiablePresentation(String processId, String
);
}

private Mono<String> createVerifiablePresentationForDome(String processId, String authorizationToken,List<CredentialsBasicInfo> selectedVcList) {
private Mono<String> createVerifiablePresentationForDome(String processId, String authorizationToken,VcSelectorResponse vcSelectorResponse) {
return getUserIdFromToken(authorizationToken)
.flatMap(userId ->getVerifiableCredentials(processId,userId,selectedVcList, VC_JSON)
.flatMap(this::createEncodedPresentation)
// Log success
.doOnSuccess(verifiablePresentation -> log.info("ProcessID: {} - DOME Verifiable Presentation created successfully: {}", processId, verifiablePresentation))
// Handle errors
.onErrorResume(e -> {
log.error("Error in creating Verifiable Presentation: ", e);
return Mono.error(e);
})
.flatMap(userId ->getVerifiableCredentials(processId,userId,vcSelectorResponse.selectedVcList(), VC_JWT)
.flatMap(verifiableCredentialsList -> getSubjectDidFromTheFirstVcOfTheList(verifiableCredentialsList)
.flatMap(did -> createUnsignedPresentation(verifiableCredentialsList, did, vcSelectorResponse.nonce(), null)
.flatMap(document -> signerService.buildJWTSFromJsonNode(document,did,"vp")))
.flatMap(this::encodePresentation)

)
// Log success
.doOnSuccess(verifiablePresentation -> log.info("ProcessID: {} - DOME Verifiable Presentation created successfully: {}", processId, verifiablePresentation))
// Handle errors
.onErrorResume(e -> {
log.error("Error in creating Verifiable Presentation: ", e);
return Mono.error(e);
})
);
}
/**
Expand Down Expand Up @@ -171,52 +175,27 @@ private Mono<JsonNode> createUnsignedPresentation(
Instant issueTime = Instant.now();
Instant expirationTime = issueTime.plus(appConfig.getCredentialPresentationExpirationTime(), ChronoUnit.valueOf(appConfig.getCredentialPresentationExpirationUnit().toUpperCase()));
Map<String, Object> vpParsed = JWTClaimsSet.parse(objectMapper.writeValueAsString(vpBuilder)).getClaims();
JWTClaimsSet payload = new JWTClaimsSet.Builder()
JWTClaimsSet.Builder payloadBuilder = new JWTClaimsSet.Builder()
.issuer(holderDid)
.subject(holderDid)
.audience(audience)
.notBeforeTime(java.util.Date.from(issueTime))
.expirationTime(java.util.Date.from(expirationTime))
.issueTime(java.util.Date.from(issueTime))
.jwtID(id)
.claim("vp", vpParsed)
.claim("nonce", nonce)
.build();
.claim("nonce", nonce);

if (audience != null) {
payloadBuilder.audience(audience);
}

JWTClaimsSet payload = payloadBuilder.build();
log.debug(payload.toString());
return objectMapper.readTree(payload.toString());
});
}

/**
* Creates an unsigned Verifiable Presentation containing the selected VCs.
*
* @param vcs The list of VC JWTs to include in the VP.
*/
private Mono<String> createEncodedPresentation(
List<String> vcs) {
return Mono.fromCallable(() -> {
List<JsonNode> vcsJsonList = vcs.stream()
.map(vc -> {
try {
return objectMapper.readTree(vc);
} catch (Exception e) {
throw new ParseErrorException("Error parsing VC string to JsonNode");
}
})
.toList();

DomeVerifiablePresentation vp = DomeVerifiablePresentation
.builder()
.holder("did:my:wallet")
.context(List.of(JSONLD_CONTEXT_W3C_2018_CREDENTIALS_V1))
.type(List.of(VERIFIABLE_PRESENTATION))
.verifiableCredential(vcsJsonList)
.build();

String vpJson = objectMapper.writeValueAsString(vp);

return Base64.getUrlEncoder().withoutPadding().encodeToString(vpJson.getBytes());

});
private Mono<String> encodePresentation(String vp) {
return Mono.fromCallable(() -> Base64.getUrlEncoder().withoutPadding().encodeToString(vp.getBytes()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,17 @@ public Mono<Object> processQrContent(String processId, String authorizationToken
.doOnSuccess(credential -> log.info("ProcessID: {} - Credential Issued: {}", processId, credential))
.doOnError(e -> log.error("ProcessID: {} - Error while issuing credential: {}", processId, e.getMessage()));
}
case VC_LOGIN_REQUEST: {
log.info("ProcessID: {} - Processing a Verifiable Credential Login Request", processId);
return attestationExchangeCommonWorkflow.processAuthorizationRequest(processId, authorizationToken, qrContent)
.doOnSuccess(credential -> log.info("ProcessID: {} - Attestation Exchange", processId))
.doOnError(e -> log.error("ProcessID: {} - Error while processing Attestation Exchange: {}", processId, e.getMessage()));
}
case OPENID_AUTHENTICATION_REQUEST: {
log.info("ProcessID: {} - Processing an Authentication Request", processId);
return Mono.error(new NoSuchQrContentException("OpenID Authentication Request not implemented yet"));
}
case DOME_VC_LOGIN_REQUEST: {
log.info("ProcessID: {} - Processing an Authentication Request from DOME", processId);
return attestationExchangeDOMEWorkflow.getSelectableCredentialsRequiredToBuildThePresentation(processId,authorizationToken,qrContent);
case VP_TOKEN_AUTHENTICATION_REQUEST: {
if (DOME_LOGIN_REQUEST_PATTERN.matcher(qrContent).matches()){
log.info("ProcessID: {} - Processing an Authentication Request from DOME", processId);
return attestationExchangeDOMEWorkflow.getSelectableCredentialsRequiredToBuildThePresentation(processId,authorizationToken,qrContent);
}
else {
log.info("ProcessID: {} - Processing a Verifiable Credential Login Request for common workflow", processId);
return attestationExchangeCommonWorkflow.processAuthorizationRequest(processId, authorizationToken, qrContent)
.doOnSuccess(credential -> log.info("ProcessID: {} - Attestation Exchange", processId))
.doOnError(e -> log.error("ProcessID: {} - Error while processing Attestation Exchange: {}", processId, e.getMessage()));
}
}
case UNKNOWN: {
String errorMessage = "The received QR content cannot be processed";
Expand All @@ -71,20 +69,16 @@ public Mono<Object> processQrContent(String processId, String authorizationToken

private Mono<QrType> identifyQrContentType(String qrContent) {
return Mono.fromSupplier(() -> {
if(DOME_LOGIN_REQUEST_PATTERN.matcher(qrContent).matches()){
return DOME_VC_LOGIN_REQUEST;
if(VP_TOKEN_AUTHENTICATION_REQUEST_PATTERN.matcher(qrContent).matches()){
return VP_TOKEN_AUTHENTICATION_REQUEST;
}
else if (LOGIN_REQUEST_PATTERN.matcher(qrContent).matches()) {
return VC_LOGIN_REQUEST;
} else if (CREDENTIAL_OFFER_PATTERN.matcher(qrContent).matches()) {
else if (CREDENTIAL_OFFER_PATTERN.matcher(qrContent).matches()) {
return QrType.CREDENTIAL_OFFER_URI;
} else if (EBSI_CREDENTIAL_OFFER_PATTERN.matcher(qrContent).matches()){
return EBSI_CREDENTIAL_OFFER;
} else if (OPENID_CREDENTIAL_OFFER_PATTERN.matcher(qrContent).matches()) {
return OPENID_CREDENTIAL_OFFER;
} else if (OPENID_AUTHENTICATION_REQUEST_PATTERN.matcher(qrContent).matches()) {
return OPENID_AUTHENTICATION_REQUEST;
} else {
} else {
log.warn("Unknown QR content type: {}", qrContent);
return UNKNOWN;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public Mono<String> buildJWTSFromJsonNode(JsonNode document, String did, String
.flatMap(docType -> Mono.fromCallable(() -> {
try {
ECKey ecJWK = JWK.parse(privateKey.value().toString()).toECKey();
log.debug("ECKey: {}", ecJWK);

JWSAlgorithm jwsAlgorithm = mapToJWSAlgorithm(ecJWK.getAlgorithm());
JWSSigner signer = new ECDSASigner(ecJWK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private ApplicationRegexPattern() {
public static final Pattern CREDENTIAL_OFFER_PATTERN = Pattern.compile("(https|http)\\S*(credential-offer)\\S*");
public static final Pattern OPENID_CREDENTIAL_OFFER_PATTERN = Pattern.compile("openid-credential-offer://\\S*");
public static final Pattern EBSI_CREDENTIAL_OFFER_PATTERN = Pattern.compile("\\S*(conformance.ebsi)\\S*");
public static final Pattern DOME_LOGIN_REQUEST_PATTERN = Pattern.compile("\\S*(did:web:dome-marketplace.org)\\S*");
public static final Pattern DOME_REDIRECT_URI_PATTERN = Pattern.compile("\\S*(dome-marketplace.org)\\S*");
public static final Pattern OPENID_AUTHENTICATION_REQUEST_PATTERN = Pattern.compile("openid://\\S*");
public static final Pattern DOME_LOGIN_REQUEST_PATTERN = Pattern.compile("\\S*(dome)\\S*");
public static final Pattern DOME_REDIRECT_URI_PATTERN = Pattern.compile("\\S*(dome)\\S*");
public static final Pattern VP_TOKEN_AUTHENTICATION_REQUEST_PATTERN = Pattern.compile("\\S*(response_type=vp_token)\\S*");
}
16 changes: 16 additions & 0 deletions src/main/java/es/in2/wallet/domain/util/ApplicationUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
Expand Down Expand Up @@ -113,4 +114,19 @@ public static Mono<String> extractResponseType(String jwt){
return signedJwt.getJWTClaimsSet().getClaim("response_type").toString();
});
}
public static String formatUrl(String scheme, String domain, Integer port, String path) {
UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance()
.scheme(scheme)
.host(domain);

if (port != null && port != 443) {
uriBuilder.port(port);
}

if (path != null) {
uriBuilder.path(path);
}

return uriBuilder.toUriString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@


import es.in2.wallet.application.port.AppConfig;
import es.in2.wallet.infrastructure.appconfiguration.util.ConfigAdapterFactory;
import es.in2.wallet.infrastructure.core.config.properties.AuthServerProperties;
import es.in2.wallet.infrastructure.core.config.properties.VerifiablePresentationProperties;
import es.in2.wallet.infrastructure.core.config.properties.WalletDrivingApplicationProperties;
import es.in2.wallet.infrastructure.ebsi.config.properties.EbsiProperties;
import es.in2.wallet.infrastructure.appconfiguration.util.ConfigAdapterFactory;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.util.List;

import static es.in2.wallet.domain.util.ApplicationUtils.formatUrl;

@Configuration
@Slf4j
public class AppConfigImpl implements AppConfig {
Expand Down Expand Up @@ -59,7 +61,7 @@ public List<String> getWalletDrivingUrls() {
String domain = "localhost".equalsIgnoreCase(urlProperties.domain()) ?
urlProperties.domain() :
genericConfigAdapter.getConfiguration(urlProperties.domain());
return formatUrl(urlProperties.scheme(), domain, urlProperties.port());
return formatUrl(urlProperties.scheme(), domain, urlProperties.port(), null);
})
.toList();
}
Expand Down Expand Up @@ -136,20 +138,6 @@ public String getCredentialPresentationExpirationUnit() {
return verifiablePresentationProperties.expirationUnit();
}

private String formatUrl(String scheme, String domain, int port) {
if (port == 443) {
return String.format("%s://%s", scheme, domain);
}
return String.format("%s://%s:%d", scheme, domain, port);
}

private String formatUrl(String scheme, String domain, int port, String path) {
if (port == 443) {
return String.format("%s://%s%s", scheme, domain, path);
}
return String.format("%s://%s:%d%s", scheme, domain, port, path);
}

private String getAuthServerJwtDecoderPath() {
return authServerProperties.jwtDecoderPath();
}
Expand Down
Loading

0 comments on commit 4779ed5

Please sign in to comment.