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..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 @@ -97,6 +97,11 @@ org.wso2.carbon.identity.compatibility.settings.core 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.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..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,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.authenticator.service.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..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 @@ -204,6 +204,16 @@ commons-beanutils commons-beanutils + + org.wso2.carbon.identity.openid4vc + 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/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..e4609d8aff --- /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,161 @@ +/* + * 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 clientId; + private String responseMode; + private String registrationCertificate; + private Boolean rejectVcWithoutStatusClaim; + + 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 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; + 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; + } + + 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) { + + 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.clientId, that.clientId) + && Objects.equals(this.responseMode, that.responseMode) + && Objects.equals(this.registrationCertificate, that.registrationCertificate); + } + + @Override + public int hashCode() { + return Objects.hash(clientIdScheme, clientId, responseMode, registrationCertificate); + } + + @Override + 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("}"); + 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..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 @@ -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.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; 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,66 @@ 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 { + OpenID4VPConfigService.TenantConfig cfg = configService.getConfig(tenantDomain); + return new OpenID4VPConfiguration() + .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()) + .rejectVcWithoutStatusClaim( + cfg.getRejectVcWithoutStatusClaim() != null + ? cfg.getRejectVcWithoutStatusClaim() : Boolean.FALSE); + } catch (VPAuthenticatorException 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 { + OpenID4VPConfigService.TenantConfig tenantConfig = new OpenID4VPConfigService.TenantConfig(); + tenantConfig.setClientIdScheme(config.getClientIdScheme()); + tenantConfig.setClientId(config.getClientId()); + tenantConfig.setResponseMode(config.getResponseMode()); + tenantConfig.setRegistrationCertificate(config.getRegistrationCertificate()); + tenantConfig.setRejectVcWithoutStatusClaim(config.getRejectVcWithoutStatusClaim()); + configService.setConfig(tenantConfig, tenantDomain); + return config; + } catch (VPAuthenticatorException 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.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..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 @@ -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 Verifiable Presentation 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 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 Verifiable Presentation 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. 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.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..e47183a795 --- /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.36-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..1881a8a0ca --- /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,48 @@ +/* + * 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() { + + 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/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..4f8d9c3093 --- /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.36-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/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/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..41ac2b80c6 --- /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,117 @@ +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 credentialQueryId; + @NotNull + private String type; + private String purpose; + private String issuer; + private String issuerCertPem; + private String jwksUri; + private List claims; + 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() { + 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 = "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() { + return claims; + } + + 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/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..7148666a29 --- /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,385 @@ +/* + * 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.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; +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.ClaimConstraintModel; +import org.wso2.carbon.identity.api.server.vp.template.management.v1.RequestedCredentialModel; +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; +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 (PresentationManagementException 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 (PresentationManagementClientException e) { + if (PresentationManagementErrorCode.DEFINITION_ALREADY_EXISTS == e.getErrorCode()) { + throw handleClientError(ErrorMessage.ERROR_CODE_DEFINITION_ALREADY_EXISTS, e, + Response.Status.CONFLICT); + } + 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); + } + } + + /** + * 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 (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); + } + } + + /** + * 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 (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); + } + } + + /** + * 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 (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); + } + } + + // --- 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.setCredentialQueryId(apiModel.getCredentialQueryId()); + 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()); + 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.setCredentialQueryId(cred.getCredentialQueryId()); + 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()); + result.add(model); + } + 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(); + 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() { + + if (!Boolean.parseBoolean(IdentityUtil.getProperty("OpenID4VP.Enabled"))) { + throw buildFeatureDisabledError(); + } + PresentationDefinitionService service = + VPDefinitionManagementServiceHolder.getPresentationDefinitionService(); + if (service == null) { + 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/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..0b4f50c344 --- /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,380 @@ +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 (legacy single-issuer field). + claims: + type: array + description: List of claims requested from this credential. + 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. + 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 + 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..8c82effa89 --- /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.36-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 + + 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..c13da45356 --- /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.36-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..b95ada3fe6 --- /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", + "VPVerificationService 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..6cdd6f2c9d --- /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.VPFlowService; + +/** + * Service holder for VP Verification API. + * Obtains the VPFlowService OSGi service via PrivilegedCarbonContext. + */ +public class VPVerificationServiceHolder { + + private VPVerificationServiceHolder() { + } + + /** + * Get VPFlowService OSGi service. + * + * @return VPFlowService instance. + */ + public static VPFlowService getVPFlowService() { + + return (VPFlowService) PrivilegedCarbonContext + .getThreadLocalCarbonContext() + .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/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..df0a40f107 --- /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.36-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..25c0b6ea7e --- /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,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; +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; + + @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; + } +} 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..26f3af5ae7 --- /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 requestId; + private String walletUrl; + private String requestUri; + private Long expiresAt; + + @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") + 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..1cad4d33a7 --- /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,230 @@ +/* + * 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 requestId; + private String status; + private Presentation presentation; + private List errors; + + @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") + 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") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + 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 List credentials; + 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("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; } + + @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; + private Map claims; + + @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; } + + @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; } + } + + /** + * 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 from the sub claim. + */ + public static class Holder { + + private String id; + + @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; } + } + + /** + * 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..ce5ec23bed --- /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/{request_id} — poll for verification 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/{request_id}") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Get VP verification status", + 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), + @ApiResponse(code = 404, message = "Not Found", response = Error.class), + @ApiResponse(code = 500, message = "Internal Server Error", response = Error.class) + }) + public Response getVerificationStatus( + @ApiParam(value = "Request ID returned from initiation.", required = true) + @PathParam("request_id") String requestId) { + + 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 new file mode 100644 index 0000000000..1a0373a70f --- /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 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/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..30095a181d --- /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,240 @@ +/* + * 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.VPFlowSession; +import org.wso2.carbon.identity.openid4vc.presentation.authenticator.model.VPFlowStatus; +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; + +import java.net.URI; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.Response; + +/** + * Core business logic for the VP Verification REST API. + * Delegates to VPFlowService (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."); + } + + VPFlowService service = getService(); + if (service == null) { + return buildNotImplementedResponse(); + } + + String tenantDomain = ContextLoader.getTenantDomainFromContext(); + + try { + VPFlowInitiationResult initiation = + service.initiate(request.getPresentationDefinitionId(), tenantDomain); + + VerificationInitiateResponse resp = new VerificationInitiateResponse(); + 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.getRequestId()); + 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 requestId Request ID from initiation. + * @return JAX-RS Response with VerificationStatusResponse (200) or Error. + */ + public Response getVerificationStatus(String requestId) { + + if (StringUtils.isBlank(requestId)) { + return buildBadRequestResponse(ErrorMessage.ERROR_CODE_INVALID_REQUEST, + "requestId is required."); + } + + VPFlowService service = getService(); + if (service == null) { + return buildNotImplementedResponse(); + } + + VPFlowSession session = service.getSession(requestId); + if (session == null) { + return buildNotFoundResponse(requestId); + } + + VerificationStatusResponse resp = new VerificationStatusResponse(); + resp.setRequestId(requestId); + resp.setStatus(session.getStatus() != null ? session.getStatus().name() : VPFlowStatus.FAILED.name()); + + if (session.getVerificationResult() != null && session.getStatus() == VPFlowStatus.VERIFIED) { + resp.setPresentation(buildPresentation(session.getVerificationResult())); + resp.setErrors(session.getVerificationResult().getErrors()); + } else if (session.getStatus() == VPFlowStatus.FAILED) { + if (session.getVerificationResult() != null) { + resp.setErrors(session.getVerificationResult().getErrors()); + } + } + + return Response.ok(resp).build(); + } + + 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(firstMeta != null ? firstMeta.getVpFormat() : null); + p.setSubmittedAt(firstMeta != null + ? Instant.ofEpochMilli(firstMeta.getPresentationTime()).toString() : null); + + // ── Credentials — one entry per verified credential ─────────────────── + List credentials = new 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); + } + } + 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); + } + + // ── 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; + } + } + } + + return p; + } + + private VPFlowService getService() { + return VPVerificationServiceHolder.getVPFlowService(); + } + + 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 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 requestId: " + requestId); + 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(); + } + + 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(); + } +} 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..ec6338776f --- /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 requestId) { + + return CORE_SERVICE.getVerificationStatus(requestId); + } +} 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..d3346eabc4 --- /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.36-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 + + 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