From 39bf1cae0c6da65e23fa268b3e7d77d035d7a7df Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 18 Jun 2026 12:09:26 +0530 Subject: [PATCH 01/21] Add REST API module for VP Presentation Definition management --- .../pom.xml | 45 +++ .../VPDefinitionManagementConstants.java | 83 ++++ .../VPDefinitionManagementServiceHolder.java | 44 +++ .../pom.xml | 132 +++++++ .../vp/template/management/v1/Error.java | 76 ++++ .../PresentationDefinitionCreationModel.java | 71 ++++ .../v1/PresentationDefinitionList.java | 57 +++ .../v1/PresentationDefinitionListItem.java | 65 ++++ .../v1/PresentationDefinitionResponse.java | 76 ++++ .../v1/PresentationDefinitionUpdateModel.java | 67 ++++ .../v1/PresentationDefinitionsApi.java | 141 +++++++ .../v1/PresentationDefinitionsApiService.java | 39 ++ .../v1/RequestedCredentialModel.java | 61 +++ ...sentationDefinitionsApiServiceFactory.java | 35 ++ .../ServerVPDefinitionManagementService.java | 317 ++++++++++++++++ ...PresentationDefinitionsApiServiceImpl.java | 79 ++++ .../resources/PresentationDefinitions.yaml | 357 ++++++++++++++++++ .../pom.xml | 37 ++ 18 files changed, 1782 insertions(+) create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementConstants.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/Error.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionCreationModel.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionList.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionListItem.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionResponse.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionUpdateModel.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApi.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApiService.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/factories/PresentationDefinitionsApiServiceFactory.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/impl/PresentationDefinitionsApiServiceImpl.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml new file mode 100644 index 0000000000..865557f73e --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml @@ -0,0 +1,45 @@ + + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.template.management + 1.6.30-SNAPSHOT + ../pom.xml + + + org.wso2.carbon.identity.api.server.vp.template.management.common + jar + WSO2 Carbon - VP Template Management API Common + + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.management + provided + + + diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementConstants.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementConstants.java new file mode 100644 index 0000000000..7773b94dc1 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementConstants.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.common; + +/** + * Constants for Presentation Definition Management API. + */ +public class VPDefinitionManagementConstants { + + private VPDefinitionManagementConstants() { + } + + public static final String VP_DEFINITION_MANAGEMENT_PATH_COMPONENT = "/presentation-definitions"; + + /** + * Enum for error messages. + */ + public enum ErrorMessage { + + // Client errors (60xxx). + ERROR_CODE_DEFINITION_NOT_FOUND("VPD-60001", "Presentation definition not found.", + "Unable to find a presentation definition with the id: %s"), + ERROR_CODE_INVALID_INPUT("VPD-60002", "Invalid input.", + "Provided input is invalid. %s"), + ERROR_CODE_DEFINITION_ALREADY_EXISTS("VPD-60003", "Presentation definition already exists.", + "A presentation definition with the given identifier already exists."), + + // Server errors (65xxx). + ERROR_CODE_ERROR_LISTING_DEFINITIONS("VPD-65001", "Error listing presentation definitions.", + "A system error occurred while listing presentation definitions."), + ERROR_CODE_ERROR_CREATING_DEFINITION("VPD-65002", "Error creating presentation definition.", + "A system error occurred while creating the presentation definition."), + ERROR_CODE_ERROR_RETRIEVING_DEFINITION("VPD-65003", "Error retrieving presentation definition.", + "A system error occurred while retrieving the presentation definition with id: %s"), + ERROR_CODE_ERROR_UPDATING_DEFINITION("VPD-65004", "Error updating presentation definition.", + "A system error occurred while updating the presentation definition with id: %s"), + ERROR_CODE_ERROR_DELETING_DEFINITION("VPD-65005", "Error deleting presentation definition.", + "A system error occurred while deleting the presentation definition with id: %s"); + + private final String code; + private final String message; + private final String description; + + ErrorMessage(String code, String message, String description) { + this.code = code; + this.message = message; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return code + " | " + message; + } + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java new file mode 100644 index 0000000000..fc84bd576b --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.common; + +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.openid4vc.presentation.management.service.PresentationDefinitionService; + +/** + * Service holder for VP Definition Management. + * Obtains the PresentationDefinitionService OSGi service via PrivilegedCarbonContext. + */ +public class VPDefinitionManagementServiceHolder { + + private VPDefinitionManagementServiceHolder() { + } + + /** + * Get PresentationDefinitionService OSGi service. + * + * @return PresentationDefinitionService instance + */ + public static PresentationDefinitionService getPresentationDefinitionService() { + + return (PresentationDefinitionService) PrivilegedCarbonContext + .getThreadLocalCarbonContext() + .getOSGiService(PresentationDefinitionService.class, null); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml new file mode 100644 index 0000000000..d52041cea1 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml @@ -0,0 +1,132 @@ + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.template.management + 1.6.30-SNAPSHOT + ../pom.xml + + + WSO2 Identity Server - VP Template Management API + WSO2 IS - VP Template Management API + org.wso2.carbon.identity.api.server.vp.template.management.v1 + jar + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + provided + + + org.apache.cxf + cxf-rt-frontend-jaxrs + provided + + + javax.ws.rs + javax.ws.rs-api + provided + + + io.swagger + swagger-jaxrs + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + javax.ws.rs + jsr311-api + + + com.google.guava + guava + + + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.common + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.template.management.common + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.management + provided + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.common + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + 1.8 + 1.8 + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + + diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/Error.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/Error.java new file mode 100644 index 0000000000..8d81e797a3 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/Error.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Error response model. + */ +@ApiModel(description = "Error response") +public class Error { + + private String code; + private String message; + private String description; + private String traceId; + + @ApiModelProperty(required = true, value = "Error code.") + @JsonProperty("code") + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @ApiModelProperty(required = true, value = "Error message.") + @JsonProperty("message") + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @ApiModelProperty(value = "Error description.") + @JsonProperty("description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(value = "Trace ID for debugging.") + @JsonProperty("traceId") + public String getTraceId() { + return traceId; + } + + public void setTraceId(String traceId) { + this.traceId = traceId; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionCreationModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionCreationModel.java new file mode 100644 index 0000000000..3ca66bebea --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionCreationModel.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +import javax.validation.constraints.NotNull; + +/** + * Creation model for presentation definition. + */ +@ApiModel(description = "Creation model for presentation definition") +public class PresentationDefinitionCreationModel { + + @NotNull + private String name; + private String description; + @NotNull + private List credentials; + + @ApiModelProperty(required = true, value = "Name of the presentation definition.") + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(value = "Description of the presentation definition.") + @JsonProperty("description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(required = true, value = "The requested credentials to construct the definition.") + @JsonProperty("credentials") + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionList.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionList.java new file mode 100644 index 0000000000..e0a2b2b3ab --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionList.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.ArrayList; +import java.util.List; + +/** + * List response model for presentation definitions. + */ +@ApiModel(description = "List of presentation definitions") +public class PresentationDefinitionList { + + private Integer totalResults; + private List presentationDefinitions = new ArrayList<>(); + + @ApiModelProperty(value = "Total number of presentation definitions.") + @JsonProperty("totalResults") + public Integer getTotalResults() { + return totalResults; + } + + public void setTotalResults(Integer totalResults) { + this.totalResults = totalResults; + } + + @ApiModelProperty(value = "List of presentation definitions.") + @JsonProperty("presentationDefinitions") + public List getPresentationDefinitions() { + return presentationDefinitions; + } + + public void setPresentationDefinitions(List presentationDefinitions) { + this.presentationDefinitions = presentationDefinitions; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionListItem.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionListItem.java new file mode 100644 index 0000000000..eeba242490 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionListItem.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * List item model for presentation definitions. + */ +@ApiModel(description = "Presentation definition list item") +public class PresentationDefinitionListItem { + + private String id; + private String name; + private String description; + + @ApiModelProperty(value = "Unique identifier of the presentation definition.") + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @ApiModelProperty(value = "Name of the presentation definition.") + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(value = "Description of the presentation definition.") + @JsonProperty("description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionResponse.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionResponse.java new file mode 100644 index 0000000000..8fc84deb10 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionResponse.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Presentation definition response model. + */ +@ApiModel(description = "Presentation definition response") +public class PresentationDefinitionResponse { + + private String id; + private String name; + private String description; + private java.util.List credentials; + + @ApiModelProperty(value = "Unique identifier of the presentation definition.") + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @ApiModelProperty(value = "Name of the presentation definition.") + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(value = "Description of the presentation definition.") + @JsonProperty("description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(value = "The list of requested credentials in this presentation definition.") + @JsonProperty("credentials") + public java.util.List getCredentials() { + return credentials; + } + + public void setCredentials(java.util.List credentials) { + this.credentials = credentials; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionUpdateModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionUpdateModel.java new file mode 100644 index 0000000000..6e97b5433d --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionUpdateModel.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; + +/** + * Update model for presentation definition. + */ +@ApiModel(description = "Update model for presentation definition") +public class PresentationDefinitionUpdateModel { + + private String name; + private String description; + private List credentials; + + @ApiModelProperty(value = "Name of the presentation definition.") + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(value = "Description of the presentation definition.") + @JsonProperty("description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @ApiModelProperty(value = "The requested credentials to construct the definition.") + @JsonProperty("credentials") + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApi.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApi.java new file mode 100644 index 0000000000..8ce62fb1b8 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApi.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.factories.PresentationDefinitionsApiServiceFactory; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * JAX-RS resource for Presentation Definition management. + */ +@Path("/vp/template") +@Api(value = "/vp/template", description = "Presentation Definition Management API") +public class PresentationDefinitionsApi { + + private final PresentationDefinitionsApiService delegate = + PresentationDefinitionsApiServiceFactory.getPresentationDefinitionsApi(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "List Presentation Definitions", response = PresentationDefinitionList.class) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK", response = PresentationDefinitionList.class), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response listPresentationDefinitions() { + + return delegate.listPresentationDefinitions(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Create a Presentation Definition", + response = PresentationDefinitionResponse.class) + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Created", response = PresentationDefinitionResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 409, message = "Conflict", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response createPresentationDefinition( + @ApiParam(value = "Presentation definition to create", required = true) + PresentationDefinitionCreationModel presentationDefinitionCreationModel) { + + return delegate.createPresentationDefinition(presentationDefinitionCreationModel); + } + + @GET + @Path("/{definition-id}") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Get a Presentation Definition", + response = PresentationDefinitionResponse.class) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK", response = PresentationDefinitionResponse.class), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response getPresentationDefinition( + @ApiParam(value = "Unique identifier of the presentation definition.", required = true) + @PathParam("definition-id") String definitionId) { + + return delegate.getPresentationDefinition(definitionId); + } + + @PUT + @Path("/{definition-id}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Update a Presentation Definition", + response = PresentationDefinitionResponse.class) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK", response = PresentationDefinitionResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response updatePresentationDefinition( + @ApiParam(value = "Unique identifier of the presentation definition.", required = true) + @PathParam("definition-id") String definitionId, + @ApiParam(value = "Updated presentation definition", required = true) + PresentationDefinitionUpdateModel presentationDefinitionUpdateModel) { + + return delegate.updatePresentationDefinition(definitionId, presentationDefinitionUpdateModel); + } + + @DELETE + @Path("/{definition-id}") + @ApiOperation(value = "Delete a Presentation Definition") + @ApiResponses(value = { + @ApiResponse(code = 204, message = "No Content"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 403, message = "Forbidden"), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response deletePresentationDefinition( + @ApiParam(value = "Unique identifier of the presentation definition.", required = true) + @PathParam("definition-id") String definitionId) { + + return delegate.deletePresentationDefinition(definitionId); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApiService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApiService.java new file mode 100644 index 0000000000..58e41bda62 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/PresentationDefinitionsApiService.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import javax.ws.rs.core.Response; + +/** + * Service interface for Presentation Definitions API. + */ +public abstract class PresentationDefinitionsApiService { + + public abstract Response listPresentationDefinitions(); + + public abstract Response createPresentationDefinition( + PresentationDefinitionCreationModel presentationDefinitionCreationModel); + + public abstract Response getPresentationDefinition(String definitionId); + + public abstract Response updatePresentationDefinition(String definitionId, + PresentationDefinitionUpdateModel presentationDefinitionUpdateModel); + + public abstract Response deletePresentationDefinition(String definitionId); +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java new file mode 100644 index 0000000000..7471d589e6 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java @@ -0,0 +1,61 @@ +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * Requested Credential Model. + */ +@ApiModel(description = "Requested Credential Model") +public class RequestedCredentialModel { + + @NotNull + private String type; + private String purpose; + private String issuer; + private List claims; + + @ApiModelProperty(required = true, value = "Type of the requested credential.") + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @ApiModelProperty(value = "Purpose of requesting the credential.") + @JsonProperty("purpose") + public String getPurpose() { + return purpose; + } + + public void setPurpose(String purpose) { + this.purpose = purpose; + } + + @ApiModelProperty(value = "The trusted issuer for this credential.") + @JsonProperty("issuer") + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + @ApiModelProperty(value = "List of claims requested from this credential.") + @JsonProperty("claims") + public List getClaims() { + return claims; + } + + public void setClaims(List claims) { + this.claims = claims; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/factories/PresentationDefinitionsApiServiceFactory.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/factories/PresentationDefinitionsApiServiceFactory.java new file mode 100644 index 0000000000..f9c8f39378 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/factories/PresentationDefinitionsApiServiceFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1.factories; + +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionsApiService; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.impl.PresentationDefinitionsApiServiceImpl; + +/** + * Factory class for PresentationDefinitionsApiService. + */ +public class PresentationDefinitionsApiServiceFactory { + + private static final PresentationDefinitionsApiService SERVICE = + new PresentationDefinitionsApiServiceImpl(); + + public static PresentationDefinitionsApiService getPresentationDefinitionsApi() { + return SERVICE; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java new file mode 100644 index 0000000000..e40e477906 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1.core; + +import org.wso2.carbon.identity.api.server.common.ContextLoader; +import org.wso2.carbon.identity.api.server.vp.template.management.common.VPDefinitionManagementConstants.ErrorMessage; +import org.wso2.carbon.identity.api.server.vp.template.management.common.VPDefinitionManagementServiceHolder; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.Error; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionCreationModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionList; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionListItem; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionResponse; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionUpdateModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.RequestedCredentialModel; +import org.wso2.carbon.identity.openid4vc.presentation.common.exception.VPException; +import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationDefinitionNotFoundException; +import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition; +import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition.RequestedCredential; +import org.wso2.carbon.identity.openid4vc.presentation.management.service.PresentationDefinitionService; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.ws.rs.core.Response; + +/** + * Core service for VP Presentation Definition Management API. + * Handles business logic, model conversion, and error mapping. + */ +public class ServerVPDefinitionManagementService { + + /** + * List all presentation definitions for the current tenant. + * + * @return PresentationDefinitionList + */ + public PresentationDefinitionList listPresentationDefinitions() { + + try { + int tenantId = getTenantId(); + PresentationDefinitionService service = getService(); + + List definitions = service.getAllPresentationDefinitions(tenantId); + + PresentationDefinitionList listResponse = new PresentationDefinitionList(); + listResponse.setTotalResults(definitions.size()); + listResponse.setPresentationDefinitions( + definitions.stream() + .map(this::toListItem) + .collect(Collectors.toList())); + return listResponse; + } catch (VPException e) { + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_LISTING_DEFINITIONS, e); + } + } + + /** + * Create a new presentation definition. + * + * @param creationModel The creation model + * @return Created PresentationDefinitionResponse + */ + public PresentationDefinitionResponse createPresentationDefinition( + PresentationDefinitionCreationModel creationModel) { + + try { + int tenantId = getTenantId(); + PresentationDefinitionService service = getService(); + + String definitionId = UUID.randomUUID().toString(); + + PresentationDefinition definition = new PresentationDefinition.Builder() + .definitionId(definitionId) + .name(creationModel.getName()) + .description(creationModel.getDescription()) + .requestedCredentials(toRequestedCredentials(creationModel.getCredentials())) + .tenantId(tenantId) + .build(); + + PresentationDefinition created = service.createPresentationDefinition(definition, tenantId); + return toResponse(created); + } catch (VPException e) { + if (e.getMessage() != null && e.getMessage().contains("already exists")) { + throw handleClientError(ErrorMessage.ERROR_CODE_DEFINITION_ALREADY_EXISTS, e, + Response.Status.CONFLICT); + } + if (e.getMessage() != null && (e.getMessage().contains("Invalid") || + e.getMessage().contains("required"))) { + throw handleClientError(ErrorMessage.ERROR_CODE_INVALID_INPUT, e, + Response.Status.BAD_REQUEST, e.getMessage()); + } + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_CREATING_DEFINITION, e); + } + } + + /** + * Get a presentation definition by ID. + * + * @param definitionId The definition ID + * @return PresentationDefinitionResponse + */ + public PresentationDefinitionResponse getPresentationDefinition(String definitionId) { + + try { + int tenantId = getTenantId(); + PresentationDefinitionService service = getService(); + + PresentationDefinition definition = service.getPresentationDefinitionById( + definitionId, tenantId); + return toResponse(definition); + } catch (PresentationDefinitionNotFoundException e) { + throw handleNotFound(definitionId); + } catch (VPException e) { + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_RETRIEVING_DEFINITION, e, + definitionId); + } + } + + /** + * Update a presentation definition. + * + * @param definitionId The definition ID + * @param updateModel The update model + * @return Updated PresentationDefinitionResponse + */ + public PresentationDefinitionResponse updatePresentationDefinition( + String definitionId, PresentationDefinitionUpdateModel updateModel) { + + try { + int tenantId = getTenantId(); + PresentationDefinitionService service = getService(); + + List credentials = updateModel.getCredentials() != null + ? toRequestedCredentials(updateModel.getCredentials()) + : null; + + PresentationDefinition definition = new PresentationDefinition.Builder() + .definitionId(definitionId) + .name(updateModel.getName()) + .description(updateModel.getDescription()) + .requestedCredentials(credentials) + .tenantId(tenantId) + .build(); + + PresentationDefinition updated = service.updatePresentationDefinition( + definition, tenantId); + return toResponse(updated); + } catch (PresentationDefinitionNotFoundException e) { + throw handleNotFound(definitionId); + } catch (VPException e) { + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_UPDATING_DEFINITION, e, + definitionId); + } + } + + /** + * Delete a presentation definition. + * + * @param definitionId The definition ID + */ + public void deletePresentationDefinition(String definitionId) { + + try { + int tenantId = getTenantId(); + PresentationDefinitionService service = getService(); + + service.deletePresentationDefinition(definitionId, tenantId); + } catch (PresentationDefinitionNotFoundException e) { + throw handleNotFound(definitionId); + } catch (VPException e) { + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_DELETING_DEFINITION, e, + definitionId); + } + } + + // --- Conversion helpers --- + + /** + * Convert API RequestedCredentialModel list to domain RequestedCredential list. + */ + private List toRequestedCredentials( + List apiModels) { + + if (apiModels == null) { + return null; + } + List result = new ArrayList<>(); + for (RequestedCredentialModel apiModel : apiModels) { + RequestedCredential cred = new RequestedCredential(); + cred.setType(apiModel.getType()); + cred.setPurpose(apiModel.getPurpose()); + cred.setIssuer(apiModel.getIssuer()); + cred.setClaims(apiModel.getClaims()); + result.add(cred); + } + return result; + } + + /** + * Convert domain RequestedCredential list to API RequestedCredentialModel list. + */ + private List toCredentialModels( + List domainCredentials) { + + if (domainCredentials == null) { + return null; + } + List result = new ArrayList<>(); + for (RequestedCredential cred : domainCredentials) { + RequestedCredentialModel model = new RequestedCredentialModel(); + model.setType(cred.getType()); + model.setPurpose(cred.getPurpose()); + model.setIssuer(cred.getIssuer()); + model.setClaims(cred.getClaims()); + result.add(model); + } + return result; + } + + private PresentationDefinitionResponse toResponse(PresentationDefinition definition) { + + PresentationDefinitionResponse response = new PresentationDefinitionResponse(); + response.setId(definition.getDefinitionId()); + response.setName(definition.getName()); + response.setDescription(definition.getDescription()); + response.setCredentials(toCredentialModels(definition.getRequestedCredentials())); + return response; + } + + private PresentationDefinitionListItem toListItem(PresentationDefinition definition) { + + PresentationDefinitionListItem item = new PresentationDefinitionListItem(); + item.setId(definition.getDefinitionId()); + item.setName(definition.getName()); + item.setDescription(definition.getDescription()); + return item; + } + + // --- Error handling --- + + private javax.ws.rs.WebApplicationException handleNotFound(String definitionId) { + + Error error = new Error(); + error.setCode(ErrorMessage.ERROR_CODE_DEFINITION_NOT_FOUND.getCode()); + error.setMessage(ErrorMessage.ERROR_CODE_DEFINITION_NOT_FOUND.getMessage()); + error.setDescription( + String.format(ErrorMessage.ERROR_CODE_DEFINITION_NOT_FOUND.getDescription(), + definitionId)); + return new javax.ws.rs.WebApplicationException( + Response.status(Response.Status.NOT_FOUND).entity(error).build()); + } + + private javax.ws.rs.WebApplicationException handleClientError( + ErrorMessage errorMessage, Exception e, Response.Status status, String... args) { + + Error error = new Error(); + error.setCode(errorMessage.getCode()); + error.setMessage(errorMessage.getMessage()); + error.setDescription(args.length > 0 + ? String.format(errorMessage.getDescription(), (Object[]) args) + : errorMessage.getDescription()); + return new javax.ws.rs.WebApplicationException( + Response.status(status).entity(error).build()); + } + + private javax.ws.rs.WebApplicationException handleServerError( + ErrorMessage errorMessage, Exception e, String... args) { + + Error error = new Error(); + error.setCode(errorMessage.getCode()); + error.setMessage(errorMessage.getMessage()); + error.setDescription(args.length > 0 + ? String.format(errorMessage.getDescription(), (Object[]) args) + : errorMessage.getDescription()); + return new javax.ws.rs.WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build()); + } + + // --- Utility --- + + private int getTenantId() { + + return ContextLoader.getTenantDomainFromContext() != null + ? org.wso2.carbon.context.PrivilegedCarbonContext + .getThreadLocalCarbonContext().getTenantId() + : -1234; // Super-tenant default + } + + private PresentationDefinitionService getService() { + + PresentationDefinitionService service = + VPDefinitionManagementServiceHolder.getPresentationDefinitionService(); + if (service == null) { + throw new javax.ws.rs.WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("PresentationDefinitionService is not available").build()); + } + return service; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/impl/PresentationDefinitionsApiServiceImpl.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/impl/PresentationDefinitionsApiServiceImpl.java new file mode 100644 index 0000000000..f785f7653d --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/impl/PresentationDefinitionsApiServiceImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1.impl; + +import org.wso2.carbon.identity.api.server.vp.template.management.common.VPDefinitionManagementConstants; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionCreationModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionResponse; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionUpdateModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionsApiService; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.core.ServerVPDefinitionManagementService; + +import java.net.URI; +import javax.ws.rs.core.Response; + +/** + * Implementation of PresentationDefinitionsApiService. + * Delegates all operations to ServerVPDefinitionManagementService and wraps in JAX-RS Response. + */ +public class PresentationDefinitionsApiServiceImpl extends PresentationDefinitionsApiService { + + private static final ServerVPDefinitionManagementService CORE_SERVICE = + new ServerVPDefinitionManagementService(); + + @Override + public Response listPresentationDefinitions() { + + return Response.ok().entity(CORE_SERVICE.listPresentationDefinitions()).build(); + } + + @Override + public Response createPresentationDefinition( + PresentationDefinitionCreationModel presentationDefinitionCreationModel) { + + PresentationDefinitionResponse created = + CORE_SERVICE.createPresentationDefinition(presentationDefinitionCreationModel); + URI location = URI.create( + VPDefinitionManagementConstants.VP_DEFINITION_MANAGEMENT_PATH_COMPONENT + + "/" + created.getId()); + return Response.created(location).entity(created).build(); + } + + @Override + public Response getPresentationDefinition(String definitionId) { + + return Response.ok().entity(CORE_SERVICE.getPresentationDefinition(definitionId)).build(); + } + + @Override + public Response updatePresentationDefinition(String definitionId, + PresentationDefinitionUpdateModel presentationDefinitionUpdateModel) { + + return Response.ok().entity( + CORE_SERVICE.updatePresentationDefinition(definitionId, + presentationDefinitionUpdateModel)).build(); + } + + @Override + public Response deletePresentationDefinition(String definitionId) { + + CORE_SERVICE.deletePresentationDefinition(definitionId); + return Response.noContent().build(); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml new file mode 100644 index 0000000000..c04a8e24b8 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml @@ -0,0 +1,357 @@ +openapi: 3.0.0 +info: + title: "WSO2 IS Presentation Definition Management API" + version: "v1" + description: "Admin API for managing presentation definitions in WSO2 Identity Server." + contact: + name: WSO2 + url: 'http://wso2.com/products/identity-server/' + email: architecture@wso2.org + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' + +servers: + - url: 'https://{server-url}/t/{tenant-domain}/api/server/v1' + variables: + server-url: + default: localhost:9443 + tenant-domain: + default: carbon.super + +security: + - OAuth2: [] + - BasicAuth: [] + +paths: + /vp/template: + get: + tags: + - Presentation Definitions + summary: List Presentation Definitions + description: | + Returns a list of all presentation definitions for the tenant. + operationId: listPresentationDefinitions + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionList' + '401': + description: Unauthorized + '403': + description: Forbidden + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + tags: + - Presentation Definitions + summary: Create a Presentation Definition + description: | + Creates a new presentation definition. + operationId: createPresentationDefinition + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionCreationModel' + required: true + responses: + '201': + description: Created + headers: + Location: + description: URI of the created resource. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '409': + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /vp/template/{definition-id}: + get: + tags: + - Presentation Definitions + summary: Get a Presentation Definition + description: | + Retrieves a presentation definition by its ID. + operationId: getPresentationDefinition + parameters: + - $ref: '#/components/parameters/definitionIdPathParam' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionResponse' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + tags: + - Presentation Definitions + summary: Update a Presentation Definition + description: | + Updates an existing presentation definition. + operationId: updatePresentationDefinition + parameters: + - $ref: '#/components/parameters/definitionIdPathParam' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionUpdateModel' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationDefinitionResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - Presentation Definitions + summary: Delete a Presentation Definition + description: | + Deletes a presentation definition by its ID. + operationId: deletePresentationDefinition + parameters: + - $ref: '#/components/parameters/definitionIdPathParam' + responses: + '204': + description: No Content + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +components: + parameters: + definitionIdPathParam: + name: definition-id + in: path + description: Unique identifier of the presentation definition. + required: true + schema: + type: string + + schemas: + PresentationDefinitionResponse: + type: object + required: + - id + - name + - credentials + properties: + id: + type: string + description: Unique identifier of the presentation definition. + example: "pd-001" + name: + type: string + description: Name of the presentation definition. + example: "Employee Credential Verification" + description: + type: string + description: Description of the presentation definition. + example: "Verifies employee credentials" + credentials: + type: array + description: The list of requested credentials in this presentation definition. + items: + $ref: '#/components/schemas/RequestedCredentialModel' + + RequestedCredentialModel: + type: object + required: + - type + properties: + type: + type: string + description: Type of the requested credential. + example: "EmployeeBadge" + purpose: + type: string + description: Purpose of requesting the credential. + example: "Please share your employee badge to prove employment." + issuer: + type: string + description: The trusted issuer for this credential. + claims: + type: array + description: List of claims requested from this credential. + items: + type: string + example: "email" + + PresentationDefinitionCreationModel: + type: object + required: + - name + - credentials + properties: + name: + type: string + description: Name of the presentation definition. + example: "Employee Credential Verification" + description: + type: string + description: Description of the presentation definition. + example: "Verifies employee credentials" + credentials: + type: array + description: The requested credentials to construct the definition. + items: + $ref: '#/components/schemas/RequestedCredentialModel' + + PresentationDefinitionUpdateModel: + type: object + properties: + name: + type: string + description: Name of the presentation definition. + example: "Updated Employee Credential Verification" + description: + type: string + description: Description of the presentation definition. + credentials: + type: array + description: The requested credentials to construct the definition. + items: + $ref: '#/components/schemas/RequestedCredentialModel' + + PresentationDefinitionList: + type: object + properties: + totalResults: + type: integer + description: Total number of presentation definitions. + example: 2 + presentationDefinitions: + type: array + items: + $ref: '#/components/schemas/PresentationDefinitionListItem' + + PresentationDefinitionListItem: + type: object + properties: + id: + type: string + description: Unique identifier of the presentation definition. + example: "pd-001" + name: + type: string + description: Name of the presentation definition. + example: "Employee Credential Verification" + description: + type: string + description: Description of the presentation definition. + + Error: + type: object + required: + - code + - message + properties: + code: + type: string + example: "VPD-60001" + message: + type: string + example: "Some error message" + description: + type: string + example: "Some error description" + traceId: + type: string + example: "e0fbcfeb-3617-43c4-8dd0-7b7d38e13047" + + securitySchemes: + BasicAuth: + type: http + scheme: basic + OAuth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: 'https://localhost:9443/oauth2/authorize' + tokenUrl: 'https://localhost:9443/oauth2/token' + scopes: {} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml new file mode 100644 index 0000000000..998f1b5f62 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml @@ -0,0 +1,37 @@ + + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + identity-api-server + 1.6.30-SNAPSHOT + ../../pom.xml + + + org.wso2.carbon.identity.api.server.vp.template.management + pom + + + org.wso2.carbon.identity.api.server.vp.template.management.v1 + org.wso2.carbon.identity.api.server.vp.template.management.common + + From ac82db48e0b880d2ec45211e6642d8d5ab33b3e4 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 18 Jun 2026 12:09:31 +0530 Subject: [PATCH 02/21] Add REST API module for VP token verification with structured presentation response --- .../pom.xml | 60 +++++ .../common/VPVerificationConstants.java | 59 +++++ .../common/VPVerificationServiceHolder.java | 44 ++++ .../pom.xml | 134 +++++++++++ .../api/server/vp/verification/v1/Error.java | 43 ++++ .../v1/VerificationInitiateRequest.java | 78 ++++++ .../v1/VerificationInitiateResponse.java | 55 +++++ .../v1/VerificationStatusResponse.java | 226 ++++++++++++++++++ .../vp/verification/v1/VpVerificationApi.java | 93 +++++++ .../v1/VpVerificationApiService.java | 31 +++ .../VpVerificationApiServiceFactory.java | 34 +++ .../v1/core/ServerVPVerificationService.java | 216 +++++++++++++++++ .../v1/impl/VpVerificationApiServiceImpl.java | 46 ++++ .../pom.xml | 37 +++ 14 files changed, 1156 insertions(+) create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/Error.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/factories/VpVerificationApiServiceFactory.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java create mode 100644 components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml new file mode 100644 index 0000000000..207c5ad467 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.verification + 1.6.30-SNAPSHOT + ../pom.xml + + + org.wso2.carbon.identity.api.server.vp.verification.common + jar + WSO2 Carbon - VP Verification API Common + + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.authenticator + ${carbon.identity.openid4vc.version} + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + 1.8 + 1.8 + + + + + diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java new file mode 100644 index 0000000000..3e826679de --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.common; + +/** + * Constants for the VP Verification REST API. + */ +public class VPVerificationConstants { + + public static final String VP_VERIFICATION_INITIATE_PATH = "/api/server/v1/vp/verification/initiate"; + public static final String VP_VERIFICATION_STATUS_PATH = "/api/server/v1/vp/verification/status"; + + private VPVerificationConstants() { + } + + /** + * Error messages for VP Verification API. + */ + public enum ErrorMessage { + + ERROR_CODE_INVALID_REQUEST("VPV-60001", "Invalid request."), + ERROR_CODE_SESSION_NOT_FOUND("VPV-60401", "Verification session not found."), + ERROR_CODE_INTERNAL_ERROR("VPV-65001", "Internal server error."), + ERROR_CODE_SERVICE_UNAVAILABLE("VPV-65002", + "StandaloneVerificationService is not available."); + + private final String code; + private final String message; + + ErrorMessage(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java new file mode 100644 index 0000000000..7a9f9b283c --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.common; + +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; + +/** + * Service holder for VP Verification API. + * Obtains the StandaloneVerificationService OSGi service via PrivilegedCarbonContext. + */ +public class VPVerificationServiceHolder { + + private VPVerificationServiceHolder() { + } + + /** + * Get StandaloneVerificationService OSGi service. + * + * @return StandaloneVerificationService instance. + */ + public static StandaloneVerificationService getStandaloneVerificationService() { + + return (StandaloneVerificationService) PrivilegedCarbonContext + .getThreadLocalCarbonContext() + .getOSGiService(StandaloneVerificationService.class, null); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml new file mode 100644 index 0000000000..d626ef4897 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml @@ -0,0 +1,134 @@ + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.verification + 1.6.30-SNAPSHOT + ../pom.xml + + + WSO2 Identity Server - VP Verification REST API + WSO2 IS - Standalone VP Verification API + org.wso2.carbon.identity.api.server.vp.verification.v1 + jar + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + provided + + + org.apache.cxf + cxf-rt-frontend-jaxrs + provided + + + javax.ws.rs + javax.ws.rs-api + provided + + + io.swagger + swagger-jaxrs + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + javax.ws.rs + jsr311-api + + + com.google.guava + guava + + + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.common + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.verification.common + ${project.version} + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.authenticator + ${carbon.identity.openid4vc.version} + provided + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.common + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + 1.8 + 1.8 + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + + diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/Error.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/Error.java new file mode 100644 index 0000000000..365df4b4eb --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/Error.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Standard error response model. + */ +public class Error { + + private String code; + private String message; + private String description; + + @JsonProperty("code") + public String getCode() { return code; } + public void setCode(String code) { this.code = code; } + + @JsonProperty("message") + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + + @JsonProperty("description") + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java new file mode 100644 index 0000000000..c3c6cc71d9 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Request body for initiating a VP verification session. + */ +@ApiModel(description = "Request body for initiating a VP verification session.") +public class VerificationInitiateRequest { + + private String presentationDefinitionId; + private String clientIdScheme; + private String responseMode; + private String registrationCert; + + @ApiModelProperty(required = true, + value = "ID of the presentation definition the verifier wants the wallet to satisfy.") + @JsonProperty("presentationDefinitionId") + public String getPresentationDefinitionId() { + return presentationDefinitionId; + } + + public void setPresentationDefinitionId(String presentationDefinitionId) { + this.presentationDefinitionId = presentationDefinitionId; + } + + @ApiModelProperty(value = "Client ID scheme: redirect_uri, x509_san_dns, or x509_hash. " + + "Defaults to x509_san_dns.") + @JsonProperty("clientIdScheme") + public String getClientIdScheme() { + return clientIdScheme; + } + + public void setClientIdScheme(String clientIdScheme) { + this.clientIdScheme = clientIdScheme; + } + + @ApiModelProperty(value = "Response mode: direct_post or direct_post.jwt. Defaults to direct_post.") + @JsonProperty("responseMode") + public String getResponseMode() { + return responseMode; + } + + public void setResponseMode(String responseMode) { + this.responseMode = responseMode; + } + + @ApiModelProperty(value = "PEM-encoded X.509 certificate to include in the x5c header of the request JWT. " + + "Required for x509_san_dns and x509_hash schemes.") + @JsonProperty("registrationCert") + public String getRegistrationCert() { + return registrationCert; + } + + public void setRegistrationCert(String registrationCert) { + this.registrationCert = registrationCert; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java new file mode 100644 index 0000000000..29f8c16ef7 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Response returned when a verification session is created. + */ +@ApiModel(description = "Response returned when a verification session is created.") +public class VerificationInitiateResponse { + + private String txnId; + private String walletUrl; + private String requestUri; + private Long expiresAt; + + @ApiModelProperty(value = "Transaction ID used to poll for the verification status.") + @JsonProperty("txnId") + public String getTxnId() { return txnId; } + public void setTxnId(String txnId) { this.txnId = txnId; } + + @ApiModelProperty(value = "Deep-link URL to launch the wallet (show as QR code).") + @JsonProperty("walletUrl") + public String getWalletUrl() { return walletUrl; } + public void setWalletUrl(String walletUrl) { this.walletUrl = walletUrl; } + + @ApiModelProperty(value = "URI from which the wallet will fetch the signed request JWT.") + @JsonProperty("requestUri") + public String getRequestUri() { return requestUri; } + public void setRequestUri(String requestUri) { this.requestUri = requestUri; } + + @ApiModelProperty(value = "Unix epoch milliseconds when the session expires.") + @JsonProperty("expiresAt") + public Long getExpiresAt() { return expiresAt; } + public void setExpiresAt(Long expiresAt) { this.expiresAt = expiresAt; } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java new file mode 100644 index 0000000000..8a826f4466 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.List; +import java.util.Map; + +/** + * Response for a VP verification session status poll. + */ +@ApiModel(description = "Response for a VP verification session status poll.") +public class VerificationStatusResponse { + + private String txnId; + private String status; + private Presentation presentation; + private List errors; + + @ApiModelProperty(value = "Transaction ID of the verification session.") + @JsonProperty("txnId") + public String getTxnId() { return txnId; } + public void setTxnId(String txnId) { this.txnId = txnId; } + + @ApiModelProperty(value = "Current status: ACTIVE, VERIFIED, FAILED.") + @JsonProperty("status") + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + @ApiModelProperty(value = "Full presentation details (present when status=VERIFIED).") + @JsonProperty("presentation") + @JsonInclude(JsonInclude.Include.NON_NULL) + public Presentation getPresentation() { return presentation; } + public void setPresentation(Presentation presentation) { this.presentation = presentation; } + + @ApiModelProperty(value = "Error descriptions when status=FAILED.") + @JsonProperty("errors") + public List getErrors() { return errors; } + public void setErrors(List errors) { this.errors = errors; } + + // ── Nested model classes ────────────────────────────────────────────────── + + /** + * Top-level presentation envelope — format, timing, credential details, holder, and key binding. + */ + public static class Presentation { + + private String format; + private String submittedAt; + private Credential credential; + private Holder holder; + private KeyBinding keyBinding; + + @JsonProperty("format") + public String getFormat() { return format; } + public void setFormat(String format) { this.format = format; } + + @ApiModelProperty(value = "ISO-8601 timestamp when IS received the wallet's VP response.") + @JsonProperty("submittedAt") + public String getSubmittedAt() { return submittedAt; } + public void setSubmittedAt(String submittedAt) { this.submittedAt = submittedAt; } + + @JsonProperty("credential") + public Credential getCredential() { return credential; } + public void setCredential(Credential credential) { this.credential = credential; } + + @JsonProperty("holder") + public Holder getHolder() { return holder; } + public void setHolder(Holder holder) { this.holder = holder; } + + @ApiModelProperty(value = "KB-JWT details — present only when the wallet included a Key Binding JWT.") + @JsonProperty("keyBinding") + @JsonInclude(JsonInclude.Include.NON_NULL) + public KeyBinding getKeyBinding() { return keyBinding; } + public void setKeyBinding(KeyBinding keyBinding) { this.keyBinding = keyBinding; } + } + + /** + * Issuer-signed credential metadata. + */ + public static class Credential { + + private String type; + private String issuer; + private String issuedAt; + private String expiresAt; + private String signingAlgorithm; + private HolderBinding holderBinding; + + @ApiModelProperty(value = "Credential type (vct claim).") + @JsonProperty("type") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + @ApiModelProperty(value = "Credential issuer (iss claim).") + @JsonProperty("issuer") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getIssuer() { return issuer; } + public void setIssuer(String issuer) { this.issuer = issuer; } + + @ApiModelProperty(value = "ISO-8601 timestamp when the credential was issued (iat claim).") + @JsonProperty("issuedAt") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getIssuedAt() { return issuedAt; } + public void setIssuedAt(String issuedAt) { this.issuedAt = issuedAt; } + + @ApiModelProperty(value = "ISO-8601 expiry of the credential (exp claim); null if no expiry set.") + @JsonProperty("expiresAt") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getExpiresAt() { return expiresAt; } + public void setExpiresAt(String expiresAt) { this.expiresAt = expiresAt; } + + @ApiModelProperty(value = "Signing algorithm used by the issuer (JWT header alg).") + @JsonProperty("signingAlgorithm") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getSigningAlgorithm() { return signingAlgorithm; } + public void setSigningAlgorithm(String signingAlgorithm) { this.signingAlgorithm = signingAlgorithm; } + + @ApiModelProperty(value = "Holder binding key info (cnf claim); null if no holder binding configured.") + @JsonProperty("holderBinding") + @JsonInclude(JsonInclude.Include.NON_NULL) + public HolderBinding getHolderBinding() { return holderBinding; } + public void setHolderBinding(HolderBinding holderBinding) { this.holderBinding = holderBinding; } + } + + /** + * Holder binding key info from the {@code cnf.jwk} claim. + */ + public static class HolderBinding { + + private String method; + private String keyType; + private String curve; + + @ApiModelProperty(value = "Binding method, e.g. 'cnf.jwk'.") + @JsonProperty("method") + public String getMethod() { return method; } + public void setMethod(String method) { this.method = method; } + + @ApiModelProperty(value = "Public key type: EC, RSA, or OKP.") + @JsonProperty("keyType") + public String getKeyType() { return keyType; } + public void setKeyType(String keyType) { this.keyType = keyType; } + + @ApiModelProperty(value = "Elliptic curve name (e.g. P-256, Ed25519); null for RSA.") + @JsonProperty("curve") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getCurve() { return curve; } + public void setCurve(String curve) { this.curve = curve; } + } + + /** + * Credential holder — subject identifier and disclosed attribute claims. + */ + public static class Holder { + + private String id; + private Map claims; + + @ApiModelProperty(value = "Subject identifier (sub claim).") + @JsonProperty("id") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + @ApiModelProperty(value = "Disclosed attribute claims (everything except JWT/SD-JWT technical fields).") + @JsonProperty("claims") + public Map getClaims() { return claims; } + public void setClaims(Map claims) { this.claims = claims; } + } + + /** + * Key Binding JWT details — proves the wallet held the private key at presentation time. + */ + public static class KeyBinding { + + private boolean verified; + private String presentedAt; + private String audience; + private String nonce; + + @ApiModelProperty(value = "True when the KB-JWT signature, iat, sd_hash, and nonce all passed verification.") + @JsonProperty("verified") + public boolean isVerified() { return verified; } + public void setVerified(boolean verified) { this.verified = verified; } + + @ApiModelProperty(value = "ISO-8601 timestamp from the KB-JWT iat — when the wallet created this presentation.") + @JsonProperty("presentedAt") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getPresentedAt() { return presentedAt; } + public void setPresentedAt(String presentedAt) { this.presentedAt = presentedAt; } + + @ApiModelProperty(value = "Audience claim from KB-JWT — the verifier this presentation was intended for.") + @JsonProperty("audience") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getAudience() { return audience; } + public void setAudience(String audience) { this.audience = audience; } + + @ApiModelProperty(value = "Nonce from KB-JWT — matches the nonce from the original VP request.") + @JsonProperty("nonce") + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getNonce() { return nonce; } + public void setNonce(String nonce) { this.nonce = nonce; } + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java new file mode 100644 index 0000000000..a35724c183 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.wso2.carbon.identity.api.server.vp.verification.v1.factories.VpVerificationApiServiceFactory; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * JAX-RS resource for standalone VP (Verifiable Presentation) verification. + * + *

Endpoints (public — no authentication required): + *

    + *
  • POST /vp/verification/initiate — start a new verification transaction
  • + *
  • GET /vp/verification/status/{txn_id} — poll for transaction result
  • + *
+ */ +@Path("/vp/verification") +@Api(value = "/vp/verification", description = "Standalone VP Verification API") +public class VpVerificationApi { + + private final VpVerificationApiService delegate = + VpVerificationApiServiceFactory.getVpVerificationApi(); + + @POST + @Path("/initiate") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Initiate a VP verification transaction", + notes = "Creates a new verification transaction and returns a wallet URL for QR code display.", + response = VerificationInitiateResponse.class) + @ApiResponses(value = { + @ApiResponse(code = 201, message = "Created", response = VerificationInitiateResponse.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response initiateVerification( + @ApiParam(value = "Verification initiation request", required = true) + VerificationInitiateRequest body) { + + return delegate.initiateVerification(body); + } + + @GET + @Path("/status/{txn_id}") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Get VP verification status", + notes = "Poll for the result of a verification transaction. " + + "Returns PENDING while waiting, COMPLETED with result_token on success, " + + "FAILED with error, or EXPIRED.", + response = VerificationStatusResponse.class) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK", response = VerificationStatusResponse.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response getVerificationStatus( + @ApiParam(value = "Transaction ID returned from initiation.", required = true) + @PathParam("txn_id") String txnId) { + + return delegate.getVerificationStatus(txnId); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java new file mode 100644 index 0000000000..03a6d80481 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1; + +import javax.ws.rs.core.Response; + +/** + * Abstract service for VP Verification API. + */ +public abstract class VpVerificationApiService { + + public abstract Response initiateVerification(VerificationInitiateRequest body); + + public abstract Response getVerificationStatus(String txnId); +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/factories/VpVerificationApiServiceFactory.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/factories/VpVerificationApiServiceFactory.java new file mode 100644 index 0000000000..8c71694d81 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/factories/VpVerificationApiServiceFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1.factories; + +import org.wso2.carbon.identity.api.server.vp.verification.v1.VpVerificationApiService; +import org.wso2.carbon.identity.api.server.vp.verification.v1.impl.VpVerificationApiServiceImpl; + +/** + * Factory class for VpVerificationApiService. + */ +public class VpVerificationApiServiceFactory { + + private static final VpVerificationApiService SERVICE = new VpVerificationApiServiceImpl(); + + public static VpVerificationApiService getVpVerificationApi() { + return SERVICE; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java new file mode 100644 index 0000000000..23c326edb7 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1.core; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.api.server.common.ContextLoader; +import org.wso2.carbon.identity.api.server.vp.verification.common.VPVerificationConstants; +import org.wso2.carbon.identity.api.server.vp.verification.common.VPVerificationConstants.ErrorMessage; +import org.wso2.carbon.identity.api.server.vp.verification.common.VPVerificationServiceHolder; +import org.wso2.carbon.identity.api.server.vp.verification.v1.Error; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationInitiateRequest; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationInitiateResponse; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Credential; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.HolderBinding; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Holder; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.KeyBinding; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Presentation; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.StandaloneVerificationSession; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestStatus; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.StandaloneVerificationInitiation; +import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; + +import java.net.URI; +import java.time.Instant; +import javax.ws.rs.core.Response; + +/** + * Core business logic for the VP Verification REST API. + * Delegates to StandaloneVerificationService (obtained via OSGi) and maps to REST response models. + */ +public class ServerVPVerificationService { + + private static final Log LOG = LogFactory.getLog(ServerVPVerificationService.class); + + /** + * Initiate a new standalone VP verification session. + * + * @param request Initiation request containing presentationDefinitionId. + * @return JAX-RS Response with VerificationInitiateResponse (201) or Error. + */ + public Response initiateVerification(VerificationInitiateRequest request) { + + if (request == null || StringUtils.isBlank(request.getPresentationDefinitionId())) { + return buildBadRequestResponse(ErrorMessage.ERROR_CODE_INVALID_REQUEST, + "presentationDefinitionId is required."); + } + + StandaloneVerificationService service = getService(); + if (service == null) { + return buildInternalErrorResponse(ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE, + ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE.getMessage()); + } + + String tenantDomain = ContextLoader.getTenantDomainFromContext(); + + try { + StandaloneVerificationInitiation initiation = + service.initiate(request.getPresentationDefinitionId(), tenantDomain, + request.getClientIdScheme(), request.getResponseMode(), + request.getRegistrationCert()); + + VerificationInitiateResponse resp = new VerificationInitiateResponse(); + resp.setTxnId(initiation.getTxnId()); + resp.setWalletUrl(initiation.getWalletUrl()); + resp.setRequestUri(initiation.getRequestUri()); + resp.setExpiresAt(initiation.getExpiresAt()); + + URI location = URI.create( + VPVerificationConstants.VP_VERIFICATION_STATUS_PATH + "/" + initiation.getTxnId()); + return Response.created(location).entity(resp).build(); + + } catch (VPAuthenticatorException e) { + LOG.error("Failed to initiate VP verification session.", e); + return buildInternalErrorResponse(ErrorMessage.ERROR_CODE_INTERNAL_ERROR, e.getMessage()); + } + } + + /** + * Poll the status of a verification session. + * + * @param txnId Transaction ID from initiation. + * @return JAX-RS Response with VerificationStatusResponse (200) or Error. + */ + public Response getVerificationStatus(String txnId) { + + if (StringUtils.isBlank(txnId)) { + return buildBadRequestResponse(ErrorMessage.ERROR_CODE_INVALID_REQUEST, + "txnId is required."); + } + + StandaloneVerificationService service = getService(); + if (service == null) { + return buildInternalErrorResponse(ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE, + ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE.getMessage()); + } + + StandaloneVerificationSession session = service.getSession(txnId); + if (session == null) { + return buildNotFoundResponse(txnId); + } + + VerificationStatusResponse resp = new VerificationStatusResponse(); + resp.setTxnId(txnId); + resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPRequestStatus.FAILED.name()); + + if (session.getVerificationResult() != null && session.getStatus() == VPRequestStatus.VERIFIED) { + PresentationMetadata meta = session.getVerificationResult().getMetadata(); + if (meta != null) { + resp.setPresentation(buildPresentation(meta)); + } + resp.setErrors(session.getVerificationResult().getErrors()); + } else if (session.getStatus() == VPRequestStatus.FAILED) { + if (session.getVerificationResult() != null) { + resp.setErrors(session.getVerificationResult().getErrors()); + } + } + + return Response.ok(resp).build(); + } + + private Presentation buildPresentation(PresentationMetadata meta) { + + Presentation p = new Presentation(); + p.setFormat(meta.getVpFormat()); + p.setSubmittedAt(Instant.ofEpochMilli(meta.getPresentationTime()).toString()); + + // ── Credential ──────────────────────────────────────────────────────── + Credential cred = new Credential(); + cred.setType(meta.getCredentialType()); + cred.setIssuer(meta.getIssuerDid()); + cred.setSigningAlgorithm(meta.getAlgorithm()); + if (meta.getIssuedAt() != null) { + cred.setIssuedAt(Instant.ofEpochMilli(meta.getIssuedAt()).toString()); + } + if (meta.getExpiresAt() != null) { + cred.setExpiresAt(Instant.ofEpochMilli(meta.getExpiresAt()).toString()); + } + if (StringUtils.isNotBlank(meta.getHolderBindingMethod())) { + HolderBinding hb = new HolderBinding(); + hb.setMethod(meta.getHolderBindingMethod()); + hb.setKeyType(meta.getHolderKeyType()); + hb.setCurve(meta.getHolderKeyCurve()); + cred.setHolderBinding(hb); + } + p.setCredential(cred); + + // ── Holder ──────────────────────────────────────────────────────────── + Holder holder = new Holder(); + holder.setId(meta.getHolderDid()); + holder.setClaims(meta.getCredentialClaims()); + p.setHolder(holder); + + // ── Key Binding ─────────────────────────────────────────────────────── + if (meta.isKbJwtVerified()) { + KeyBinding kb = new KeyBinding(); + kb.setVerified(true); + if (meta.getKbJwtPresentedAt() != null) { + kb.setPresentedAt(Instant.ofEpochMilli(meta.getKbJwtPresentedAt()).toString()); + } + kb.setAudience(meta.getKbJwtAudience()); + kb.setNonce(meta.getNonce()); + p.setKeyBinding(kb); + } + + return p; + } + + private StandaloneVerificationService getService() { + return VPVerificationServiceHolder.getStandaloneVerificationService(); + } + + private Response buildBadRequestResponse(ErrorMessage errorMsg, String description) { + Error error = new Error(); + error.setCode(errorMsg.getCode()); + error.setMessage(errorMsg.getMessage()); + error.setDescription(description); + return Response.status(Response.Status.BAD_REQUEST).entity(error).build(); + } + + private Response buildNotFoundResponse(String txnId) { + Error error = new Error(); + error.setCode(ErrorMessage.ERROR_CODE_SESSION_NOT_FOUND.getCode()); + error.setMessage(ErrorMessage.ERROR_CODE_SESSION_NOT_FOUND.getMessage()); + error.setDescription("No verification session found for txnId: " + txnId); + return Response.status(Response.Status.NOT_FOUND).entity(error).build(); + } + + private Response buildInternalErrorResponse(ErrorMessage errorMsg, String description) { + Error error = new Error(); + error.setCode(errorMsg.getCode()); + error.setMessage(errorMsg.getMessage()); + error.setDescription(description); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build(); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java new file mode 100644 index 0000000000..08caf95970 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.verification.v1.impl; + +import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationInitiateRequest; +import org.wso2.carbon.identity.api.server.vp.verification.v1.VpVerificationApiService; +import org.wso2.carbon.identity.api.server.vp.verification.v1.core.ServerVPVerificationService; + +import javax.ws.rs.core.Response; + +/** + * Thin wrapper implementation of VpVerificationApiService. + * Delegates all logic to ServerVPVerificationService. + */ +public class VpVerificationApiServiceImpl extends VpVerificationApiService { + + private static final ServerVPVerificationService CORE_SERVICE = new ServerVPVerificationService(); + + @Override + public Response initiateVerification(VerificationInitiateRequest body) { + + return CORE_SERVICE.initiateVerification(body); + } + + @Override + public Response getVerificationStatus(String txnId) { + + return CORE_SERVICE.getVerificationStatus(txnId); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml new file mode 100644 index 0000000000..9978d0bcb6 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml @@ -0,0 +1,37 @@ + + + + + 4.0.0 + + + org.wso2.carbon.identity.server.api + identity-api-server + 1.6.30-SNAPSHOT + ../../pom.xml + + + org.wso2.carbon.identity.api.server.vp.verification + pom + + + org.wso2.carbon.identity.api.server.vp.verification.common + org.wso2.carbon.identity.api.server.vp.verification.v1 + + From 20c02bed5d9e55de0ece5062c6e318cea124a10d Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 18 Jun 2026 12:09:37 +0530 Subject: [PATCH 03/21] Register VP API modules and update openid4vc dependency version in root pom --- pom.xml | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8a42a22e02..ef8de274c2 100644 --- a/pom.xml +++ b/pom.xml @@ -959,6 +959,48 @@ ${project.version} provided + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.template.management.v1 + ${project.version} + provided + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.template.management.common + ${project.version} + provided + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.verification.v1 + ${project.version} + provided + + + org.wso2.carbon.identity.server.api + org.wso2.carbon.identity.api.server.vp.verification.common + ${project.version} + provided + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.authenticator + ${carbon.identity.openid4vc.version} + provided + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.management + ${carbon.identity.openid4vc.version} + provided + + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.common + ${carbon.identity.openid4vc.version} + provided + org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.credential.management.common @@ -1158,7 +1200,7 @@ - 1.1.5 + 1.1.7-SNAPSHOT 6.9.10 @@ -1214,6 +1256,8 @@ components/org.wso2.carbon.identity.api.server.workflow components/org.wso2.carbon.identity.api.server.asynchronous.operation.status.management components/org.wso2.carbon.identity.api.server.vc.template.management + components/org.wso2.carbon.identity.api.server.vp.template.management + components/org.wso2.carbon.identity.api.server.vp.verification components/org.wso2.carbon.identity.api.server.credential.management components/org.wso2.carbon.identity.api.server.moesif.publisher From b5b2c18b0d5545b80a6dede16b0ae714220c182e Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Fri, 19 Jun 2026 20:45:03 +0530 Subject: [PATCH 04/21] Introduce config api for managing tenant level configs --- .../pom.xml | 5 + .../configs/common/ConfigsServiceHolder.java | 13 ++ .../api/server/configs/common/Constants.java | 15 ++- .../pom.xml | 5 + .../api/server/configs/v1/ConfigsApi.java | 50 ++++++++ .../server/configs/v1/ConfigsApiService.java | 5 + .../v1/model/OpenID4VPConfiguration.java | 121 ++++++++++++++++++ .../core/ServerConfigManagementService.java | 56 ++++++++ .../v1/impl/ConfigsApiServiceImpl.java | 14 ++ .../v1/constants/FlowEndpointConstants.java | 2 + .../handlers/AbstractMetaResponseHandler.java | 3 + .../handlers/RegistrationFlowMetaHandler.java | 2 + .../v1/VerificationInitiateRequest.java | 35 ----- .../v1/core/ServerVPVerificationService.java | 4 +- 14 files changed, 291 insertions(+), 39 deletions(-) create mode 100644 components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml index 8f30f388cf..63f608ef2a 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml @@ -97,6 +97,11 @@ org.wso2.carbon.identity.compatibility.settings.core provided + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.common + provided + diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java index 762adcdf83..96c278f86f 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java @@ -32,6 +32,7 @@ import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService; import org.wso2.carbon.identity.oauth2.impersonation.services.ImpersonationConfigMgtService; import org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.core.JWTClientAuthenticatorMgtService; +import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigService; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.idp.mgt.IdpManager; import org.wso2.carbon.logging.service.RemoteLoggingConfigService; @@ -290,4 +291,16 @@ public static FapiConfigMgtService getFapiConfigMgtService() { return FapiConfigMgtServiceHolder.SERVICE; } + + /** + * Get OpenID4VPConfigService osgi service. + * Returns null if the OID4VP feature is not enabled/deployed. + * + * @return OpenID4VPConfigService + */ + public static OpenID4VPConfigService getOpenID4VPConfigService() { + + return (OpenID4VPConfigService) PrivilegedCarbonContext + .getThreadLocalCarbonContext().getOSGiService(OpenID4VPConfigService.class, null); + } } diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/Constants.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/Constants.java index 6ac4fb2459..a0de23ce37 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/Constants.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/Constants.java @@ -298,7 +298,20 @@ public enum ErrorMessage { "Server encountered an error while retrieving the FAPI configuration."), ERROR_CODE_FAPI_CONFIG_UPDATE("65037", "Unable to update FAPI configuration.", - "Server encountered an error while updating the FAPI configuration."); + "Server encountered an error while updating the FAPI configuration."), + + /** + * OpenID4VP configuration errors. + */ + ERROR_CODE_OID4VP_NOT_ENABLED("65038", + "OpenID4VP feature is not enabled.", + "The OpenID4VP feature is not enabled in this deployment."), + ERROR_CODE_OID4VP_CONFIG_RETRIEVE("65039", + "Unable to retrieve OpenID4VP configuration.", + "Server encountered an error while retrieving the OpenID4VP configuration."), + ERROR_CODE_OID4VP_CONFIG_UPDATE("65040", + "Unable to update OpenID4VP configuration.", + "Server encountered an error while updating the OpenID4VP configuration."); private final String code; private final String message; diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml index adcffc4bd0..a2ce06454e 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml @@ -204,6 +204,11 @@ commons-beanutils commons-beanutils + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.common + provided + diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApi.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApi.java index 92ea7bda19..a7a3d1d9d1 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApi.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApi.java @@ -40,6 +40,7 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.FraudDetectionConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; +import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthSAML2Config; @@ -359,6 +360,55 @@ public Response getAgentConfiguration() { return delegate.getAgentConfiguration(); } + @Valid + @GET + @Path("/openid4vp") + + @Produces({ "application/json" }) + @ApiOperation(value = "Retrieve the tenant OpenID4VP configuration.", + notes = "Retrieve the tenant OpenID4VP configuration.", + response = OpenID4VPConfiguration.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = {}) + }, tags={ "OpenID4VP Configurations", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Successful Response", response = OpenID4VPConfiguration.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response getOpenID4VPConfiguration() { + + return delegate.getOpenID4VPConfiguration(); + } + + @Valid + @PUT + @Path("/openid4vp") + + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + @ApiOperation(value = "Update the tenant OpenID4VP configuration.", + notes = "Update the tenant OpenID4VP configuration.", + response = OpenID4VPConfiguration.class, authorizations = { + @Authorization(value = "BasicAuth"), + @Authorization(value = "OAuth2", scopes = {}) + }, tags={ "OpenID4VP Configurations", }) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Successful Response", response = OpenID4VPConfiguration.class), + @ApiResponse(code = 400, message = "Bad Request", response = Error.class), + @ApiResponse(code = 401, message = "Unauthorized", response = Void.class), + @ApiResponse(code = 403, message = "Forbidden", response = Void.class), + @ApiResponse(code = 500, message = "Server Error", response = Error.class) + }) + public Response updateOpenID4VPConfiguration( + @ApiParam(value = "", required = true) @Valid OpenID4VPConfiguration openID4VPConfiguration) { + + return delegate.updateOpenID4VPConfiguration(openID4VPConfiguration); + } + @Valid @GET @Path("/provisioning/inbound/scim") diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApiService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApiService.java index 8900d09ee8..5096142997 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApiService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/ConfigsApiService.java @@ -26,6 +26,7 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.CompatibilitySettings; import org.wso2.carbon.identity.api.server.configs.v1.model.CORSPatch; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; +import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthSAML2Config; @@ -64,6 +65,10 @@ public interface ConfigsApiService { public Response getAgentConfiguration(); + public Response getOpenID4VPConfiguration(); + + public Response updateOpenID4VPConfiguration(OpenID4VPConfiguration openID4VPConfiguration); + public Response getInboundScimConfigs(); public Response getIssuerUsageScopeConfig(); diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java new file mode 100644 index 0000000000..0c56c22d54 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.configs.v1.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Objects; + +import javax.validation.Valid; + +/** + * OpenID4VP tenant-level configuration. + */ +public class OpenID4VPConfiguration { + + private String clientIdScheme; + private String responseMode; + private String registrationCertificate; + + public OpenID4VPConfiguration clientIdScheme(String clientIdScheme) { + + this.clientIdScheme = clientIdScheme; + return this; + } + + @ApiModelProperty(example = "x509_san_dns", + value = "The client_id_scheme used when building the VP request JWT.") + @JsonProperty("clientIdScheme") + @Valid + public String getClientIdScheme() { + return clientIdScheme; + } + + public void setClientIdScheme(String clientIdScheme) { + this.clientIdScheme = clientIdScheme; + } + + public OpenID4VPConfiguration responseMode(String responseMode) { + + this.responseMode = responseMode; + return this; + } + + @ApiModelProperty(example = "direct_post", + value = "The response_mode used in the VP request (direct_post or direct_post.jwt).") + @JsonProperty("responseMode") + @Valid + public String getResponseMode() { + return responseMode; + } + + public void setResponseMode(String responseMode) { + this.responseMode = responseMode; + } + + public OpenID4VPConfiguration registrationCertificate(String registrationCertificate) { + + this.registrationCertificate = registrationCertificate; + return this; + } + + @ApiModelProperty(value = "Optional verifier_attestation JWT for wallet registration.") + @JsonProperty("registrationCertificate") + @Valid + public String getRegistrationCertificate() { + return registrationCertificate; + } + + public void setRegistrationCertificate(String registrationCertificate) { + this.registrationCertificate = registrationCertificate; + } + + @Override + public boolean equals(java.lang.Object o) { + + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OpenID4VPConfiguration that = (OpenID4VPConfiguration) o; + return Objects.equals(this.clientIdScheme, that.clientIdScheme) + && Objects.equals(this.responseMode, that.responseMode) + && Objects.equals(this.registrationCertificate, that.registrationCertificate); + } + + @Override + public int hashCode() { + return Objects.hash(clientIdScheme, responseMode, registrationCertificate); + } + + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + sb.append("class OpenID4VPConfiguration {\n"); + sb.append(" clientIdScheme: ").append(clientIdScheme).append("\n"); + sb.append(" responseMode: ").append(responseMode).append("\n"); + sb.append(" registrationCertificate: ").append("[REDACTED]").append("\n"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java index 6e6e6337fd..93ee51d427 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java @@ -62,6 +62,10 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.FraudDetectionConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; +import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; +import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigMgtException; +import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigService; +import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPTenantConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthSAML2Config; @@ -663,6 +667,58 @@ public FapiConfig updateFAPIConfiguration(FapiConfig fapiConfig) { } } + /** + * Retrieves the OpenID4VP configuration for the current tenant domain. + * + * @return OpenID4VPConfiguration the current OID4VP configuration. + */ + public OpenID4VPConfiguration getOpenID4VPConfiguration() { + + String tenantDomain = ContextLoader.getTenantDomainFromContext(); + OpenID4VPConfigService configService = ConfigsServiceHolder.getOpenID4VPConfigService(); + if (configService == null) { + throw handleException(Response.Status.NOT_IMPLEMENTED, + Constants.ErrorMessage.ERROR_CODE_OID4VP_NOT_ENABLED, null); + } + try { + OpenID4VPTenantConfig cfg = configService.getConfig(tenantDomain); + return new OpenID4VPConfiguration() + .clientIdScheme(cfg.getClientIdScheme()) + .responseMode(cfg.getResponseMode()) + .registrationCertificate(cfg.getRegistrationCertificate()); + } catch (OpenID4VPConfigMgtException e) { + throw handleException(Response.Status.INTERNAL_SERVER_ERROR, + Constants.ErrorMessage.ERROR_CODE_OID4VP_CONFIG_RETRIEVE, null); + } + } + + /** + * Updates the OpenID4VP configuration for the current tenant domain. + * + * @param config the new OID4VP configuration. + * @return the updated OpenID4VPConfiguration. + */ + public OpenID4VPConfiguration updateOpenID4VPConfiguration(OpenID4VPConfiguration config) { + + String tenantDomain = ContextLoader.getTenantDomainFromContext(); + OpenID4VPConfigService configService = ConfigsServiceHolder.getOpenID4VPConfigService(); + if (configService == null) { + throw handleException(Response.Status.NOT_IMPLEMENTED, + Constants.ErrorMessage.ERROR_CODE_OID4VP_NOT_ENABLED, null); + } + try { + OpenID4VPTenantConfig tenantConfig = new OpenID4VPTenantConfig(); + tenantConfig.setClientIdScheme(config.getClientIdScheme()); + tenantConfig.setResponseMode(config.getResponseMode()); + tenantConfig.setRegistrationCertificate(config.getRegistrationCertificate()); + configService.setConfig(tenantConfig, tenantDomain); + return config; + } catch (OpenID4VPConfigMgtException e) { + throw handleException(Response.Status.INTERNAL_SERVER_ERROR, + Constants.ErrorMessage.ERROR_CODE_OID4VP_CONFIG_UPDATE, null); + } + } + /** * Get the CORS config for a tenant. */ diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/impl/ConfigsApiServiceImpl.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/impl/ConfigsApiServiceImpl.java index ad63502c10..2c80438c75 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/impl/ConfigsApiServiceImpl.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/impl/ConfigsApiServiceImpl.java @@ -29,6 +29,7 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.FapiConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.FraudDetectionConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; +import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthSAML2Config; @@ -96,6 +97,19 @@ public Response getAgentConfiguration() { return Response.ok().entity(configManagementService.getAgentConfiguration()).build(); } + @Override + public Response getOpenID4VPConfiguration() { + + return Response.ok().entity(configManagementService.getOpenID4VPConfiguration()).build(); + } + + @Override + public Response updateOpenID4VPConfiguration(OpenID4VPConfiguration openID4VPConfiguration) { + + return Response.ok().entity(configManagementService.updateOpenID4VPConfiguration(openID4VPConfiguration)) + .build(); + } + @Override public Response getInboundScimConfigs() { diff --git a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/constants/FlowEndpointConstants.java b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/constants/FlowEndpointConstants.java index 92cd34cf00..4c65c64158 100644 --- a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/constants/FlowEndpointConstants.java +++ b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/constants/FlowEndpointConstants.java @@ -172,6 +172,7 @@ public static class Executors { public static final String CONFIRMATION_CODE_VALIDATION_EXECUTOR = "ConfirmationCodeValidationExecutor"; public static final String USER_PROVISIONING_EXECUTOR = "UserProvisioningExecutor"; public static final String FLOW_EXTENSION_EXECUTOR = "FlowExtensionExecutor"; + public static final String OPENID4VP_REGISTRATION_EXECUTOR = "OpenID4VPRegistrationExecutor"; } /** @@ -185,6 +186,7 @@ public static class Authenticators { public static final String FACEBOOK_AUTHENTICATOR = "FacebookAuthenticator"; public static final String OFFICE365_AUTHENTICATOR = "Office365Authenticator"; public static final String APPLE_AUTHENTICATOR = "AppleOIDCAuthenticator"; + public static final String OPENID4VP_AUTHENTICATOR = "OpenID4VPAuthenticator"; } /** diff --git a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/AbstractMetaResponseHandler.java b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/AbstractMetaResponseHandler.java index 17d868c119..967e8bb769 100644 --- a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/AbstractMetaResponseHandler.java +++ b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/AbstractMetaResponseHandler.java @@ -56,6 +56,7 @@ import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.PROP_READ_ONLY; import static org.wso2.carbon.identity.api.server.claim.management.common.Constant.PROP_REQUIRED; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.ErrorMessages.ERROR_CODE_GET_IDENTITY_PROVIDERS; +import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Authenticators.OPENID4VP_AUTHENTICATOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.APPLE_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.EMAIL_OTP_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.FACEBOOK_EXECUTOR; @@ -65,6 +66,7 @@ import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.MAGIC_LINK_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OFFICE365_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OPENID_CONNECT_EXECUTOR; +import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OPENID4VP_REGISTRATION_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.PASSWORD_PROVISIONING_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.SMS_OTP_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.USER_PROVISIONING_EXECUTOR; @@ -337,6 +339,7 @@ private static Map getConnectionExecutorMap() { connectionExecutorMap.put(FlowEndpointConstants.Authenticators.APPLE_AUTHENTICATOR, APPLE_EXECUTOR); connectionExecutorMap.put(FlowEndpointConstants.Authenticators.OPENID_CONNECT_AUTHENTICATOR, OPENID_CONNECT_EXECUTOR); + connectionExecutorMap.put(OPENID4VP_AUTHENTICATOR, OPENID4VP_REGISTRATION_EXECUTOR); return connectionExecutorMap; } diff --git a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/RegistrationFlowMetaHandler.java b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/RegistrationFlowMetaHandler.java index dfa6618963..e39f546474 100644 --- a/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/RegistrationFlowMetaHandler.java +++ b/components/org.wso2.carbon.identity.api.server.flow.management/org.wso2.carbon.identity.api.server.flow.management.v1/src/main/java/org/wso2/carbon/identity/api/server/flow/management/v1/response/handlers/RegistrationFlowMetaHandler.java @@ -34,6 +34,7 @@ import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.GOOGLE_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OFFICE365_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OPENID_CONNECT_EXECUTOR; +import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.Executors.OPENID4VP_REGISTRATION_EXECUTOR; import static org.wso2.carbon.identity.api.server.flow.management.v1.constants.FlowEndpointConstants.SELF_REGISTRATION_ATTRIBUTE_PROFILE; /** @@ -86,6 +87,7 @@ public List getSupportedExecutors() { supportedExecutors.add(APPLE_EXECUTOR); supportedExecutors.add(GITHUB_EXECUTOR); supportedExecutors.add(FIDO2_EXECUTOR); + supportedExecutors.add(OPENID4VP_REGISTRATION_EXECUTOR); return supportedExecutors; } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java index c3c6cc71d9..25c0b6ea7e 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateRequest.java @@ -29,9 +29,6 @@ public class VerificationInitiateRequest { private String presentationDefinitionId; - private String clientIdScheme; - private String responseMode; - private String registrationCert; @ApiModelProperty(required = true, value = "ID of the presentation definition the verifier wants the wallet to satisfy.") @@ -43,36 +40,4 @@ public String getPresentationDefinitionId() { public void setPresentationDefinitionId(String presentationDefinitionId) { this.presentationDefinitionId = presentationDefinitionId; } - - @ApiModelProperty(value = "Client ID scheme: redirect_uri, x509_san_dns, or x509_hash. " - + "Defaults to x509_san_dns.") - @JsonProperty("clientIdScheme") - public String getClientIdScheme() { - return clientIdScheme; - } - - public void setClientIdScheme(String clientIdScheme) { - this.clientIdScheme = clientIdScheme; - } - - @ApiModelProperty(value = "Response mode: direct_post or direct_post.jwt. Defaults to direct_post.") - @JsonProperty("responseMode") - public String getResponseMode() { - return responseMode; - } - - public void setResponseMode(String responseMode) { - this.responseMode = responseMode; - } - - @ApiModelProperty(value = "PEM-encoded X.509 certificate to include in the x5c header of the request JWT. " - + "Required for x509_san_dns and x509_hash schemes.") - @JsonProperty("registrationCert") - public String getRegistrationCert() { - return registrationCert; - } - - public void setRegistrationCert(String registrationCert) { - this.registrationCert = registrationCert; - } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 23c326edb7..69fcfa1c9a 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -76,9 +76,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { try { StandaloneVerificationInitiation initiation = - service.initiate(request.getPresentationDefinitionId(), tenantDomain, - request.getClientIdScheme(), request.getResponseMode(), - request.getRegistrationCert()); + service.initiate(request.getPresentationDefinitionId(), tenantDomain); VerificationInitiateResponse resp = new VerificationInitiateResponse(); resp.setTxnId(initiation.getTxnId()); From 7b2939d0e98c4b5704345cb4ed59b27b9933a36d Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Tue, 23 Jun 2026 14:56:19 +0530 Subject: [PATCH 05/21] Implement trust issuer validation --- .../v1/model/OpenID4VPConfiguration.java | 24 ++++++++++++++++++- .../core/ServerConfigManagementService.java | 9 +++++-- .../v1/RequestedCredentialModel.java | 22 +++++++++++++++++ .../ServerVPDefinitionManagementService.java | 4 ++++ .../resources/PresentationDefinitions.yaml | 12 +++++++++- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java index 0c56c22d54..4583a8bcb7 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java @@ -31,6 +31,7 @@ public class OpenID4VPConfiguration { private String clientIdScheme; + private String clientId; private String responseMode; private String registrationCertificate; @@ -52,6 +53,25 @@ public void setClientIdScheme(String clientIdScheme) { this.clientIdScheme = clientIdScheme; } + public OpenID4VPConfiguration clientId(String clientId) { + + this.clientId = clientId; + return this; + } + + @ApiModelProperty(example = "x509_san_dns:myserver.example.com", + value = "Optional override for the client_id sent in VP requests. " + + "Leave blank to auto-derive from the client ID scheme.") + @JsonProperty("clientId") + @Valid + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + public OpenID4VPConfiguration responseMode(String responseMode) { this.responseMode = responseMode; @@ -98,13 +118,14 @@ public boolean equals(java.lang.Object o) { } OpenID4VPConfiguration that = (OpenID4VPConfiguration) o; return Objects.equals(this.clientIdScheme, that.clientIdScheme) + && Objects.equals(this.clientId, that.clientId) && Objects.equals(this.responseMode, that.responseMode) && Objects.equals(this.registrationCertificate, that.registrationCertificate); } @Override public int hashCode() { - return Objects.hash(clientIdScheme, responseMode, registrationCertificate); + return Objects.hash(clientIdScheme, clientId, responseMode, registrationCertificate); } @Override @@ -113,6 +134,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class OpenID4VPConfiguration {\n"); sb.append(" clientIdScheme: ").append(clientIdScheme).append("\n"); + sb.append(" clientId: ").append(clientId).append("\n"); sb.append(" responseMode: ").append(responseMode).append("\n"); sb.append(" registrationCertificate: ").append("[REDACTED]").append("\n"); sb.append("}"); diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java index 93ee51d427..6d0a5e3b28 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java @@ -66,6 +66,7 @@ import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigMgtException; import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigService; import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPTenantConfig; +import org.wso2.carbon.identity.openid4vc.presentation.common.constant.OpenID4VPConstants; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthSAML2Config; @@ -683,8 +684,11 @@ public OpenID4VPConfiguration getOpenID4VPConfiguration() { try { OpenID4VPTenantConfig cfg = configService.getConfig(tenantDomain); return new OpenID4VPConfiguration() - .clientIdScheme(cfg.getClientIdScheme()) - .responseMode(cfg.getResponseMode()) + .clientIdScheme(StringUtils.defaultIfBlank( + cfg.getClientIdScheme(), OpenID4VPConstants.Defaults.CLIENT_ID_SCHEME)) + .clientId(cfg.getClientId()) + .responseMode(StringUtils.defaultIfBlank( + cfg.getResponseMode(), OpenID4VPConstants.Defaults.RESPONSE_MODE)) .registrationCertificate(cfg.getRegistrationCertificate()); } catch (OpenID4VPConfigMgtException e) { throw handleException(Response.Status.INTERNAL_SERVER_ERROR, @@ -709,6 +713,7 @@ public OpenID4VPConfiguration updateOpenID4VPConfiguration(OpenID4VPConfiguratio try { OpenID4VPTenantConfig tenantConfig = new OpenID4VPTenantConfig(); tenantConfig.setClientIdScheme(config.getClientIdScheme()); + tenantConfig.setClientId(config.getClientId()); tenantConfig.setResponseMode(config.getResponseMode()); tenantConfig.setRegistrationCertificate(config.getRegistrationCertificate()); configService.setConfig(tenantConfig, tenantDomain); diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java index 7471d589e6..7827aa8d98 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java @@ -18,6 +18,8 @@ public class RequestedCredentialModel { private String purpose; private String issuer; private List claims; + private Boolean enforceTrustedIssuers; + private List trustedIssuers; @ApiModelProperty(required = true, value = "Type of the requested credential.") @JsonProperty("type") @@ -58,4 +60,24 @@ public List getClaims() { public void setClaims(List claims) { this.claims = claims; } + + @ApiModelProperty(value = "Whether to enforce trusted issuer validation for this credential.") + @JsonProperty("enforceTrustedIssuers") + public Boolean getEnforceTrustedIssuers() { + return enforceTrustedIssuers; + } + + public void setEnforceTrustedIssuers(Boolean enforceTrustedIssuers) { + this.enforceTrustedIssuers = enforceTrustedIssuers; + } + + @ApiModelProperty(value = "List of trusted issuer URIs/DIDs for this credential.") + @JsonProperty("trustedIssuers") + public List getTrustedIssuers() { + return trustedIssuers; + } + + public void setTrustedIssuers(List trustedIssuers) { + this.trustedIssuers = trustedIssuers; + } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java index e40e477906..a9fc04885a 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java @@ -208,6 +208,8 @@ private List toRequestedCredentials( cred.setPurpose(apiModel.getPurpose()); cred.setIssuer(apiModel.getIssuer()); cred.setClaims(apiModel.getClaims()); + cred.setEnforceTrustedIssuers(Boolean.TRUE.equals(apiModel.getEnforceTrustedIssuers())); + cred.setTrustedIssuers(apiModel.getTrustedIssuers()); result.add(cred); } return result; @@ -229,6 +231,8 @@ private List toCredentialModels( model.setPurpose(cred.getPurpose()); model.setIssuer(cred.getIssuer()); model.setClaims(cred.getClaims()); + model.setEnforceTrustedIssuers(cred.isEnforceTrustedIssuers()); + model.setTrustedIssuers(cred.getTrustedIssuers()); result.add(model); } return result; diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml index c04a8e24b8..321c710297 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml @@ -254,13 +254,23 @@ components: example: "Please share your employee badge to prove employment." issuer: type: string - description: The trusted issuer for this credential. + description: The trusted issuer for this credential (legacy single-issuer field). claims: type: array description: List of claims requested from this credential. items: type: string example: "email" + enforceTrustedIssuers: + type: boolean + description: When true, the credential's issuer is validated against the trustedIssuers list. + example: true + trustedIssuers: + type: array + description: List of trusted issuer URIs or DIDs. Enforced only when enforceTrustedIssuers is true. + items: + type: string + example: "https://issuer.example.com" PresentationDefinitionCreationModel: type: object From 74057422c8d504988b225e9a75e2809f588331a1 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Tue, 23 Jun 2026 21:49:53 +0530 Subject: [PATCH 06/21] Add status claim support --- .../v1/model/OpenID4VPConfiguration.java | 18 +++++ .../core/ServerConfigManagementService.java | 6 +- .../management/v1/ClaimConstraintModel.java | 68 +++++++++++++++++++ .../v1/RequestedCredentialModel.java | 8 +-- .../ServerVPDefinitionManagementService.java | 38 ++++++++++- 5 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/ClaimConstraintModel.java diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java index 4583a8bcb7..e4609d8aff 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/OpenID4VPConfiguration.java @@ -34,6 +34,7 @@ public class OpenID4VPConfiguration { private String clientId; private String responseMode; private String registrationCertificate; + private Boolean rejectVcWithoutStatusClaim; public OpenID4VPConfiguration clientIdScheme(String clientIdScheme) { @@ -107,6 +108,23 @@ public void setRegistrationCertificate(String registrationCertificate) { this.registrationCertificate = registrationCertificate; } + public OpenID4VPConfiguration rejectVcWithoutStatusClaim(Boolean rejectVcWithoutStatusClaim) { + + this.rejectVcWithoutStatusClaim = rejectVcWithoutStatusClaim; + return this; + } + + @ApiModelProperty(value = "When true, VCs that do not carry a status claim are rejected during verification.") + @JsonProperty("rejectVcWithoutStatusClaim") + @Valid + public Boolean getRejectVcWithoutStatusClaim() { + return rejectVcWithoutStatusClaim; + } + + public void setRejectVcWithoutStatusClaim(Boolean rejectVcWithoutStatusClaim) { + this.rejectVcWithoutStatusClaim = rejectVcWithoutStatusClaim; + } + @Override public boolean equals(java.lang.Object o) { diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java index 6d0a5e3b28..451d7aae53 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java @@ -689,7 +689,10 @@ public OpenID4VPConfiguration getOpenID4VPConfiguration() { .clientId(cfg.getClientId()) .responseMode(StringUtils.defaultIfBlank( cfg.getResponseMode(), OpenID4VPConstants.Defaults.RESPONSE_MODE)) - .registrationCertificate(cfg.getRegistrationCertificate()); + .registrationCertificate(cfg.getRegistrationCertificate()) + .rejectVcWithoutStatusClaim( + cfg.getRejectVcWithoutStatusClaim() != null + ? cfg.getRejectVcWithoutStatusClaim() : Boolean.FALSE); } catch (OpenID4VPConfigMgtException e) { throw handleException(Response.Status.INTERNAL_SERVER_ERROR, Constants.ErrorMessage.ERROR_CODE_OID4VP_CONFIG_RETRIEVE, null); @@ -716,6 +719,7 @@ public OpenID4VPConfiguration updateOpenID4VPConfiguration(OpenID4VPConfiguratio tenantConfig.setClientId(config.getClientId()); tenantConfig.setResponseMode(config.getResponseMode()); tenantConfig.setRegistrationCertificate(config.getRegistrationCertificate()); + tenantConfig.setRejectVcWithoutStatusClaim(config.getRejectVcWithoutStatusClaim()); configService.setConfig(tenantConfig, tenantDomain); return config; } catch (OpenID4VPConfigMgtException e) { diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/ClaimConstraintModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/ClaimConstraintModel.java new file mode 100644 index 0000000000..7e564a9580 --- /dev/null +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/ClaimConstraintModel.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.identity.api.server.vp.template.management.v1; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * Claim Constraint Model — represents a single claim requirement within a requested credential. + */ +@ApiModel(description = "Claim Constraint Model") +public class ClaimConstraintModel { + + @NotNull + private String name; + private Boolean mandatory = Boolean.TRUE; + private List allowedValues; + + @ApiModelProperty(required = true, value = "The claim name (e.g. given_name).") + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ApiModelProperty(value = "Whether this claim is mandatory. Defaults to true.") + @JsonProperty("mandatory") + public Boolean getMandatory() { + return mandatory; + } + + public void setMandatory(Boolean mandatory) { + this.mandatory = mandatory; + } + + @ApiModelProperty(value = "Allowed values for this claim. When set, the credential value must be one of these.") + @JsonProperty("allowedValues") + public List getAllowedValues() { + return allowedValues; + } + + public void setAllowedValues(List allowedValues) { + this.allowedValues = allowedValues; + } +} diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java index 7827aa8d98..f540aaaa6e 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java @@ -17,7 +17,7 @@ public class RequestedCredentialModel { private String type; private String purpose; private String issuer; - private List claims; + private List claims; private Boolean enforceTrustedIssuers; private List trustedIssuers; @@ -51,13 +51,13 @@ public void setIssuer(String issuer) { this.issuer = issuer; } - @ApiModelProperty(value = "List of claims requested from this credential.") + @ApiModelProperty(value = "List of claim constraints for this credential.") @JsonProperty("claims") - public List getClaims() { + public List getClaims() { return claims; } - public void setClaims(List claims) { + public void setClaims(List claims) { this.claims = claims; } diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java index a9fc04885a..42d1fff35f 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java @@ -27,10 +27,12 @@ import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionListItem; import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionResponse; import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionUpdateModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.ClaimConstraintModel; import org.wso2.carbon.identity.api.server.vp.template.management.v1.RequestedCredentialModel; import org.wso2.carbon.identity.openid4vc.presentation.common.exception.VPException; import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationDefinitionNotFoundException; import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition; +import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition.ClaimConstraint; import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition.RequestedCredential; import org.wso2.carbon.identity.openid4vc.presentation.management.service.PresentationDefinitionService; @@ -207,7 +209,7 @@ private List toRequestedCredentials( cred.setType(apiModel.getType()); cred.setPurpose(apiModel.getPurpose()); cred.setIssuer(apiModel.getIssuer()); - cred.setClaims(apiModel.getClaims()); + cred.setClaims(toClaimConstraints(apiModel.getClaims())); cred.setEnforceTrustedIssuers(Boolean.TRUE.equals(apiModel.getEnforceTrustedIssuers())); cred.setTrustedIssuers(apiModel.getTrustedIssuers()); result.add(cred); @@ -230,7 +232,7 @@ private List toCredentialModels( model.setType(cred.getType()); model.setPurpose(cred.getPurpose()); model.setIssuer(cred.getIssuer()); - model.setClaims(cred.getClaims()); + model.setClaims(toClaimConstraintModels(cred.getClaims())); model.setEnforceTrustedIssuers(cred.isEnforceTrustedIssuers()); model.setTrustedIssuers(cred.getTrustedIssuers()); result.add(model); @@ -238,6 +240,38 @@ private List toCredentialModels( return result; } + private List toClaimConstraints(List apiModels) { + + if (apiModels == null) { + return null; + } + List result = new ArrayList<>(); + for (ClaimConstraintModel cm : apiModels) { + ClaimConstraint cc = new ClaimConstraint(); + cc.setName(cm.getName()); + cc.setMandatory(Boolean.TRUE.equals(cm.getMandatory() == null ? Boolean.TRUE : cm.getMandatory())); + cc.setAllowedValues(cm.getAllowedValues()); + result.add(cc); + } + return result; + } + + private List toClaimConstraintModels(List domainConstraints) { + + if (domainConstraints == null) { + return null; + } + List result = new ArrayList<>(); + for (ClaimConstraint cc : domainConstraints) { + ClaimConstraintModel cm = new ClaimConstraintModel(); + cm.setName(cc.getName()); + cm.setMandatory(cc.isMandatory()); + cm.setAllowedValues(cc.getAllowedValues()); + result.add(cm); + } + return result; + } + private PresentationDefinitionResponse toResponse(PresentationDefinition definition) { PresentationDefinitionResponse response = new PresentationDefinitionResponse(); From 367aee0c98ad454eb03089dab2188f5854ddc9f8 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Sun, 5 Jul 2026 13:28:47 +0530 Subject: [PATCH 07/21] Update OpenID4VPConfigService references and add OpenID4VP config REST API --- .../pom.xml | 2 +- .../configs/common/ConfigsServiceHolder.java | 2 +- .../pom.xml | 5 + .../core/ServerConfigManagementService.java | 9 +- .../src/main/resources/configs.yaml | 130 ++++++++++++++++++ 5 files changed, 141 insertions(+), 7 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml index 63f608ef2a..fdf869f189 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/pom.xml @@ -99,7 +99,7 @@ org.wso2.carbon.identity.openid4vc - org.wso2.carbon.identity.openid4vc.presentation.common + org.wso2.carbon.identity.openid4vc.presentation.authenticator provided diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java index 96c278f86f..fc7a6c6cce 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.common/src/main/java/org/wso2/carbon/identity/api/server/configs/common/ConfigsServiceHolder.java @@ -32,7 +32,7 @@ import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService; import org.wso2.carbon.identity.oauth2.impersonation.services.ImpersonationConfigMgtService; import org.wso2.carbon.identity.oauth2.token.handler.clientauth.jwt.core.JWTClientAuthenticatorMgtService; -import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigService; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.OpenID4VPConfigService; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.idp.mgt.IdpManager; import org.wso2.carbon.logging.service.RemoteLoggingConfigService; diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml index a2ce06454e..e5306c7117 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/pom.xml @@ -209,6 +209,11 @@ org.wso2.carbon.identity.openid4vc.presentation.common provided + + org.wso2.carbon.identity.openid4vc + org.wso2.carbon.identity.openid4vc.presentation.authenticator + provided + diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java index 451d7aae53..d1023218df 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java @@ -63,9 +63,8 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; -import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigMgtException; -import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPConfigService; -import org.wso2.carbon.identity.openid4vc.presentation.common.config.OpenID4VPTenantConfig; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.OpenID4VPConfigMgtException; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.OpenID4VPConfigService; import org.wso2.carbon.identity.openid4vc.presentation.common.constant.OpenID4VPConstants; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthPassiveSTSConfig; @@ -682,7 +681,7 @@ public OpenID4VPConfiguration getOpenID4VPConfiguration() { Constants.ErrorMessage.ERROR_CODE_OID4VP_NOT_ENABLED, null); } try { - OpenID4VPTenantConfig cfg = configService.getConfig(tenantDomain); + OpenID4VPConfigService.TenantConfig cfg = configService.getConfig(tenantDomain); return new OpenID4VPConfiguration() .clientIdScheme(StringUtils.defaultIfBlank( cfg.getClientIdScheme(), OpenID4VPConstants.Defaults.CLIENT_ID_SCHEME)) @@ -714,7 +713,7 @@ public OpenID4VPConfiguration updateOpenID4VPConfiguration(OpenID4VPConfiguratio Constants.ErrorMessage.ERROR_CODE_OID4VP_NOT_ENABLED, null); } try { - OpenID4VPTenantConfig tenantConfig = new OpenID4VPTenantConfig(); + OpenID4VPConfigService.TenantConfig tenantConfig = new OpenID4VPConfigService.TenantConfig(); tenantConfig.setClientIdScheme(config.getClientIdScheme()); tenantConfig.setClientId(config.getClientId()); tenantConfig.setResponseMode(config.getResponseMode()); diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml index aa778f5b02..f0183755d5 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml @@ -1399,6 +1399,95 @@ paths: schema: $ref: '#/components/schemas/FraudDetectionConfig' required: true + /configs/openid4vp: + get: + tags: + - OpenID4VP Configurations + summary: Retrieve the tenant OpenID4VP configuration. + operationId: getOpenID4VPConfiguration + description: Retrieve the OpenID for Verifiable Presentations (OpenID4VP) configuration of the tenant. + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OpenID4VPConfiguration' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '501': + description: Not Implemented + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + put: + tags: + - OpenID4VP Configurations + summary: Update the tenant OpenID4VP configuration. + operationId: updateOpenID4VPConfiguration + description: Update the OpenID for Verifiable Presentations (OpenID4VP) configuration of the tenant. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/OpenID4VPConfiguration' + required: true + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/OpenID4VPConfiguration' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '501': + description: Not Implemented + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /configs/authentication/inbound/oauth2/issuer/usage-scope: get: tags: @@ -2583,6 +2672,47 @@ components: conflictOnClaimUniquenessViolation: true oauth: enableLegacyGrantTypeValidation: false + OpenID4VPConfiguration: + type: object + description: Tenant-level OpenID for Verifiable Presentations (OpenID4VP) configuration. + properties: + clientIdScheme: + type: string + description: > + The client_id_scheme used when building the VP request JWT. + Determines how the verifier identifies itself to the wallet. + enum: + - redirect_uri + - x509_san_dns + - x509_hash + example: x509_san_dns + clientId: + type: string + description: > + Optional override for the client_id sent in VP requests. + Leave blank to auto-derive from the selected client ID scheme. + example: x509_san_dns:myserver.example.com + responseMode: + type: string + description: > + The response_mode used in the VP request. + Use direct_post.jwt for encrypted responses or direct_post for plain JSON. + enum: + - direct_post + - direct_post.jwt + example: direct_post + registrationCertificate: + type: string + description: > + Optional verifier attestation JWT (rc-wrp+jwt) issued by a trust anchor. + Used for wallet registration. Leave blank if not applicable. + example: eyJ0eXAiOiJyYy13cnArand0IiwieDVjIjpbIi4uLiJdfQ.eyJzdWIiOiJleGFtcGxlIn0.signature + rejectVcWithoutStatusClaim: + type: boolean + description: > + When true, credentials that do not include a status claim are rejected + during verification. Disable to allow credentials without revocation support. + example: false CompatibilitySettingsGroup: type: object description: Compatibility settings for a specific group. From 037d3c9a0d65c70a0551809d70962d7075a7f1c3 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Sun, 5 Jul 2026 13:28:53 +0530 Subject: [PATCH 08/21] Add JWKS URI support, improve exception handling, and add feature-disabled check in VP definition API --- .../VPDefinitionManagementServiceHolder.java | 10 ++- .../v1/RequestedCredentialModel.java | 22 +++++++ .../ServerVPDefinitionManagementService.java | 66 +++++++++++++------ .../resources/PresentationDefinitions.yaml | 13 ++++ 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java index fc84bd576b..1881a8a0ca 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/common/VPDefinitionManagementServiceHolder.java @@ -37,8 +37,12 @@ private VPDefinitionManagementServiceHolder() { */ public static PresentationDefinitionService getPresentationDefinitionService() { - return (PresentationDefinitionService) PrivilegedCarbonContext - .getThreadLocalCarbonContext() - .getOSGiService(PresentationDefinitionService.class, null); + try { + return (PresentationDefinitionService) PrivilegedCarbonContext + .getThreadLocalCarbonContext() + .getOSGiService(PresentationDefinitionService.class, null); + } catch (NullPointerException e) { + return null; + } } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java index f540aaaa6e..6c9cedc42b 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java @@ -17,6 +17,8 @@ public class RequestedCredentialModel { private String type; private String purpose; private String issuer; + private String issuerCertPem; + private String jwksUri; private List claims; private Boolean enforceTrustedIssuers; private List trustedIssuers; @@ -51,6 +53,26 @@ public void setIssuer(String issuer) { this.issuer = issuer; } + @ApiModelProperty(value = "PEM-encoded X.509 certificate of the credential issuer.") + @JsonProperty("issuerCertPem") + public String getIssuerCertPem() { + return issuerCertPem; + } + + public void setIssuerCertPem(String issuerCertPem) { + this.issuerCertPem = issuerCertPem; + } + + @ApiModelProperty(value = "JWKS endpoint URL of the credential issuer.") + @JsonProperty("jwksUri") + public String getJwksUri() { + return jwksUri; + } + + public void setJwksUri(String jwksUri) { + this.jwksUri = jwksUri; + } + @ApiModelProperty(value = "List of claim constraints for this credential.") @JsonProperty("claims") public List getClaims() { diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java index 42d1fff35f..3c8572a819 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.api.server.vp.template.management.v1.core; import org.wso2.carbon.identity.api.server.common.ContextLoader; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.api.server.vp.template.management.common.VPDefinitionManagementConstants.ErrorMessage; import org.wso2.carbon.identity.api.server.vp.template.management.common.VPDefinitionManagementServiceHolder; import org.wso2.carbon.identity.api.server.vp.template.management.v1.Error; @@ -29,8 +30,9 @@ import org.wso2.carbon.identity.api.server.vp.template.management.v1.PresentationDefinitionUpdateModel; import org.wso2.carbon.identity.api.server.vp.template.management.v1.ClaimConstraintModel; import org.wso2.carbon.identity.api.server.vp.template.management.v1.RequestedCredentialModel; -import org.wso2.carbon.identity.openid4vc.presentation.common.exception.VPException; -import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationDefinitionNotFoundException; +import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationManagementClientException; +import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationManagementErrorCode; +import org.wso2.carbon.identity.openid4vc.presentation.management.exception.PresentationManagementException; import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition; import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition.ClaimConstraint; import org.wso2.carbon.identity.openid4vc.presentation.management.model.PresentationDefinition.RequestedCredential; @@ -68,7 +70,7 @@ public PresentationDefinitionList listPresentationDefinitions() { .map(this::toListItem) .collect(Collectors.toList())); return listResponse; - } catch (VPException e) { + } catch (PresentationManagementException e) { throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_LISTING_DEFINITIONS, e); } } @@ -98,17 +100,18 @@ public PresentationDefinitionResponse createPresentationDefinition( PresentationDefinition created = service.createPresentationDefinition(definition, tenantId); return toResponse(created); - } catch (VPException e) { - if (e.getMessage() != null && e.getMessage().contains("already exists")) { + } catch (PresentationManagementClientException e) { + if (PresentationManagementErrorCode.DEFINITION_ALREADY_EXISTS == e.getErrorCode()) { throw handleClientError(ErrorMessage.ERROR_CODE_DEFINITION_ALREADY_EXISTS, e, Response.Status.CONFLICT); } - if (e.getMessage() != null && (e.getMessage().contains("Invalid") || - e.getMessage().contains("required"))) { + if (PresentationManagementErrorCode.VALIDATION_ERROR == e.getErrorCode()) { throw handleClientError(ErrorMessage.ERROR_CODE_INVALID_INPUT, e, Response.Status.BAD_REQUEST, e.getMessage()); } throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_CREATING_DEFINITION, e); + } catch (PresentationManagementException e) { + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_CREATING_DEFINITION, e); } } @@ -127,9 +130,12 @@ public PresentationDefinitionResponse getPresentationDefinition(String definitio PresentationDefinition definition = service.getPresentationDefinitionById( definitionId, tenantId); return toResponse(definition); - } catch (PresentationDefinitionNotFoundException e) { - throw handleNotFound(definitionId); - } catch (VPException e) { + } catch (PresentationManagementClientException e) { + if (PresentationManagementErrorCode.DEFINITION_NOT_FOUND == e.getErrorCode()) { + throw handleNotFound(definitionId); + } + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_RETRIEVING_DEFINITION, e, definitionId); + } catch (PresentationManagementException e) { throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_RETRIEVING_DEFINITION, e, definitionId); } @@ -164,9 +170,12 @@ public PresentationDefinitionResponse updatePresentationDefinition( PresentationDefinition updated = service.updatePresentationDefinition( definition, tenantId); return toResponse(updated); - } catch (PresentationDefinitionNotFoundException e) { - throw handleNotFound(definitionId); - } catch (VPException e) { + } catch (PresentationManagementClientException e) { + if (PresentationManagementErrorCode.DEFINITION_NOT_FOUND == e.getErrorCode()) { + throw handleNotFound(definitionId); + } + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_UPDATING_DEFINITION, e, definitionId); + } catch (PresentationManagementException e) { throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_UPDATING_DEFINITION, e, definitionId); } @@ -184,9 +193,12 @@ public void deletePresentationDefinition(String definitionId) { PresentationDefinitionService service = getService(); service.deletePresentationDefinition(definitionId, tenantId); - } catch (PresentationDefinitionNotFoundException e) { - throw handleNotFound(definitionId); - } catch (VPException e) { + } catch (PresentationManagementClientException e) { + if (PresentationManagementErrorCode.DEFINITION_NOT_FOUND == e.getErrorCode()) { + throw handleNotFound(definitionId); + } + throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_DELETING_DEFINITION, e, definitionId); + } catch (PresentationManagementException e) { throw handleServerError(ErrorMessage.ERROR_CODE_ERROR_DELETING_DEFINITION, e, definitionId); } @@ -209,6 +221,8 @@ private List toRequestedCredentials( cred.setType(apiModel.getType()); cred.setPurpose(apiModel.getPurpose()); cred.setIssuer(apiModel.getIssuer()); + cred.setIssuerCertPem(apiModel.getIssuerCertPem()); + cred.setJwksUri(apiModel.getJwksUri()); cred.setClaims(toClaimConstraints(apiModel.getClaims())); cred.setEnforceTrustedIssuers(Boolean.TRUE.equals(apiModel.getEnforceTrustedIssuers())); cred.setTrustedIssuers(apiModel.getTrustedIssuers()); @@ -232,6 +246,8 @@ private List toCredentialModels( model.setType(cred.getType()); model.setPurpose(cred.getPurpose()); model.setIssuer(cred.getIssuer()); + model.setIssuerCertPem(cred.getIssuerCertPem()); + model.setJwksUri(cred.getJwksUri()); model.setClaims(toClaimConstraintModels(cred.getClaims())); model.setEnforceTrustedIssuers(cred.isEnforceTrustedIssuers()); model.setTrustedIssuers(cred.getTrustedIssuers()); @@ -343,13 +359,25 @@ private int getTenantId() { private PresentationDefinitionService getService() { + if (!Boolean.parseBoolean(IdentityUtil.getProperty("OpenID4VP.Enabled"))) { + throw buildFeatureDisabledError(); + } PresentationDefinitionService service = VPDefinitionManagementServiceHolder.getPresentationDefinitionService(); if (service == null) { - throw new javax.ws.rs.WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("PresentationDefinitionService is not available").build()); + throw buildFeatureDisabledError(); } return service; } + + private javax.ws.rs.WebApplicationException buildFeatureDisabledError() { + + Error error = new Error(); + error.setCode("VPD-60004"); + error.setMessage("OpenID4VP feature is not enabled."); + error.setDescription( + "The OpenID4VP feature is disabled. Enable it via [openid4vp] enabled=true in deployment.toml."); + return new javax.ws.rs.WebApplicationException( + Response.status(Response.Status.NOT_IMPLEMENTED).entity(error).build()); + } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml index 321c710297..0b4f50c344 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/resources/PresentationDefinitions.yaml @@ -261,6 +261,19 @@ components: items: type: string example: "email" + issuerCertPem: + type: string + description: > + PEM-encoded X.509 certificate of the credential issuer. When provided, the issuer's + signature is verified directly against this certificate without any network discovery. + example: "-----BEGIN CERTIFICATE-----\nMIIB....\n-----END CERTIFICATE-----" + jwksUri: + type: string + description: > + JWKS endpoint URL of the credential issuer. When provided and no issuerCertPem is set, + the verifier fetches the issuer's public keys from this URI instead of performing + automatic well-known discovery. + example: "https://issuer.example.com/jwks" enforceTrustedIssuers: type: boolean description: When true, the credential's issuer is validated against the trustedIssuers list. From 4a0d210242ccd34189de3f8b1db9755e5bec1a7e Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Sun, 5 Jul 2026 13:28:57 +0530 Subject: [PATCH 09/21] Return 501 Not Implemented when OpenID4VP feature is disabled --- .../v1/core/ServerVPVerificationService.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 69fcfa1c9a..62c38e9105 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -68,8 +68,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { StandaloneVerificationService service = getService(); if (service == null) { - return buildInternalErrorResponse(ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE, - ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE.getMessage()); + return buildNotImplementedResponse(); } String tenantDomain = ContextLoader.getTenantDomainFromContext(); @@ -109,8 +108,7 @@ public Response getVerificationStatus(String txnId) { StandaloneVerificationService service = getService(); if (service == null) { - return buildInternalErrorResponse(ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE, - ErrorMessage.ERROR_CODE_SERVICE_UNAVAILABLE.getMessage()); + return buildNotImplementedResponse(); } StandaloneVerificationSession session = service.getSession(txnId); @@ -211,4 +209,13 @@ private Response buildInternalErrorResponse(ErrorMessage errorMsg, String descri error.setDescription(description); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build(); } + + private Response buildNotImplementedResponse() { + Error error = new Error(); + error.setCode("OID4VP-60001"); + error.setMessage("OpenID4VP feature is not enabled."); + error.setDescription( + "The OpenID4VP feature is disabled. Enable it via [openid4vp] enabled=true in deployment.toml."); + return Response.status(Response.Status.NOT_IMPLEMENTED).entity(error).build(); + } } From 6afaded1faed84daadf758143dc32cb3e9fcbada Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Sun, 5 Jul 2026 13:30:40 +0530 Subject: [PATCH 10/21] Rename VP abbreviations to Verifiable Presentation in configs API descriptions --- .../src/main/resources/configs.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml index f0183755d5..7c99459b59 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/resources/configs.yaml @@ -2679,7 +2679,7 @@ components: clientIdScheme: type: string description: > - The client_id_scheme used when building the VP request JWT. + The client_id_scheme used when building the Verifiable Presentation request JWT. Determines how the verifier identifies itself to the wallet. enum: - redirect_uri @@ -2689,13 +2689,13 @@ components: clientId: type: string description: > - Optional override for the client_id sent in VP requests. + Optional override for the client_id sent in Verifiable Presentation requests. Leave blank to auto-derive from the selected client ID scheme. example: x509_san_dns:myserver.example.com responseMode: type: string description: > - The response_mode used in the VP request. + The response_mode used in the Verifiable Presentation request. Use direct_post.jwt for encrypted responses or direct_post for plain JSON. enum: - direct_post From c35488f6fc2577ba8ef017637bde436b19333e96 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Tue, 7 Jul 2026 21:43:18 +0530 Subject: [PATCH 11/21] Move verified claims from holder to per-credential in verification status response --- .../v1/VerificationStatusResponse.java | 26 +++-- .../v1/core/ServerVPVerificationService.java | 108 ++++++++++-------- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java index 8a826f4466..6f3e0d865b 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java @@ -55,6 +55,7 @@ public class VerificationStatusResponse { @ApiModelProperty(value = "Error descriptions when status=FAILED.") @JsonProperty("errors") + @JsonInclude(JsonInclude.Include.NON_EMPTY) public List getErrors() { return errors; } public void setErrors(List errors) { this.errors = errors; } @@ -67,7 +68,7 @@ public static class Presentation { private String format; private String submittedAt; - private Credential credential; + private List credentials; private Holder holder; private KeyBinding keyBinding; @@ -80,11 +81,13 @@ public static class Presentation { public String getSubmittedAt() { return submittedAt; } public void setSubmittedAt(String submittedAt) { this.submittedAt = submittedAt; } - @JsonProperty("credential") - public Credential getCredential() { return credential; } - public void setCredential(Credential credential) { this.credential = credential; } + @JsonProperty("credentials") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public List getCredentials() { return credentials; } + public void setCredentials(List credentials) { this.credentials = credentials; } @JsonProperty("holder") + @JsonInclude(JsonInclude.Include.NON_NULL) public Holder getHolder() { return holder; } public void setHolder(Holder holder) { this.holder = holder; } @@ -106,6 +109,7 @@ public static class Credential { private String expiresAt; private String signingAlgorithm; private HolderBinding holderBinding; + private Map claims; @ApiModelProperty(value = "Credential type (vct claim).") @JsonProperty("type") @@ -142,6 +146,12 @@ public static class Credential { @JsonInclude(JsonInclude.Include.NON_NULL) public HolderBinding getHolderBinding() { return holderBinding; } public void setHolderBinding(HolderBinding holderBinding) { this.holderBinding = holderBinding; } + + @ApiModelProperty(value = "Disclosed attribute claims from this credential (technical JWT fields excluded).") + @JsonProperty("claims") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public Map getClaims() { return claims; } + public void setClaims(Map claims) { this.claims = claims; } } /** @@ -171,23 +181,17 @@ public static class HolderBinding { } /** - * Credential holder — subject identifier and disclosed attribute claims. + * Credential holder — subject identifier from the sub claim. */ public static class Holder { private String id; - private Map claims; @ApiModelProperty(value = "Subject identifier (sub claim).") @JsonProperty("id") @JsonInclude(JsonInclude.Include.NON_NULL) public String getId() { return id; } public void setId(String id) { this.id = id; } - - @ApiModelProperty(value = "Disclosed attribute claims (everything except JWT/SD-JWT technical fields).") - @JsonProperty("claims") - public Map getClaims() { return claims; } - public void setClaims(Map claims) { this.claims = claims; } } /** diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 62c38e9105..b33786d965 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -35,14 +35,16 @@ import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.KeyBinding; import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Presentation; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.StandaloneVerificationSession; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSession; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestStatus; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.StandaloneVerificationInitiation; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSessionInitiation; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; +import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.VerificationResult; import java.net.URI; import java.time.Instant; +import java.util.List; import javax.ws.rs.core.Response; /** @@ -74,7 +76,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { String tenantDomain = ContextLoader.getTenantDomainFromContext(); try { - StandaloneVerificationInitiation initiation = + VPSessionInitiation initiation = service.initiate(request.getPresentationDefinitionId(), tenantDomain); VerificationInitiateResponse resp = new VerificationInitiateResponse(); @@ -111,7 +113,7 @@ public Response getVerificationStatus(String txnId) { return buildNotImplementedResponse(); } - StandaloneVerificationSession session = service.getSession(txnId); + VPSession session = service.getSession(txnId); if (session == null) { return buildNotFoundResponse(txnId); } @@ -121,10 +123,7 @@ public Response getVerificationStatus(String txnId) { resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPRequestStatus.FAILED.name()); if (session.getVerificationResult() != null && session.getStatus() == VPRequestStatus.VERIFIED) { - PresentationMetadata meta = session.getVerificationResult().getMetadata(); - if (meta != null) { - resp.setPresentation(buildPresentation(meta)); - } + resp.setPresentation(buildPresentation(session.getVerificationResult())); resp.setErrors(session.getVerificationResult().getErrors()); } else if (session.getStatus() == VPRequestStatus.FAILED) { if (session.getVerificationResult() != null) { @@ -135,48 +134,67 @@ public Response getVerificationStatus(String txnId) { return Response.ok(resp).build(); } - private Presentation buildPresentation(PresentationMetadata meta) { + private Presentation buildPresentation(VerificationResult result) { + + List metadataList = result.getCredentialMetadataList(); + PresentationMetadata firstMeta = (metadataList != null && !metadataList.isEmpty()) + ? metadataList.get(0) : null; Presentation p = new Presentation(); - p.setFormat(meta.getVpFormat()); - p.setSubmittedAt(Instant.ofEpochMilli(meta.getPresentationTime()).toString()); - - // ── Credential ──────────────────────────────────────────────────────── - Credential cred = new Credential(); - cred.setType(meta.getCredentialType()); - cred.setIssuer(meta.getIssuerDid()); - cred.setSigningAlgorithm(meta.getAlgorithm()); - if (meta.getIssuedAt() != null) { - cred.setIssuedAt(Instant.ofEpochMilli(meta.getIssuedAt()).toString()); - } - if (meta.getExpiresAt() != null) { - cred.setExpiresAt(Instant.ofEpochMilli(meta.getExpiresAt()).toString()); + p.setFormat(firstMeta != null ? firstMeta.getVpFormat() : null); + p.setSubmittedAt(firstMeta != null + ? Instant.ofEpochMilli(firstMeta.getPresentationTime()).toString() : null); + + // ── Credentials — one entry per verified credential ─────────────────── + List credentials = new java.util.ArrayList<>(); + if (metadataList != null) { + for (PresentationMetadata meta : metadataList) { + Credential cred = new Credential(); + cred.setType(meta.getCredentialType()); + cred.setIssuer(meta.getIssuerDid()); + cred.setSigningAlgorithm(meta.getAlgorithm()); + if (meta.getIssuedAt() != null) { + cred.setIssuedAt(Instant.ofEpochMilli(meta.getIssuedAt()).toString()); + } + if (meta.getExpiresAt() != null) { + cred.setExpiresAt(Instant.ofEpochMilli(meta.getExpiresAt()).toString()); + } + if (StringUtils.isNotBlank(meta.getHolderBindingMethod())) { + HolderBinding hb = new HolderBinding(); + hb.setMethod(meta.getHolderBindingMethod()); + hb.setKeyType(meta.getHolderKeyType()); + hb.setCurve(meta.getHolderKeyCurve()); + cred.setHolderBinding(hb); + } + cred.setClaims(meta.getCredentialClaims()); + credentials.add(cred); + } } - if (StringUtils.isNotBlank(meta.getHolderBindingMethod())) { - HolderBinding hb = new HolderBinding(); - hb.setMethod(meta.getHolderBindingMethod()); - hb.setKeyType(meta.getHolderKeyType()); - hb.setCurve(meta.getHolderKeyCurve()); - cred.setHolderBinding(hb); + p.setCredentials(credentials); + + // ── Holder — only emitted when a sub claim was present in the credential ─ + if (firstMeta != null && StringUtils.isNotBlank(firstMeta.getHolderDid())) { + Holder holder = new Holder(); + holder.setId(firstMeta.getHolderDid()); + p.setHolder(holder); } - p.setCredential(cred); - - // ── Holder ──────────────────────────────────────────────────────────── - Holder holder = new Holder(); - holder.setId(meta.getHolderDid()); - holder.setClaims(meta.getCredentialClaims()); - p.setHolder(holder); - - // ── Key Binding ─────────────────────────────────────────────────────── - if (meta.isKbJwtVerified()) { - KeyBinding kb = new KeyBinding(); - kb.setVerified(true); - if (meta.getKbJwtPresentedAt() != null) { - kb.setPresentedAt(Instant.ofEpochMilli(meta.getKbJwtPresentedAt()).toString()); + + // ── Key Binding — use first credential that carried a verified KB-JWT ─ + // All KB-JWTs in a VP share the same nonce and audience, so any one is representative. + if (metadataList != null) { + for (PresentationMetadata meta : metadataList) { + if (meta.isKbJwtVerified()) { + KeyBinding kb = new KeyBinding(); + kb.setVerified(true); + if (meta.getKbJwtPresentedAt() != null) { + kb.setPresentedAt(Instant.ofEpochMilli(meta.getKbJwtPresentedAt()).toString()); + } + kb.setAudience(meta.getKbJwtAudience()); + kb.setNonce(meta.getNonce()); + p.setKeyBinding(kb); + break; + } } - kb.setAudience(meta.getKbJwtAudience()); - kb.setNonce(meta.getNonce()); - p.setKeyBinding(kb); } return p; From eb5668c52247865ebc5e060d9d122fd92bbd3f55 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Tue, 7 Jul 2026 21:54:56 +0530 Subject: [PATCH 12/21] Fix inline java.util.ArrayList import in ServerVPVerificationService --- .../vp/verification/v1/core/ServerVPVerificationService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index b33786d965..b27200b881 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -44,6 +44,7 @@ import java.net.URI; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import javax.ws.rs.core.Response; @@ -146,7 +147,7 @@ private Presentation buildPresentation(VerificationResult result) { ? Instant.ofEpochMilli(firstMeta.getPresentationTime()).toString() : null); // ── Credentials — one entry per verified credential ─────────────────── - List credentials = new java.util.ArrayList<>(); + List credentials = new ArrayList<>(); if (metadataList != null) { for (PresentationMetadata meta : metadataList) { Credential cred = new Credential(); From 13d47151a9bd810f8f882f76819ca3b80989a4e3 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 14:24:21 +0530 Subject: [PATCH 13/21] Rename VPSessionInitiation to VPRequestHandle --- .../vp/verification/v1/core/ServerVPVerificationService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index b27200b881..f7bbe18f3d 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -38,7 +38,7 @@ import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSession; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestStatus; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSessionInitiation; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestHandle; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.VerificationResult; @@ -77,7 +77,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { String tenantDomain = ContextLoader.getTenantDomainFromContext(); try { - VPSessionInitiation initiation = + VPRequestHandle initiation = service.initiate(request.getPresentationDefinitionId(), tenantDomain); VerificationInitiateResponse resp = new VerificationInitiateResponse(); From b5e097e82ccf8bbbae8e872c83fc2d22b86b3f8d Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 14:25:55 +0530 Subject: [PATCH 14/21] Rename VPRequestHandle to VPFlowInitiationResult --- .../vp/verification/v1/core/ServerVPVerificationService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index f7bbe18f3d..838d08a355 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -38,7 +38,7 @@ import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSession; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestStatus; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestHandle; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowInitiationResult; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.VerificationResult; @@ -77,7 +77,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { String tenantDomain = ContextLoader.getTenantDomainFromContext(); try { - VPRequestHandle initiation = + VPFlowInitiationResult initiation = service.initiate(request.getPresentationDefinitionId(), tenantDomain); VerificationInitiateResponse resp = new VerificationInitiateResponse(); From dfb6bd59224ccc8b2fa9d6599450b9d509455fe9 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 14:35:15 +0530 Subject: [PATCH 15/21] Rename VPRequestStatus to VPFlowStatus --- .../verification/v1/core/ServerVPVerificationService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 838d08a355..aff0a33a2b 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -36,7 +36,7 @@ import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Presentation; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSession; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPRequestStatus; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowStatus; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowInitiationResult; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; @@ -121,12 +121,12 @@ public Response getVerificationStatus(String txnId) { VerificationStatusResponse resp = new VerificationStatusResponse(); resp.setTxnId(txnId); - resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPRequestStatus.FAILED.name()); + resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPFlowStatus.FAILED.name()); - if (session.getVerificationResult() != null && session.getStatus() == VPRequestStatus.VERIFIED) { + if (session.getVerificationResult() != null && session.getStatus() == VPFlowStatus.VERIFIED) { resp.setPresentation(buildPresentation(session.getVerificationResult())); resp.setErrors(session.getVerificationResult().getErrors()); - } else if (session.getStatus() == VPRequestStatus.FAILED) { + } else if (session.getStatus() == VPFlowStatus.FAILED) { if (session.getVerificationResult() != null) { resp.setErrors(session.getVerificationResult().getErrors()); } From 5dacc87cd85497a444cbccbbf58094fa298b9353 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 14:37:48 +0530 Subject: [PATCH 16/21] Rename VPSession to VPFlowSession and VPSubmission to VPAuthorizationResponse --- .../vp/verification/v1/core/ServerVPVerificationService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index aff0a33a2b..2e96ff2350 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -35,7 +35,7 @@ import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.KeyBinding; import org.wso2.carbon.identity.api.server.vp.verification.v1.VerificationStatusResponse.Presentation; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPSession; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowSession; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowStatus; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowInitiationResult; @@ -114,7 +114,7 @@ public Response getVerificationStatus(String txnId) { return buildNotImplementedResponse(); } - VPSession session = service.getSession(txnId); + VPFlowSession session = service.getSession(txnId); if (session == null) { return buildNotFoundResponse(txnId); } From f2eaa7e3a8fdfa86b8cc3d11c00177e5e1c378a6 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 16:12:10 +0530 Subject: [PATCH 17/21] Update VP service reference from VPVerificationService to VPFlowService VPVerificationService and VPRegistrationService were merged into a single VPFlowService interface in the authenticator bundle. Update the service holder and core service to look up and use VPFlowService via OSGi. --- .../common/VPVerificationConstants.java | 2 +- .../common/VPVerificationServiceHolder.java | 14 +++++++------- .../v1/core/ServerVPVerificationService.java | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java index 3e826679de..b95ada3fe6 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationConstants.java @@ -38,7 +38,7 @@ public enum ErrorMessage { ERROR_CODE_SESSION_NOT_FOUND("VPV-60401", "Verification session not found."), ERROR_CODE_INTERNAL_ERROR("VPV-65001", "Internal server error."), ERROR_CODE_SERVICE_UNAVAILABLE("VPV-65002", - "StandaloneVerificationService is not available."); + "VPVerificationService is not available."); private final String code; private final String message; diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java index 7a9f9b283c..6cdd6f2c9d 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/common/VPVerificationServiceHolder.java @@ -19,11 +19,11 @@ package org.wso2.carbon.identity.api.server.vp.verification.common; import org.wso2.carbon.context.PrivilegedCarbonContext; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.VPFlowService; /** * Service holder for VP Verification API. - * Obtains the StandaloneVerificationService OSGi service via PrivilegedCarbonContext. + * Obtains the VPFlowService OSGi service via PrivilegedCarbonContext. */ public class VPVerificationServiceHolder { @@ -31,14 +31,14 @@ private VPVerificationServiceHolder() { } /** - * Get StandaloneVerificationService OSGi service. + * Get VPFlowService OSGi service. * - * @return StandaloneVerificationService instance. + * @return VPFlowService instance. */ - public static StandaloneVerificationService getStandaloneVerificationService() { + public static VPFlowService getVPFlowService() { - return (StandaloneVerificationService) PrivilegedCarbonContext + return (VPFlowService) PrivilegedCarbonContext .getThreadLocalCarbonContext() - .getOSGiService(StandaloneVerificationService.class, null); + .getOSGiService(VPFlowService.class, null); } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 2e96ff2350..717fdae030 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -37,7 +37,7 @@ import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowSession; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowStatus; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.StandaloneVerificationService; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.VPFlowService; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowInitiationResult; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.PresentationMetadata; import org.wso2.carbon.identity.openid4vc.presentation.verification.dto.VerificationResult; @@ -50,7 +50,7 @@ /** * Core business logic for the VP Verification REST API. - * Delegates to StandaloneVerificationService (obtained via OSGi) and maps to REST response models. + * Delegates to VPFlowService (obtained via OSGi) and maps to REST response models. */ public class ServerVPVerificationService { @@ -69,7 +69,7 @@ public Response initiateVerification(VerificationInitiateRequest request) { "presentationDefinitionId is required."); } - StandaloneVerificationService service = getService(); + VPFlowService service = getService(); if (service == null) { return buildNotImplementedResponse(); } @@ -109,7 +109,7 @@ public Response getVerificationStatus(String txnId) { "txnId is required."); } - StandaloneVerificationService service = getService(); + VPFlowService service = getService(); if (service == null) { return buildNotImplementedResponse(); } @@ -201,8 +201,8 @@ private Presentation buildPresentation(VerificationResult result) { return p; } - private StandaloneVerificationService getService() { - return VPVerificationServiceHolder.getStandaloneVerificationService(); + private VPFlowService getService() { + return VPVerificationServiceHolder.getVPFlowService(); } private Response buildBadRequestResponse(ErrorMessage errorMsg, String description) { From 53130fa18a5097573906b6f79dcd7af8750ac310 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Wed, 8 Jul 2026 20:20:28 +0530 Subject: [PATCH 18/21] Update OpenID4VP config exception type to VPAuthenticatorException --- .../configs/v1/core/ServerConfigManagementService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java index d1023218df..56ed847f3e 100644 --- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/main/java/org/wso2/carbon/identity/api/server/configs/v1/core/ServerConfigManagementService.java @@ -63,7 +63,7 @@ import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationConfiguration; import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch; import org.wso2.carbon.identity.api.server.configs.v1.model.OpenID4VPConfiguration; -import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.OpenID4VPConfigMgtException; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.exception.VPAuthenticatorException; import org.wso2.carbon.identity.openid4vc.presentation.authenticator.service.OpenID4VPConfigService; import org.wso2.carbon.identity.openid4vc.presentation.common.constant.OpenID4VPConstants; import org.wso2.carbon.identity.api.server.configs.v1.model.InboundAuthOAuth2Config; @@ -692,7 +692,7 @@ public OpenID4VPConfiguration getOpenID4VPConfiguration() { .rejectVcWithoutStatusClaim( cfg.getRejectVcWithoutStatusClaim() != null ? cfg.getRejectVcWithoutStatusClaim() : Boolean.FALSE); - } catch (OpenID4VPConfigMgtException e) { + } catch (VPAuthenticatorException e) { throw handleException(Response.Status.INTERNAL_SERVER_ERROR, Constants.ErrorMessage.ERROR_CODE_OID4VP_CONFIG_RETRIEVE, null); } @@ -721,7 +721,7 @@ public OpenID4VPConfiguration updateOpenID4VPConfiguration(OpenID4VPConfiguratio tenantConfig.setRejectVcWithoutStatusClaim(config.getRejectVcWithoutStatusClaim()); configService.setConfig(tenantConfig, tenantDomain); return config; - } catch (OpenID4VPConfigMgtException e) { + } catch (VPAuthenticatorException e) { throw handleException(Response.Status.INTERNAL_SERVER_ERROR, Constants.ErrorMessage.ERROR_CODE_OID4VP_CONFIG_UPDATE, null); } From d16097da9ef57104417002452b5581e4d2d1669f Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 9 Jul 2026 01:32:06 +0530 Subject: [PATCH 19/21] Rename txnId to requestId across VP verification API Replace the txnId field and all associated getter/setter/annotation references with requestId in VerificationInitiateResponse, VerificationStatusResponse, VpVerificationApi, VpVerificationApiService, VpVerificationApiServiceImpl, and ServerVPVerificationService. Updates the status endpoint path parameter from {txn_id} to {request_id} and aligns JSON property names in the response models accordingly. Co-Authored-By: Claude Sonnet 4.6 --- .../v1/VerificationInitiateResponse.java | 10 ++++----- .../v1/VerificationStatusResponse.java | 10 ++++----- .../vp/verification/v1/VpVerificationApi.java | 16 +++++++------- .../v1/VpVerificationApiService.java | 2 +- .../v1/core/ServerVPVerificationService.java | 22 +++++++++---------- .../v1/impl/VpVerificationApiServiceImpl.java | 4 ++-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java index 29f8c16ef7..26f3af5ae7 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationInitiateResponse.java @@ -28,15 +28,15 @@ @ApiModel(description = "Response returned when a verification session is created.") public class VerificationInitiateResponse { - private String txnId; + private String requestId; private String walletUrl; private String requestUri; private Long expiresAt; - @ApiModelProperty(value = "Transaction ID used to poll for the verification status.") - @JsonProperty("txnId") - public String getTxnId() { return txnId; } - public void setTxnId(String txnId) { this.txnId = txnId; } + @ApiModelProperty(value = "Request ID used to poll for the verification status.") + @JsonProperty("requestId") + public String getRequestId() { return requestId; } + public void setRequestId(String requestId) { this.requestId = requestId; } @ApiModelProperty(value = "Deep-link URL to launch the wallet (show as QR code).") @JsonProperty("walletUrl") diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java index 6f3e0d865b..1cad4d33a7 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VerificationStatusResponse.java @@ -32,15 +32,15 @@ @ApiModel(description = "Response for a VP verification session status poll.") public class VerificationStatusResponse { - private String txnId; + private String requestId; private String status; private Presentation presentation; private List errors; - @ApiModelProperty(value = "Transaction ID of the verification session.") - @JsonProperty("txnId") - public String getTxnId() { return txnId; } - public void setTxnId(String txnId) { this.txnId = txnId; } + @ApiModelProperty(value = "Request ID of the verification session.") + @JsonProperty("requestId") + public String getRequestId() { return requestId; } + public void setRequestId(String requestId) { this.requestId = requestId; } @ApiModelProperty(value = "Current status: ACTIVE, VERIFIED, FAILED.") @JsonProperty("status") diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java index a35724c183..ce5ec23bed 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApi.java @@ -40,7 +40,7 @@ *

Endpoints (public — no authentication required): *

    *
  • POST /vp/verification/initiate — start a new verification transaction
  • - *
  • GET /vp/verification/status/{txn_id} — poll for transaction result
  • + *
  • GET /vp/verification/status/{request_id} — poll for verification result
  • *
*/ @Path("/vp/verification") @@ -71,13 +71,13 @@ public Response initiateVerification( } @GET - @Path("/status/{txn_id}") + @Path("/status/{request_id}") @Produces(MediaType.APPLICATION_JSON) @ApiOperation( value = "Get VP verification status", - notes = "Poll for the result of a verification transaction. " - + "Returns PENDING while waiting, COMPLETED with result_token on success, " - + "FAILED with error, or EXPIRED.", + notes = "Poll for the result of a verification session. " + + "Returns ACTIVE while waiting, VERIFIED with presentation on success, " + + "or FAILED with errors.", response = VerificationStatusResponse.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = VerificationStatusResponse.class), @@ -85,9 +85,9 @@ public Response initiateVerification( @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) }) public Response getVerificationStatus( - @ApiParam(value = "Transaction ID returned from initiation.", required = true) - @PathParam("txn_id") String txnId) { + @ApiParam(value = "Request ID returned from initiation.", required = true) + @PathParam("request_id") String requestId) { - return delegate.getVerificationStatus(txnId); + return delegate.getVerificationStatus(requestId); } } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java index 03a6d80481..1a0373a70f 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/verification/v1/VpVerificationApiService.java @@ -27,5 +27,5 @@ public abstract class VpVerificationApiService { public abstract Response initiateVerification(VerificationInitiateRequest body); - public abstract Response getVerificationStatus(String txnId); + public abstract Response getVerificationStatus(String requestId); } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java index 717fdae030..30095a181d 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/core/ServerVPVerificationService.java @@ -81,13 +81,13 @@ public Response initiateVerification(VerificationInitiateRequest request) { service.initiate(request.getPresentationDefinitionId(), tenantDomain); VerificationInitiateResponse resp = new VerificationInitiateResponse(); - resp.setTxnId(initiation.getTxnId()); + resp.setRequestId(initiation.getRequestId()); resp.setWalletUrl(initiation.getWalletUrl()); resp.setRequestUri(initiation.getRequestUri()); resp.setExpiresAt(initiation.getExpiresAt()); URI location = URI.create( - VPVerificationConstants.VP_VERIFICATION_STATUS_PATH + "/" + initiation.getTxnId()); + VPVerificationConstants.VP_VERIFICATION_STATUS_PATH + "/" + initiation.getRequestId()); return Response.created(location).entity(resp).build(); } catch (VPAuthenticatorException e) { @@ -99,14 +99,14 @@ public Response initiateVerification(VerificationInitiateRequest request) { /** * Poll the status of a verification session. * - * @param txnId Transaction ID from initiation. + * @param requestId Request ID from initiation. * @return JAX-RS Response with VerificationStatusResponse (200) or Error. */ - public Response getVerificationStatus(String txnId) { + public Response getVerificationStatus(String requestId) { - if (StringUtils.isBlank(txnId)) { + if (StringUtils.isBlank(requestId)) { return buildBadRequestResponse(ErrorMessage.ERROR_CODE_INVALID_REQUEST, - "txnId is required."); + "requestId is required."); } VPFlowService service = getService(); @@ -114,13 +114,13 @@ public Response getVerificationStatus(String txnId) { return buildNotImplementedResponse(); } - VPFlowSession session = service.getSession(txnId); + VPFlowSession session = service.getSession(requestId); if (session == null) { - return buildNotFoundResponse(txnId); + return buildNotFoundResponse(requestId); } VerificationStatusResponse resp = new VerificationStatusResponse(); - resp.setTxnId(txnId); + resp.setRequestId(requestId); resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPFlowStatus.FAILED.name()); if (session.getVerificationResult() != null && session.getStatus() == VPFlowStatus.VERIFIED) { @@ -213,11 +213,11 @@ private Response buildBadRequestResponse(ErrorMessage errorMsg, String descripti return Response.status(Response.Status.BAD_REQUEST).entity(error).build(); } - private Response buildNotFoundResponse(String txnId) { + private Response buildNotFoundResponse(String requestId) { Error error = new Error(); error.setCode(ErrorMessage.ERROR_CODE_SESSION_NOT_FOUND.getCode()); error.setMessage(ErrorMessage.ERROR_CODE_SESSION_NOT_FOUND.getMessage()); - error.setDescription("No verification session found for txnId: " + txnId); + error.setDescription("No verification session found for requestId: " + requestId); return Response.status(Response.Status.NOT_FOUND).entity(error).build(); } diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java index 08caf95970..ec6338776f 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/verification/v1/impl/VpVerificationApiServiceImpl.java @@ -39,8 +39,8 @@ public Response initiateVerification(VerificationInitiateRequest body) { } @Override - public Response getVerificationStatus(String txnId) { + public Response getVerificationStatus(String requestId) { - return CORE_SERVICE.getVerificationStatus(txnId); + return CORE_SERVICE.getVerificationStatus(requestId); } } From f96dffadc05ab8202b62ef502cf18db9bd9fadca Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 16 Jul 2026 10:09:42 +0530 Subject: [PATCH 20/21] Add credentialQueryId to RequestedCredential API model --- .../management/v1/RequestedCredentialModel.java | 12 ++++++++++++ .../v1/core/ServerVPDefinitionManagementService.java | 2 ++ 2 files changed, 14 insertions(+) diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java index 6c9cedc42b..41ac2b80c6 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/RequestedCredentialModel.java @@ -13,6 +13,8 @@ @ApiModel(description = "Requested Credential Model") public class RequestedCredentialModel { + @NotNull + private String credentialQueryId; @NotNull private String type; private String purpose; @@ -23,6 +25,16 @@ public class RequestedCredentialModel { private Boolean enforceTrustedIssuers; private List trustedIssuers; + @ApiModelProperty(value = "Credential query ID used as the DCQL credential query identifier.") + @JsonProperty("credentialQueryId") + public String getCredentialQueryId() { + return credentialQueryId; + } + + public void setCredentialQueryId(String credentialQueryId) { + this.credentialQueryId = credentialQueryId; + } + @ApiModelProperty(required = true, value = "Type of the requested credential.") @JsonProperty("type") public String getType() { diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java index 3c8572a819..7148666a29 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/src/main/java/org/wso2/carbon/identity/api/server/vp/template/management/v1/core/ServerVPDefinitionManagementService.java @@ -218,6 +218,7 @@ private List toRequestedCredentials( List result = new ArrayList<>(); for (RequestedCredentialModel apiModel : apiModels) { RequestedCredential cred = new RequestedCredential(); + cred.setCredentialQueryId(apiModel.getCredentialQueryId()); cred.setType(apiModel.getType()); cred.setPurpose(apiModel.getPurpose()); cred.setIssuer(apiModel.getIssuer()); @@ -243,6 +244,7 @@ private List toCredentialModels( List result = new ArrayList<>(); for (RequestedCredential cred : domainCredentials) { RequestedCredentialModel model = new RequestedCredentialModel(); + model.setCredentialQueryId(cred.getCredentialQueryId()); model.setType(cred.getType()); model.setPurpose(cred.getPurpose()); model.setIssuer(cred.getIssuer()); From c449eb72c8a3773a0a0d3687400f4fc12a434db6 Mon Sep 17 00:00:00 2001 From: Dinitha Wijewardhana Date: Thu, 16 Jul 2026 13:04:32 +0530 Subject: [PATCH 21/21] Bump VP module parent version from 1.6.30-SNAPSHOT to 1.6.36-SNAPSHOT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After rebasing onto upstream/master, the root POM advanced through several release cycles (1.6.30 → 1.6.36-SNAPSHOT). The two new VP modules still referenced the old parent version, causing a non-resolvable parent POM error in CI. Co-Authored-By: Claude Sonnet 4.6 --- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../org.wso2.carbon.identity.api.server.vp.verification/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml index 865557f73e..e47183a795 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.common/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.vp.template.management - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml index d52041cea1..4f8d9c3093 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/org.wso2.carbon.identity.api.server.vp.template.management.v1/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.vp.template.management - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml index 998f1b5f62..8c82effa89 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.template.management/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.identity.server.api identity-api-server - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../../pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml index 207c5ad467..c13da45356 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.common/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.vp.verification - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml index d626ef4897..df0a40f107 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/org.wso2.carbon.identity.api.server.vp.verification.v1/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.identity.server.api org.wso2.carbon.identity.api.server.vp.verification - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../pom.xml diff --git a/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml b/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml index 9978d0bcb6..d3346eabc4 100644 --- a/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml +++ b/components/org.wso2.carbon.identity.api.server.vp.verification/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.identity.server.api identity-api-server - 1.6.30-SNAPSHOT + 1.6.36-SNAPSHOT ../../pom.xml