Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
39bf1ca
Add REST API module for VP Presentation Definition management
Zeta201 Jun 18, 2026
ac82db4
Add REST API module for VP token verification with structured present…
Zeta201 Jun 18, 2026
20c02be
Register VP API modules and update openid4vc dependency version in ro…
Zeta201 Jun 18, 2026
b5b2c18
Introduce config api for managing tenant level configs
Zeta201 Jun 19, 2026
7b2939d
Implement trust issuer validation
Zeta201 Jun 23, 2026
7405742
Add status claim support
Zeta201 Jun 23, 2026
367aee0
Update OpenID4VPConfigService references and add OpenID4VP config RES…
Zeta201 Jul 5, 2026
037d3c9
Add JWKS URI support, improve exception handling, and add feature-dis…
Zeta201 Jul 5, 2026
4a0d210
Return 501 Not Implemented when OpenID4VP feature is disabled
Zeta201 Jul 5, 2026
6afaded
Rename VP abbreviations to Verifiable Presentation in configs API des…
Zeta201 Jul 5, 2026
c35488f
Move verified claims from holder to per-credential in verification st…
Zeta201 Jul 7, 2026
eb5668c
Fix inline java.util.ArrayList import in ServerVPVerificationService
Zeta201 Jul 7, 2026
13d4715
Rename VPSessionInitiation to VPRequestHandle
Zeta201 Jul 8, 2026
b5e097e
Rename VPRequestHandle to VPFlowInitiationResult
Zeta201 Jul 8, 2026
dfb6bd5
Rename VPRequestStatus to VPFlowStatus
Zeta201 Jul 8, 2026
5dacc87
Rename VPSession to VPFlowSession and VPSubmission to VPAuthorization…
Zeta201 Jul 8, 2026
f2eaa7e
Update VP service reference from VPVerificationService to VPFlowService
Zeta201 Jul 8, 2026
53130fa
Update OpenID4VP config exception type to VPAuthenticatorException
Zeta201 Jul 8, 2026
d16097d
Rename txnId to requestId across VP verification API
Zeta201 Jul 8, 2026
f96dffa
Add credentialQueryId to RequestedCredential API model
Zeta201 Jul 16, 2026
c449eb7
Bump VP module parent version from 1.6.30-SNAPSHOT to 1.6.36-SNAPSHOT
Zeta201 Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
<artifactId>org.wso2.carbon.identity.compatibility.settings.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.openid4vc</groupId>
<artifactId>org.wso2.carbon.identity.openid4vc.presentation.authenticator</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.openid4vc</groupId>
<artifactId>org.wso2.carbon.identity.openid4vc.presentation.common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.openid4vc</groupId>
<artifactId>org.wso2.carbon.identity.openid4vc.presentation.authenticator</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,6 +65,10 @@ public interface ConfigsApiService {

public Response getAgentConfiguration();

public Response getOpenID4VPConfiguration();

public Response updateOpenID4VPConfiguration(OpenID4VPConfiguration openID4VPConfiguration);

public Response getInboundScimConfigs();

public Response getIssuerUsageScopeConfig();
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down
Loading
Loading