diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementConstants.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementConstants.java
index bd6a8f5489..579904570c 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementConstants.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementConstants.java
@@ -250,7 +250,17 @@ public enum ErrorMessage {
// Organization application based issuer configuration related error messages.
ERROR_RETRIEVING_ALLOWED_ISSUERS("65603",
"Error occurred while retrieving allowed issuers for the tenant.",
- "Unexpected error occurred while retrieving allowed issuers for the tenant.");
+ "Unexpected error occurred while retrieving allowed issuers for the tenant."),
+
+ // FAPI metadata related error messages.
+ ERROR_RETRIEVING_FAPI_METADATA("65604",
+ "Error occurred while retrieving FAPI metadata.",
+ "Unexpected error occurred while retrieving FAPI metadata for tenant: %s."),
+
+ // FAPI profile validation related error messages.
+ UNSUPPORTED_FAPI_PROFILE("65605",
+ "Unsupported FAPI profile.",
+ "The provided FAPI profile '%s' is not supported by the organization. Supported profiles: %s.");
private final String code;
private final String message;
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementServiceHolder.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementServiceHolder.java
index 124acd7814..bb876390bb 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementServiceHolder.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.common/src/main/java/org/wso2/carbon/identity/api/server/application/management/common/ApplicationManagementServiceHolder.java
@@ -31,6 +31,7 @@
import org.wso2.carbon.identity.oauth.ciba.api.CibaAuthServiceImpl;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth2.config.services.OAuth2OIDCConfigOrgUsageScopeMgtService;
+import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService;
import org.wso2.carbon.identity.organization.management.application.OrgApplicationManager;
import org.wso2.carbon.identity.sso.saml.SAMLSSOConfigServiceImpl;
import org.wso2.carbon.identity.template.mgt.TemplateManager;
@@ -151,6 +152,12 @@ private static class OAuthOIDCConfigOrgUsageScopeMgtServiceHolder {
.getOSGiService(OAuth2OIDCConfigOrgUsageScopeMgtService.class, null);
}
+ private static class FapiConfigMgtServiceHolder {
+
+ static final FapiConfigMgtService SERVICE = (FapiConfigMgtService) PrivilegedCarbonContext
+ .getThreadLocalCarbonContext().getOSGiService(FapiConfigMgtService.class, null);
+ }
+
/**
* Get ApplicationManagementService.
*
@@ -289,4 +296,14 @@ public static OAuth2OIDCConfigOrgUsageScopeMgtService getOAuth2OIDCConfigOrgUsag
return OAuthOIDCConfigOrgUsageScopeMgtServiceHolder.SERVICE;
}
+
+ /**
+ * Get FapiConfigMgtService.
+ *
+ * @return FapiConfigMgtService.
+ */
+ public static FapiConfigMgtService getFapiConfigMgtService() {
+
+ return FapiConfigMgtServiceHolder.SERVICE;
+ }
}
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiMetadata.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiMetadata.java
index 0548c852d9..8618cff220 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiMetadata.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2023-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
@@ -33,11 +33,30 @@
import javax.xml.bind.annotation.*;
public class FapiMetadata {
-
+
+ private MetadataProperty allowedFapiProfiles;
private MetadataProperty allowedSignatureAlgorithms;
private MetadataProperty allowedEncryptionAlgorithms;
private ClientAuthenticationMethodMetadata tokenEndpointAuthMethod;
+ /**
+ **/
+ public FapiMetadata allowedFapiProfiles(MetadataProperty allowedFapiProfiles) {
+
+ this.allowedFapiProfiles = allowedFapiProfiles;
+ return this;
+ }
+
+ @ApiModelProperty(value = "")
+ @JsonProperty("allowedFapiProfiles")
+ @Valid
+ public MetadataProperty getAllowedFapiProfiles() {
+ return allowedFapiProfiles;
+ }
+ public void setAllowedFapiProfiles(MetadataProperty allowedFapiProfiles) {
+ this.allowedFapiProfiles = allowedFapiProfiles;
+ }
+
/**
**/
public FapiMetadata allowedSignatureAlgorithms(MetadataProperty allowedSignatureAlgorithms) {
@@ -104,14 +123,15 @@ public boolean equals(java.lang.Object o) {
return false;
}
FapiMetadata fapiMetadata = (FapiMetadata) o;
- return Objects.equals(this.allowedSignatureAlgorithms, fapiMetadata.allowedSignatureAlgorithms) &&
+ return Objects.equals(this.allowedFapiProfiles, fapiMetadata.allowedFapiProfiles) &&
+ Objects.equals(this.allowedSignatureAlgorithms, fapiMetadata.allowedSignatureAlgorithms) &&
Objects.equals(this.allowedEncryptionAlgorithms, fapiMetadata.allowedEncryptionAlgorithms) &&
Objects.equals(this.tokenEndpointAuthMethod, fapiMetadata.tokenEndpointAuthMethod);
}
@Override
public int hashCode() {
- return Objects.hash(allowedSignatureAlgorithms, allowedEncryptionAlgorithms, tokenEndpointAuthMethod);
+ return Objects.hash(allowedFapiProfiles, allowedSignatureAlgorithms, allowedEncryptionAlgorithms, tokenEndpointAuthMethod);
}
@Override
@@ -119,7 +139,8 @@ public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class FapiMetadata {\n");
-
+
+ sb.append(" allowedFapiProfiles: ").append(toIndentedString(allowedFapiProfiles)).append("\n");
sb.append(" allowedSignatureAlgorithms: ").append(toIndentedString(allowedSignatureAlgorithms)).append("\n");
sb.append(" allowedEncryptionAlgorithms: ").append(toIndentedString(allowedEncryptionAlgorithms)).append("\n");
sb.append(" tokenEndpointAuthMethod: ").append(toIndentedString(tokenEndpointAuthMethod)).append("\n");
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiProfile.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiProfile.java
new file mode 100644
index 0000000000..0d04a83634
--- /dev/null
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/FapiProfile.java
@@ -0,0 +1,64 @@
+/*
+ * 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.application.management.v1;
+
+import io.swagger.annotations.ApiModel;
+import javax.validation.constraints.*;
+
+/**
+ * FAPI security profile. - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile. - `FAPI2_SECURITY`: FAPI 2.0 Security Profile.
+ **/
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
+
+@XmlType(name="")
+@XmlEnum(String.class)
+public enum FapiProfile {
+
+ @XmlEnumValue("FAPI1_ADVANCED") FAPI1_ADVANCED(String.valueOf("FAPI1_ADVANCED")), @XmlEnumValue("FAPI2_SECURITY") FAPI2_SECURITY(String.valueOf("FAPI2_SECURITY"));
+
+
+ private String value;
+
+ FapiProfile(String v) {
+ value = v;
+ }
+
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ public static FapiProfile fromValue(String value) {
+ for (FapiProfile b : FapiProfile.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ throw new IllegalArgumentException("Unexpected value '" + value + "'");
+ }
+}
+
+
+
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/OpenIDConnectConfiguration.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/OpenIDConnectConfiguration.java
index aa0a924bc2..5439a4132c 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/OpenIDConnectConfiguration.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/gen/java/org/wso2/carbon/identity/api/server/application/management/v1/OpenIDConnectConfiguration.java
@@ -104,6 +104,7 @@ public static StateEnum fromValue(String value) {
private SubjectConfiguration subject;
private Boolean isFAPIApplication = false;
private FapiMetadata fapiMetadata;
+ private FapiProfile fapiProfile = null;
private CIBAAuthenticationRequestConfiguration cibaAuthenticationRequest;
private AllowedIssuer issuer;
@@ -538,6 +539,25 @@ public void setFapiMetadata(FapiMetadata fapiMetadata) {
this.fapiMetadata = fapiMetadata;
}
+ /**
+ * The FAPI security profile applied to the application. Applicable only when isFAPIApplication is true. If isFAPIApplication is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default.
+ **/
+ public OpenIDConnectConfiguration fapiProfile(FapiProfile fapiProfile) {
+
+ this.fapiProfile = fapiProfile;
+ return this;
+ }
+
+ @ApiModelProperty(example = "FAPI1_ADVANCED", value = "The FAPI security profile applied to the application. Applicable only when isFAPIApplication is true. If isFAPIApplication is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default. ")
+ @JsonProperty("fapiProfile")
+ @Valid
+ public FapiProfile getFapiProfile() {
+ return fapiProfile;
+ }
+ public void setFapiProfile(FapiProfile fapiProfile) {
+ this.fapiProfile = fapiProfile;
+ }
+
/**
**/
public OpenIDConnectConfiguration cibaAuthenticationRequest(CIBAAuthenticationRequestConfiguration cibaAuthenticationRequest) {
@@ -608,6 +628,7 @@ public boolean equals(java.lang.Object o) {
Objects.equals(this.subject, openIDConnectConfiguration.subject) &&
Objects.equals(this.isFAPIApplication, openIDConnectConfiguration.isFAPIApplication) &&
Objects.equals(this.fapiMetadata, openIDConnectConfiguration.fapiMetadata) &&
+ Objects.equals(this.fapiProfile, openIDConnectConfiguration.fapiProfile) &&
Objects.equals(this.cibaAuthenticationRequest, openIDConnectConfiguration.cibaAuthenticationRequest) &&
Objects.equals(this.issuer, openIDConnectConfiguration.issuer);
}
@@ -646,6 +667,7 @@ public String toString() {
sb.append(" subject: ").append(toIndentedString(subject)).append("\n");
sb.append(" isFAPIApplication: ").append(toIndentedString(isFAPIApplication)).append("\n");
sb.append(" fapiMetadata: ").append(toIndentedString(fapiMetadata)).append("\n");
+ sb.append(" fapiProfile: ").append(toIndentedString(fapiProfile)).append("\n");
sb.append(" cibaAuthenticationRequest: ").append(toIndentedString(cibaAuthenticationRequest)).append("\n");
sb.append(" issuer: ").append(toIndentedString(issuer)).append("\n");
sb.append("}");
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java
index eeb539e063..c1157d6caa 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationManagementService.java
@@ -142,6 +142,9 @@
import org.wso2.carbon.identity.cors.mgt.core.exception.CORSManagementServiceException;
import org.wso2.carbon.identity.cors.mgt.core.model.CORSOrigin;
import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
+import org.wso2.carbon.identity.oauth2.fapi.exceptions.FapiConfigMgtException;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum;
+import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService;
import org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants;
import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOServiceProviderDTO;
import org.wso2.carbon.identity.template.mgt.TemplateManager;
@@ -190,6 +193,7 @@
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ErrorMessage.FORBIDDEN_OPERATION;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ErrorMessage.INBOUND_NOT_CONFIGURED;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ErrorMessage.UNSUPPORTED_ENHANCED_ORGANIZATION_AUTHENTICATION_ENABLED_CONFIGURATION;
+import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ErrorMessage.UNSUPPORTED_FAPI_PROFILE;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ErrorMessage.USE_EXTERNAL_CONSENT_PAGE_NOT_SUPPORTED;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.ISSUER;
import static org.wso2.carbon.identity.api.server.application.management.common.ApplicationManagementConstants.NAME;
@@ -815,7 +819,10 @@ public String createApplication(ApplicationModel applicationModel, String templa
*/
if (applicationModel.getInboundProtocolConfiguration() != null &&
applicationModel.getInboundProtocolConfiguration().getOidc() != null) {
- validateCORSOrigins(applicationModel.getInboundProtocolConfiguration().getOidc().getAllowedOrigins());
+ OpenIDConnectConfiguration oidcConfig =
+ applicationModel.getInboundProtocolConfiguration().getOidc();
+ validateCORSOrigins(oidcConfig.getAllowedOrigins());
+ validateFapiProfile(oidcConfig);
}
/*
@@ -1235,6 +1242,7 @@ public void putInboundOAuthConfiguration(String applicationId, OpenIDConnectConf
* But for now we are handling the validation here.
*/
validateCORSOrigins(oidcConfigModel.getAllowedOrigins());
+ validateFapiProfile(oidcConfigModel);
putApplicationInbound(applicationId, oidcConfigModel, OAuthInboundFunctions::getInboundProtocolConfig);
}
@@ -2219,6 +2227,64 @@ private String buildFormattedDescription(String description, String... formatDat
}
}
+ /**
+ * Validates the FAPI profile in the given OIDC configuration.
+ *
+ *
Validation: checks the resolved profile against the organisation's allowed list from
+ * {@link FapiConfigMgtService}. Throws a 400 error if the profile is not supported.
+ * No-ops when {@code oidcConfig} is null, FAPI is disabled, or {@link FapiConfigMgtService}
+ * is unavailable (treated as optional OSGi service).
+ *
+ * @param oidcConfig the OIDC configuration from the incoming request; may be null.
+ */
+ private void validateFapiProfile(OpenIDConnectConfiguration oidcConfig) {
+
+ // Only validate when the application is marked as FAPI and a specific profile is provided.
+ // If isFAPIApplication is false the profile field is ignored in storage, so validation would
+ // produce a misleading error for a field that has no effect.
+ if (oidcConfig == null || !Boolean.TRUE.equals(oidcConfig.getIsFAPIApplication())
+ || oidcConfig.getFapiProfile() == null) {
+ return;
+ }
+
+ /*
+ * FapiConfigMgtService is treated as optional here. Profile validation is an enhancement —
+ * silently skipping it avoids blocking all OIDC application updates in deployments where
+ * the FAPI bundle is not present. The configs API module (/configs/fapi) hard-requires
+ * the service at startup and fails fast if it is absent.
+ */
+ FapiConfigMgtService fapiConfigMgtService = ApplicationManagementServiceHolder.getFapiConfigMgtService();
+ if (fapiConfigMgtService == null) {
+ log.debug("FapiConfigMgtService is unavailable. Skipping fapiProfile validation.");
+ return;
+ }
+
+ String tenantDomain = ContextLoader.getTenantDomainFromContext();
+ try {
+ List supportedProfiles =
+ fapiConfigMgtService.getFapiConfig(tenantDomain).getSupportedProfiles();
+ String requestedProfileValue = oidcConfig.getFapiProfile().toString();
+
+ boolean isSupported = supportedProfiles.stream()
+ .anyMatch(p -> p.value().equals(requestedProfileValue));
+
+ if (!isSupported) {
+ String supportedProfilesStr = supportedProfiles.stream()
+ .map(FapiProfileEnum::value)
+ .collect(Collectors.joining(", "));
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Rejected fapiProfile '%s' for tenant '%s'. Supported: [%s]",
+ requestedProfileValue, tenantDomain, supportedProfilesStr));
+ }
+ throw buildClientError(UNSUPPORTED_FAPI_PROFILE, requestedProfileValue, supportedProfilesStr);
+ }
+ } catch (FapiConfigMgtException e) {
+ throw Utils.buildServerError(
+ String.format("Error while retrieving FAPI configuration for tenant '%s' during fapiProfile " +
+ "validation.", tenantDomain), e);
+ }
+ }
+
/**
* Validate the CORS Origins. This should be moved to the service layer with the CORS adding step on creating
* OIDC applications.
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationMetadataService.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationMetadataService.java
index b2c5afc637..9e0606e84b 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationMetadataService.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/ServerApplicationMetadataService.java
@@ -18,6 +18,7 @@
package org.wso2.carbon.identity.api.server.application.management.v1.core;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,6 +42,7 @@
import org.wso2.carbon.identity.api.server.application.management.v1.SAMLMetaData;
import org.wso2.carbon.identity.api.server.application.management.v1.WSTrustMetaData;
import org.wso2.carbon.identity.api.server.application.management.v1.core.functions.Utils;
+import org.wso2.carbon.identity.api.server.common.ContextLoader;
import org.wso2.carbon.identity.api.server.common.error.APIError;
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.mgt.AbstractInboundAuthenticatorConfig;
@@ -55,6 +57,10 @@
import org.wso2.carbon.identity.oauth2.config.exceptions.OAuth2OIDCConfigOrgUsageScopeMgtException;
import org.wso2.carbon.identity.oauth2.config.models.IssuerDetails;
import org.wso2.carbon.identity.oauth2.config.services.OAuth2OIDCConfigOrgUsageScopeMgtService;
+import org.wso2.carbon.identity.oauth2.fapi.exceptions.FapiConfigMgtException;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum;
+import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService;
import org.wso2.carbon.identity.oauth2.model.ClientAuthenticationMethodModel;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.identity.sso.saml.SAMLSSOConfigServiceImpl;
@@ -64,6 +70,7 @@
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -89,6 +96,7 @@ public class ServerApplicationMetadataService {
private final OAuthAdminServiceImpl oAuthAdminService;
private final STSAdminServiceInterface sTSAdminServiceInterface;
private final CibaAuthServiceImpl cibaAuthService;
+ private final FapiConfigMgtService fapiConfigMgtService;
@Deprecated
public ServerApplicationMetadataService(ApplicationManagementService applicationManagementService,
@@ -96,24 +104,35 @@ public ServerApplicationMetadataService(ApplicationManagementService application
OAuthAdminServiceImpl oAuthAdminService,
STSAdminServiceInterface sTSAdminServiceInterface) {
- this.applicationManagementService = applicationManagementService;
- this.samlSSOConfigService = samlSSOConfigService;
- this.oAuthAdminService = oAuthAdminService;
- this.sTSAdminServiceInterface = sTSAdminServiceInterface;
- this.cibaAuthService = ApplicationManagementServiceHolder.getCibaAuthService();
+ this(applicationManagementService, samlSSOConfigService, oAuthAdminService, sTSAdminServiceInterface,
+ ApplicationManagementServiceHolder.getCibaAuthService(),
+ ApplicationManagementServiceHolder.getFapiConfigMgtService());
}
+ @Deprecated
public ServerApplicationMetadataService(ApplicationManagementService applicationManagementService,
SAMLSSOConfigServiceImpl samlSSOConfigService,
OAuthAdminServiceImpl oAuthAdminService,
STSAdminServiceInterface sTSAdminServiceInterface,
CibaAuthServiceImpl cibaAuthService) {
+ this(applicationManagementService, samlSSOConfigService, oAuthAdminService, sTSAdminServiceInterface,
+ cibaAuthService, ApplicationManagementServiceHolder.getFapiConfigMgtService());
+ }
+
+ public ServerApplicationMetadataService(ApplicationManagementService applicationManagementService,
+ SAMLSSOConfigServiceImpl samlSSOConfigService,
+ OAuthAdminServiceImpl oAuthAdminService,
+ STSAdminServiceInterface sTSAdminServiceInterface,
+ CibaAuthServiceImpl cibaAuthService,
+ FapiConfigMgtService fapiConfigMgtService) {
+
this.applicationManagementService = applicationManagementService;
this.samlSSOConfigService = samlSSOConfigService;
this.oAuthAdminService = oAuthAdminService;
this.sTSAdminServiceInterface = sTSAdminServiceInterface;
this.cibaAuthService = cibaAuthService;
+ this.fapiConfigMgtService = fapiConfigMgtService;
}
private static final Log LOG = LogFactory.getLog(ServerApplicationMetadataService.class);
@@ -261,6 +280,7 @@ public OIDCMetaData getOIDCMetadata() {
supportedClientAuthMethods.stream().filter(clientAuthenticationMethod ->
fapiAllowedAuthMethods.contains(clientAuthenticationMethod.getName())).collect(Collectors.toList());
FapiMetadata fapiMetadata = new FapiMetadata();
+ fapiMetadata.allowedFapiProfiles(new MetadataProperty().options(getAllowedFapiProfiles()));
fapiMetadata.allowedSignatureAlgorithms(new MetadataProperty().options(fapiAllowedSignatureAlgorithms));
fapiMetadata.allowedEncryptionAlgorithms(new MetadataProperty().options(fapiAllowedEncryptionAlgorithms));
fapiMetadata.setTokenEndpointAuthMethod(new ClientAuthenticationMethodMetadata()
@@ -527,6 +547,33 @@ private List getAllowedIssuersForOrganization() {
}
}
+ /**
+ * Retrieves the list of allowed FAPI profiles for the current tenant from FapiConfigMgtService.
+ * Returns an empty list if the service is unavailable or no profiles are configured.
+ *
+ * @return List of allowed FAPI profile string values.
+ */
+ private List getAllowedFapiProfiles() {
+
+ if (fapiConfigMgtService == null) {
+ LOG.debug("FapiConfigMgtService is not available. Returning empty FAPI profiles list.");
+ return Collections.emptyList();
+ }
+ String tenantDomain = ContextLoader.getTenantDomainFromContext();
+ try {
+ FapiConfig fapiConfig = fapiConfigMgtService.getFapiConfig(tenantDomain);
+ List supportedProfiles = fapiConfig.getSupportedProfiles();
+ if (CollectionUtils.isEmpty(supportedProfiles)) {
+ return Collections.emptyList();
+ }
+ return supportedProfiles.stream()
+ .map(FapiProfileEnum::value)
+ .collect(Collectors.toList());
+ } catch (FapiConfigMgtException e) {
+ throw handleServerError(ErrorMessage.ERROR_RETRIEVING_FAPI_METADATA, e);
+ }
+ }
+
/**
* Handles the server errors and build APIError response with the given error message and exception.
*
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/ApiModelToOAuthConsumerApp.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/ApiModelToOAuthConsumerApp.java
index f3b7745bc8..ff67ead58c 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/ApiModelToOAuthConsumerApp.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/ApiModelToOAuthConsumerApp.java
@@ -41,6 +41,7 @@
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth.dto.OAuthConsumerAppDTO;
import org.wso2.carbon.identity.oauth2.config.models.IssuerDetails;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum;
import java.util.ArrayList;
import java.util.Arrays;
@@ -90,6 +91,11 @@ public OAuthConsumerAppDTO apply(String appName, OpenIDConnectConfiguration oidc
updatePARConfigurations(consumerAppDTO, oidcModel.getPushAuthorizationRequest());
updateSubjectConfigurations(consumerAppDTO, oidcModel.getSubject());
consumerAppDTO.setFapiConformanceEnabled(oidcModel.getIsFAPIApplication());
+ // Convert FAPI profile enum to String for storage. If no profile specified; default is set to FAPI1_ADVANCED.
+ if (oidcModel.getIsFAPIApplication()) {
+ consumerAppDTO.setFapiProfile(oidcModel.getFapiProfile() != null
+ ? oidcModel.getFapiProfile().toString() : FapiProfileEnum.FAPI1_ADVANCED.value());
+ }
updateSubjectTokenConfigurations(consumerAppDTO, oidcModel.getSubjectToken());
updateCIBAAuthenticationRequestConfigurations(consumerAppDTO, oidcModel.getCibaAuthenticationRequest());
updateIssuerDetails(consumerAppDTO, oidcModel.getIssuer());
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/OAuthConsumerAppToApiModel.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/OAuthConsumerAppToApiModel.java
index 78d7f139ac..43e78301a4 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/OAuthConsumerAppToApiModel.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/core/functions/application/inbound/oauth2/OAuthConsumerAppToApiModel.java
@@ -18,10 +18,13 @@
package org.wso2.carbon.identity.api.server.application.management.v1.core.functions.application.inbound.oauth2;
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.application.management.v1.AccessTokenConfiguration;
import org.wso2.carbon.identity.api.server.application.management.v1.AllowedIssuer;
import org.wso2.carbon.identity.api.server.application.management.v1.CIBAAuthenticationRequestConfiguration;
import org.wso2.carbon.identity.api.server.application.management.v1.ClientAuthenticationConfiguration;
+import org.wso2.carbon.identity.api.server.application.management.v1.FapiProfile;
import org.wso2.carbon.identity.api.server.application.management.v1.HybridFlowConfiguration;
import org.wso2.carbon.identity.api.server.application.management.v1.IdTokenConfiguration;
import org.wso2.carbon.identity.api.server.application.management.v1.IdTokenEncryptionConfiguration;
@@ -48,6 +51,8 @@
*/
public class OAuthConsumerAppToApiModel implements Function {
+ private static final Log LOG = LogFactory.getLog(OAuthConsumerAppToApiModel.class);
+
@Override
public OpenIDConnectConfiguration apply(OAuthConsumerAppDTO oauthAppDTO) {
@@ -72,6 +77,7 @@ public OpenIDConnectConfiguration apply(OAuthConsumerAppDTO oauthAppDTO) {
.pushAuthorizationRequest(buildPARAuthenticationConfiguration(oauthAppDTO))
.subject(buildSubjectConfiguration(oauthAppDTO))
.isFAPIApplication(oauthAppDTO.isFapiConformanceEnabled())
+ .fapiProfile(buildFapiProfileConfiguration(oauthAppDTO))
.subjectToken(buildSubjectTokenConfiguration(oauthAppDTO))
.cibaAuthenticationRequest(buildCIBAAuthenticationRequestConfiguration(oauthAppDTO))
.issuer(buildIssuerOrganizationConfiguration(oauthAppDTO));
@@ -272,4 +278,28 @@ private AllowedIssuer buildIssuerOrganizationConfiguration(OAuthConsumerAppDTO o
}
return null;
}
+
+ /**
+ * Converts the stored FAPI profile string from the DTO into the API model enum.
+ * Returns null if no profile is stored, which will omit the field from the response.
+ *
+ * @param oauthAppDTO the DTO containing the stored FAPI profile string.
+ * @return the {@link FapiProfile} enum constant, or null if not set.
+ */
+ private FapiProfile buildFapiProfileConfiguration(OAuthConsumerAppDTO oauthAppDTO) {
+
+ String fapiProfileValue = oauthAppDTO.getFapiProfile();
+ if (StringUtils.isBlank(fapiProfileValue)) {
+ return null;
+ }
+ try {
+ return FapiProfile.fromValue(fapiProfileValue);
+ } catch (IllegalArgumentException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format("Unrecognised fapiProfile value stored for application '%s': %s",
+ oauthAppDTO.getApplicationName(), fapiProfileValue), e);
+ }
+ return null;
+ }
+ }
}
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/factories/ServerApplicationMetadataServiceFactory.java b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/factories/ServerApplicationMetadataServiceFactory.java
index 6028f95876..536b511c4f 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/factories/ServerApplicationMetadataServiceFactory.java
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/java/org/wso2/carbon/identity/api/server/application/management/v1/factories/ServerApplicationMetadataServiceFactory.java
@@ -23,6 +23,7 @@
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl;
import org.wso2.carbon.identity.oauth.ciba.api.CibaAuthServiceImpl;
+import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService;
import org.wso2.carbon.identity.sso.saml.SAMLSSOConfigServiceImpl;
import org.wso2.carbon.security.sts.service.STSAdminServiceInterface;
@@ -40,6 +41,7 @@ public class ServerApplicationMetadataServiceFactory {
OAuthAdminServiceImpl oAuthAdminService = ApplicationManagementServiceHolder.getOAuthAdminService();
STSAdminServiceInterface sTSAdminServiceInterface = ApplicationManagementServiceHolder.getStsAdminService();
CibaAuthServiceImpl cibaAuthService = ApplicationManagementServiceHolder.getCibaAuthService();
+ FapiConfigMgtService fapiConfigMgtService = ApplicationManagementServiceHolder.getFapiConfigMgtService();
if (applicationManagementService == null) {
throw new IllegalStateException("ApplicationManagementService is not available from OSGi context.");
@@ -56,7 +58,7 @@ public class ServerApplicationMetadataServiceFactory {
// Null check for STSAdminServiceInterface is not mandatory as per the previous implementation.
SERVICE = new ServerApplicationMetadataService(applicationManagementService, samlSSOConfigService,
- oAuthAdminService, sTSAdminServiceInterface, cibaAuthService);
+ oAuthAdminService, sTSAdminServiceInterface, cibaAuthService, fapiConfigMgtService);
}
/**
diff --git a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/resources/applications.yaml b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/resources/applications.yaml
index f64d1751ad..c9d1ec1734 100644
--- a/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/resources/applications.yaml
+++ b/components/org.wso2.carbon.identity.api.server.application.management/org.wso2.carbon.identity.api.server.application.management.v1/src/main/resources/applications.yaml
@@ -3977,6 +3977,13 @@ components:
default: false
description: Enabling this option will make the application FAPI conformant.
example: false
+ fapiProfile:
+ description: |
+ The FAPI security profile applied to the application. Applicable only when isFAPIApplication is true.
+ If isFAPIApplication is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default.
+ allOf:
+ - $ref: '#/components/schemas/FapiProfile'
+ example: FAPI1_ADVANCED
cibaAuthenticationRequest:
$ref: '#/components/schemas/CIBAAuthenticationRequestConfiguration'
issuer:
@@ -4308,12 +4315,25 @@ components:
FapiMetadata:
type: object
properties:
+ allowedFapiProfiles:
+ description: The FAPI security profiles supported by the tenant for this application.
+ allOf:
+ - $ref: '#/components/schemas/MetadataProperty'
allowedSignatureAlgorithms:
$ref: '#/components/schemas/MetadataProperty'
allowedEncryptionAlgorithms:
$ref: '#/components/schemas/MetadataProperty'
tokenEndpointAuthMethod:
$ref: '#/components/schemas/ClientAuthenticationMethodMetadata'
+ FapiProfile:
+ type: string
+ description: |
+ FAPI security profile.
+ - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile.
+ - `FAPI2_SECURITY`: FAPI 2.0 Security Profile.
+ enum:
+ - FAPI1_ADVANCED
+ - FAPI2_SECURITY
CIBAMetadata:
type: object
properties:
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 76e236ab6d..762adcdf83 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
@@ -29,6 +29,7 @@
import org.wso2.carbon.identity.oauth.dcr.DCRConfigurationMgtService;
import org.wso2.carbon.identity.oauth2.agent.services.AgentConfigMgtService;
import org.wso2.carbon.identity.oauth2.config.services.OAuth2OIDCConfigOrgUsageScopeMgtService;
+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.idp.mgt.IdentityProviderManager;
@@ -133,6 +134,13 @@ private static class ConsentAppMappingServiceHolder {
.getOSGiService(ConsentAppMappingService.class, null);
}
+ private static class FapiConfigMgtServiceHolder {
+
+ static final FapiConfigMgtService SERVICE =
+ (FapiConfigMgtService) PrivilegedCarbonContext.getThreadLocalCarbonContext()
+ .getOSGiService(FapiConfigMgtService.class, null);
+ }
+
/**
* Get ApplicationManagementService osgi service.
*
@@ -272,4 +280,14 @@ public static ConsentAppMappingService getConsentAppMappingService() {
return ConsentAppMappingServiceHolder.SERVICE;
}
+
+ /**
+ * Get FapiConfigMgtService osgi service.
+ *
+ * @return FapiConfigMgtService
+ */
+ public static FapiConfigMgtService getFapiConfigMgtService() {
+
+ return FapiConfigMgtServiceHolder.SERVICE;
+ }
}
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 905e1a477d..6ac4fb2459 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
@@ -77,6 +77,7 @@ private Constants() {
public static final String DCR_CONFIG_SSA_JWKS = "/ssaJwks";
public static final String DCR_CONFIG_AUTHENTICATION_REQUIRED = "/authenticationRequired";
public static final String DCR_CONFIG_MANDATE_SSA = "/mandateSSA";
+ public static final String DCR_CONFIG_FAPI_PROFILE = "/fapiProfile";
/**
* SAML2 metadata endpoint uri path.
@@ -291,7 +292,13 @@ public enum ErrorMessage {
"Server encountered an error while mapping application %s."),
ERROR_CODE_CONSENT_MAPPING_DELETE("65035",
"Unable to delete consent application mapping.",
- "Server encountered an error while removing application %s.");
+ "Server encountered an error while removing application %s."),
+ ERROR_CODE_FAPI_CONFIG_RETRIEVE("65036",
+ "Unable to retrieve FAPI configuration.",
+ "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.");
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/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 e28dba8a60..92ea7bda19 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
@@ -36,6 +36,7 @@
import org.wso2.carbon.identity.api.server.configs.v1.model.Error;
import org.wso2.carbon.identity.api.server.configs.v1.model.AgentConfiguration;
import org.wso2.carbon.identity.api.server.configs.v1.model.AgentConfigPatch;
+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.ImpersonationConfiguration;
import org.wso2.carbon.identity.api.server.configs.v1.model.ImpersonationPatch;
@@ -1169,4 +1170,52 @@ public Response getCompatibilitySettingsByGroup(@ApiParam(value = "Setting group
return delegate.getCompatibilitySettingsByGroup(settingGroup);
}
+
+ @Valid
+ @GET
+ @Path("/fapi")
+
+ @Produces({ "application/json" })
+ @ApiOperation(value = "Retrieve the tenant FAPI configuration.", notes = "Retrieve the tenant Financial-grade API (FAPI) configuration.", response = FapiConfig.class, authorizations = {
+ @Authorization(value = "BasicAuth"),
+ @Authorization(value = "OAuth2", scopes = {
+
+ })
+ }, tags={ "FAPI Configurations", })
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful Response", response = FapiConfig.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 getFAPIConfiguration() {
+
+ return delegate.getFAPIConfiguration();
+ }
+
+ @Valid
+ @PUT
+ @Path("/fapi")
+ @Consumes({ "application/json" })
+ @Produces({ "application/json" })
+ @ApiOperation(value = "Update the tenant FAPI configuration.", notes = "Update the tenant Financial-grade API (FAPI) configuration. Scope (Permission) required:
* internal_config_update ", response = FapiConfig.class, authorizations = {
+ @Authorization(value = "BasicAuth"),
+ @Authorization(value = "OAuth2", scopes = {
+
+ })
+ }, tags={ "FAPI Configurations", })
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful Response", response = FapiConfig.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 updateFAPIConfiguration(@ApiParam(value = "" ,required=true) @Valid FapiConfig fapiConfig) {
+
+ return delegate.updateFAPIConfiguration(fapiConfig );
+ }
}
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 999b2bd41d..8900d09ee8 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
@@ -73,8 +73,11 @@ public interface ConfigsApiService {
public Response getPassiveSTSInboundAuthConfig();
public Response getPrivatKeyJWTValidationConfiguration();
+
public Response getDCRConfiguration();
+ public Response getFAPIConfiguration();
+
public Response getRemoteLoggingConfig(String logType);
public Response getRemoteLoggingConfigs();
@@ -99,6 +102,8 @@ public interface ConfigsApiService {
public Response patchDCRConfiguration(List patch);
+ public Response updateFAPIConfiguration(FapiConfig fapiConfig);
+
public Response restoreServerRemoteLoggingConfiguration(String logType);
public Response restoreServerRemoteLoggingConfigurations();
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/DCRConfig.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/DCRConfig.java
index 32ed63eeb7..8c1b19e252 100644
--- a/components/org.wso2.carbon.identity.api.server.configs/org.wso2.carbon.identity.api.server.configs.v1/src/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/DCRConfig.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/DCRConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2023-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
@@ -22,6 +22,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiProfile;
import javax.validation.constraints.*;
@@ -35,6 +36,7 @@ public class DCRConfig {
private Boolean authenticationRequired;
private String ssaJwks;
private Boolean enableFapiEnforcement;
+ private FapiProfile fapiProfile = null;
private Boolean mandateSSA;
/**
@@ -94,6 +96,25 @@ public void setEnableFapiEnforcement(Boolean enableFapiEnforcement) {
this.enableFapiEnforcement = enableFapiEnforcement;
}
+ /**
+ * The FAPI security profile to enforce for applications created via DCR. Applicable only when enableFapiEnforcement is true. If enableFapiEnforcement is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default.
+ **/
+ public DCRConfig fapiProfile(FapiProfile fapiProfile) {
+
+ this.fapiProfile = fapiProfile;
+ return this;
+ }
+
+ @ApiModelProperty(example = "FAPI1_ADVANCED", value = "The FAPI security profile to enforce for applications created via DCR. Applicable only when enableFapiEnforcement is true. If enableFapiEnforcement is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default. ")
+ @JsonProperty("fapiProfile")
+ @Valid
+ public FapiProfile getFapiProfile() {
+ return fapiProfile;
+ }
+ public void setFapiProfile(FapiProfile fapiProfile) {
+ this.fapiProfile = fapiProfile;
+ }
+
/**
* If true, the software_statement parameter is mandatory for the DCR create request.
**/
@@ -128,12 +149,13 @@ public boolean equals(java.lang.Object o) {
return Objects.equals(this.authenticationRequired, dcRConfig.authenticationRequired) &&
Objects.equals(this.ssaJwks, dcRConfig.ssaJwks) &&
Objects.equals(this.enableFapiEnforcement, dcRConfig.enableFapiEnforcement) &&
+ Objects.equals(this.fapiProfile, dcRConfig.fapiProfile) &&
Objects.equals(this.mandateSSA, dcRConfig.mandateSSA);
}
@Override
public int hashCode() {
- return Objects.hash(authenticationRequired, ssaJwks, enableFapiEnforcement, mandateSSA);
+ return Objects.hash(authenticationRequired, ssaJwks, enableFapiEnforcement, fapiProfile, mandateSSA);
}
@Override
@@ -145,6 +167,7 @@ public String toString() {
sb.append(" authenticationRequired: ").append(toIndentedString(authenticationRequired)).append("\n");
sb.append(" ssaJwks: ").append(toIndentedString(ssaJwks)).append("\n");
sb.append(" enableFapiEnforcement: ").append(toIndentedString(enableFapiEnforcement)).append("\n");
+ sb.append(" fapiProfile: ").append(toIndentedString(fapiProfile)).append("\n");
sb.append(" mandateSSA: ").append(toIndentedString(mandateSSA)).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/gen/java/org/wso2/carbon/identity/api/server/configs/v1/model/FapiConfig.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/FapiConfig.java
new file mode 100644
index 0000000000..4bae0cd024
--- /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/FapiConfig.java
@@ -0,0 +1,136 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonCreator;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.ArrayList;
+import java.util.List;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiProfile;
+import javax.validation.constraints.*;
+
+/**
+ * Financial-grade API (FAPI) configuration for the tenant.
+ **/
+
+import io.swagger.annotations.*;
+import java.util.Objects;
+import javax.validation.Valid;
+import javax.xml.bind.annotation.*;
+@ApiModel(description = "Financial-grade API (FAPI) configuration for the tenant.")
+public class FapiConfig {
+
+ private Boolean enabled = false;
+ private List supportedProfiles = null;
+
+
+ /**
+ * Indicates whether FAPI compliance enforcement is enabled for the tenant.
+ **/
+ public FapiConfig enabled(Boolean enabled) {
+
+ this.enabled = enabled;
+ return this;
+ }
+
+ @ApiModelProperty(example = "false", value = "Indicates whether FAPI compliance enforcement is enabled for the tenant.")
+ @JsonProperty("enabled")
+ @Valid
+ public Boolean getEnabled() {
+ return enabled;
+ }
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * List of FAPI security profiles supported by the tenant. - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile (Read and Write API Security Profile). - `FAPI2_SECURITY`: FAPI 2.0 Security Profile (baseline profile for high-security OAuth 2.0 APIs).
+ **/
+ public FapiConfig supportedProfiles(List supportedProfiles) {
+
+ this.supportedProfiles = supportedProfiles;
+ return this;
+ }
+
+ @ApiModelProperty(example = "[\"FAPI1_ADVANCED\",\"FAPI2_SECURITY\"]", value = "List of FAPI security profiles supported by the tenant. - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile (Read and Write API Security Profile). - `FAPI2_SECURITY`: FAPI 2.0 Security Profile (baseline profile for high-security OAuth 2.0 APIs). ")
+ @JsonProperty("supportedProfiles")
+ @Valid
+ public List getSupportedProfiles() {
+ return supportedProfiles;
+ }
+ public void setSupportedProfiles(List supportedProfiles) {
+ this.supportedProfiles = supportedProfiles;
+ }
+
+ public FapiConfig addSupportedProfilesItem(FapiProfile supportedProfilesItem) {
+ if (this.supportedProfiles == null) {
+ this.supportedProfiles = new ArrayList<>();
+ }
+ this.supportedProfiles.add(supportedProfilesItem);
+ return this;
+ }
+
+
+
+ @Override
+ public boolean equals(java.lang.Object o) {
+
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FapiConfig fapiConfig = (FapiConfig) o;
+ return Objects.equals(this.enabled, fapiConfig.enabled) &&
+ Objects.equals(this.supportedProfiles, fapiConfig.supportedProfiles);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(enabled, supportedProfiles);
+ }
+
+ @Override
+ public String toString() {
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("class FapiConfig {\n");
+
+ sb.append(" enabled: ").append(toIndentedString(enabled)).append("\n");
+ sb.append(" supportedProfiles: ").append(toIndentedString(supportedProfiles)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(java.lang.Object o) {
+
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+}
+
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/FapiProfile.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/FapiProfile.java
new file mode 100644
index 0000000000..631ead9687
--- /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/FapiProfile.java
@@ -0,0 +1,72 @@
+/*
+ * 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 io.swagger.annotations.ApiModel;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum;
+
+import javax.validation.constraints.*;
+
+/**
+ * FAPI security profile. - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile. - `FAPI2_SECURITY`: FAPI 2.0 Security Profile.
+ **/
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
+
+@XmlType(name="")
+@XmlEnum(String.class)
+public enum FapiProfile {
+
+ @XmlEnumValue("FAPI1_ADVANCED") FAPI1_ADVANCED(String.valueOf("FAPI1_ADVANCED")), @XmlEnumValue("FAPI2_SECURITY") FAPI2_SECURITY(String.valueOf("FAPI2_SECURITY"));
+
+
+ private String value;
+
+ FapiProfile(String v) {
+ value = v;
+ }
+
+ public String value() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+ public static FapiProfile fromValue(String value) {
+
+ for (FapiProfile b : FapiProfile.values()) {
+ if (b.value.equals(value)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ public static FapiProfile fromValue(FapiProfileEnum fapiProfileEnum) {
+
+ if (fapiProfileEnum == null) {
+ return null;
+ }
+ return fromValue(fapiProfileEnum.value());
+ }
+}
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 4b5d6f0109..6e6e6337fd 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
@@ -39,6 +39,7 @@
import org.wso2.carbon.identity.api.server.configs.v1.function.CORSConfigurationToCORSConfig;
import org.wso2.carbon.identity.api.server.configs.v1.function.CompatibilitySettingUtil;
import org.wso2.carbon.identity.api.server.configs.v1.function.DCRConnectorUtil;
+import org.wso2.carbon.identity.api.server.configs.v1.function.FAPIConnectorUtil;
import org.wso2.carbon.identity.api.server.configs.v1.function.JWTConnectorUtil;
import org.wso2.carbon.identity.api.server.configs.v1.model.AgentConfigPatch;
import org.wso2.carbon.identity.api.server.configs.v1.model.AgentConfiguration;
@@ -56,6 +57,8 @@
import org.wso2.carbon.identity.api.server.configs.v1.model.Endpoint;
import org.wso2.carbon.identity.api.server.configs.v1.model.EventConfig;
import org.wso2.carbon.identity.api.server.configs.v1.model.EventProperty;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiConfig;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiProfile;
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;
@@ -124,6 +127,9 @@
import org.wso2.carbon.identity.oauth2.config.models.IssuerUsageScopeConfig;
import org.wso2.carbon.identity.oauth2.config.models.UsageScope;
import org.wso2.carbon.identity.oauth2.config.services.OAuth2OIDCConfigOrgUsageScopeMgtService;
+import org.wso2.carbon.identity.oauth2.fapi.exceptions.FapiConfigMgtClientException;
+import org.wso2.carbon.identity.oauth2.fapi.exceptions.FapiConfigMgtException;
+import org.wso2.carbon.identity.oauth2.fapi.services.FapiConfigMgtService;
import org.wso2.carbon.identity.oauth2.impersonation.exceptions.ImpersonationConfigMgtClientException;
import org.wso2.carbon.identity.oauth2.impersonation.exceptions.ImpersonationConfigMgtException;
import org.wso2.carbon.identity.oauth2.impersonation.exceptions.ImpersonationConfigMgtServerException;
@@ -184,6 +190,7 @@ public class ServerConfigManagementService {
private final DCRConfigurationMgtService dcrConfigurationMgtService;
private final OAuth2OIDCConfigOrgUsageScopeMgtService oauth2OIDCConfigOrgUsageScopeMgtService;
private final CompatibilitySettingsService compatibilitySettingsService;
+ private final FapiConfigMgtService fapiConfigMgtService;
private static final Log log = LogFactory.getLog(ServerConfigManagementService.class);
@@ -198,7 +205,8 @@ public ServerConfigManagementService(ApplicationManagementService applicationMan
FraudDetectionConfigsService fraudDetectionConfigsService,
OAuth2OIDCConfigOrgUsageScopeMgtService
oauth2OIDCConfigOrgUsageScopeMgtService,
- CompatibilitySettingsService identityCompatibilitySettingsService) {
+ CompatibilitySettingsService identityCompatibilitySettingsService,
+ FapiConfigMgtService fapiConfigMgtService) {
this.applicationManagementService = applicationManagementService;
this.idpManager = idpManager;
@@ -211,6 +219,7 @@ public ServerConfigManagementService(ApplicationManagementService applicationMan
this.fraudDetectionConfigsService = fraudDetectionConfigsService;
this.oauth2OIDCConfigOrgUsageScopeMgtService = oauth2OIDCConfigOrgUsageScopeMgtService;
this.compatibilitySettingsService = identityCompatibilitySettingsService;
+ this.fapiConfigMgtService = fapiConfigMgtService;
}
/**
@@ -610,6 +619,50 @@ public void deleteAgentConfiguration() {
}
}
+ /**
+ * Get the FAPI configuration for the current tenant.
+ *
+ * @return FapiConfig API model.
+ */
+ public FapiConfig getFAPIConfiguration() {
+
+ String tenantDomain = ContextLoader.getTenantDomainFromContext();
+ try {
+ return FAPIConnectorUtil.toApiModel(fapiConfigMgtService.getFapiConfig(tenantDomain));
+ } catch (FapiConfigMgtClientException e) {
+ throw new APIError(Response.Status.BAD_REQUEST, this.getFapiConfigErrorResponse(e,
+ Constants.ErrorMessage.ERROR_CODE_FAPI_CONFIG_RETRIEVE, tenantDomain));
+ } catch (FapiConfigMgtException e) {
+ throw new APIError(Response.Status.INTERNAL_SERVER_ERROR, this.getFapiConfigErrorResponse(e,
+ Constants.ErrorMessage.ERROR_CODE_FAPI_CONFIG_RETRIEVE, tenantDomain));
+ }
+ }
+
+ /**
+ * Update the FAPI configuration for the current tenant.
+ *
+ * @param fapiConfig the API model to persist.
+ * @return the updated FapiConfig API model.
+ */
+ public FapiConfig updateFAPIConfiguration(FapiConfig fapiConfig) {
+
+ if (fapiConfig == null) {
+ throw handleException(Response.Status.BAD_REQUEST, Constants.ErrorMessage.ERROR_CODE_INVALID_INPUT,
+ "FAPI configuration is required in the request body.");
+ }
+ final String tenantDomain = ContextLoader.getTenantDomainFromContext();
+ try {
+ fapiConfigMgtService.setFapiConfig(FAPIConnectorUtil.toOAuthModel(fapiConfig), tenantDomain);
+ return FAPIConnectorUtil.toApiModel(fapiConfigMgtService.getFapiConfig(tenantDomain));
+ } catch (FapiConfigMgtClientException e) {
+ throw new APIError(Response.Status.BAD_REQUEST, this.getFapiConfigErrorResponse(e,
+ Constants.ErrorMessage.ERROR_CODE_FAPI_CONFIG_UPDATE, tenantDomain));
+ } catch (FapiConfigMgtException e) {
+ throw new APIError(Response.Status.INTERNAL_SERVER_ERROR, this.getFapiConfigErrorResponse(e,
+ Constants.ErrorMessage.ERROR_CODE_FAPI_CONFIG_UPDATE, tenantDomain));
+ }
+ }
+
/**
* Get the CORS config for a tenant.
*/
@@ -1583,6 +1636,20 @@ private APIError handleAgentConfigException(AgentConfigMgtException e,
return new APIError(status, errorResponse);
}
+ private ErrorResponse getFapiConfigErrorResponse(FapiConfigMgtException e,
+ Constants.ErrorMessage errorEnum, String data) {
+
+ final ErrorResponse errorResponse = getErrorBuilder(errorEnum, data).build(log, e.getMessage());
+ errorResponse.setDescription(e.getMessage());
+ if (e.getErrorCode() != null) {
+ String errorCode = e.getErrorCode();
+ errorCode = errorCode.contains(
+ org.wso2.carbon.identity.api.server.common.Constants.ERROR_CODE_DELIMITER)
+ ? errorCode : Constants.CONFIG_ERROR_PREFIX + errorCode;
+ errorResponse.setCode(errorCode);
+ }
+ return errorResponse;
+ }
/**
* Handle exceptions generated in API.
@@ -1798,6 +1865,13 @@ public void patchDCRConfig(List dcrPatchList) {
} else if (path.matches(Constants.DCR_CONFIG_SSA_JWKS)) {
String value = dcrPatch.getValue();
dcrConfig.setSsaJwks(value);
+ } else if (path.matches(Constants.DCR_CONFIG_FAPI_PROFILE)) {
+ FapiProfile fapiProfile = FapiProfile.fromValue(dcrPatch.getValue());
+ if (fapiProfile == null) {
+ throw handleException(Response.Status.BAD_REQUEST, Constants.ErrorMessage
+ .ERROR_CODE_INVALID_INPUT, "Unsupported patch value for the given path");
+ }
+ dcrConfig.setFapiProfile(fapiProfile);
} else {
// Throw an error if any other patch operations are sent in the request.
throw handleException(Response.Status.BAD_REQUEST, Constants.ErrorMessage
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/factories/ServerConfigManagementServiceFactory.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/factories/ServerConfigManagementServiceFactory.java
index 765be08778..ffda01c17c 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/factories/ServerConfigManagementServiceFactory.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/factories/ServerConfigManagementServiceFactory.java
@@ -9,6 +9,7 @@
import org.wso2.carbon.identity.oauth.dcr.DCRConfigurationMgtService;
import org.wso2.carbon.identity.oauth2.agent.services.AgentConfigMgtService;
import org.wso2.carbon.identity.oauth2.config.services.OAuth2OIDCConfigOrgUsageScopeMgtService;
+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.idp.mgt.IdentityProviderManager;
@@ -41,6 +42,7 @@ public class ServerConfigManagementServiceFactory {
ConfigsServiceHolder.getOAuth2OIDCConfigOrgUsageScopeMgtService();
CompatibilitySettingsService compatibilitySettingsService = ConfigsServiceHolder
.getIdentityCompatibilitySettingsService();
+ FapiConfigMgtService fapiConfigMgtService = ConfigsServiceHolder.getFapiConfigMgtService();
if (applicationManagementService == null) {
throw new IllegalStateException("ApplicationManagementService is not available from OSGi context.");
@@ -87,6 +89,10 @@ public class ServerConfigManagementServiceFactory {
throw new IllegalStateException("CompatibilitySettingsService is not available from OSGi context.");
}
+ if (fapiConfigMgtService == null) {
+ throw new IllegalStateException("FapiConfigMgtService is not available from OSGi context.");
+ }
+
SERVICE = new ServerConfigManagementService(applicationManagementService, identityProviderManager,
corsManagementService,
remoteLoggingConfigService,
@@ -96,7 +102,8 @@ public class ServerConfigManagementServiceFactory {
jwtClientAuthenticatorMgtService,
fraudDetectionConfigsService,
oAuth2OIDCConfigOrgUsageScopeMgtService,
- compatibilitySettingsService
+ compatibilitySettingsService,
+ fapiConfigMgtService
);
}
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/function/DCRConnectorUtil.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/function/DCRConnectorUtil.java
index fa55d9de57..38f4d6be65 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/function/DCRConnectorUtil.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/function/DCRConnectorUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2024-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
@@ -25,11 +25,13 @@
import org.wso2.carbon.identity.api.server.common.error.ErrorResponse;
import org.wso2.carbon.identity.api.server.configs.common.Constants;
import org.wso2.carbon.identity.api.server.configs.v1.model.DCRConfig;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiProfile;
import org.wso2.carbon.identity.oauth.dcr.DCRConfigurationMgtService;
import org.wso2.carbon.identity.oauth.dcr.exception.DCRMClientException;
import org.wso2.carbon.identity.oauth.dcr.exception.DCRMException;
import org.wso2.carbon.identity.oauth.dcr.exception.DCRMServerException;
import org.wso2.carbon.identity.oauth.dcr.model.DCRConfiguration;
+import org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum;
import javax.ws.rs.core.Response;
@@ -72,6 +74,7 @@ private static DCRConfig dcrConfigurationToDCRConfig(DCRConfiguration dcrConfigu
dcrConfig.setEnableFapiEnforcement(dcrConfiguration.getEnableFapiEnforcement());
dcrConfig.setSsaJwks(dcrConfiguration.getSsaJwks());
dcrConfig.setMandateSSA(dcrConfiguration.getMandateSSA());
+ dcrConfig.setFapiProfile(FapiProfile.fromValue(dcrConfiguration.getFapiProfile()));
return dcrConfig;
}
@@ -166,6 +169,8 @@ private static DCRConfiguration getDCRConfigurationFromDCRConfig (DCRConfig dcrC
dcrConfiguration.setSsaJwks(dcrConfig.getSsaJwks());
dcrConfiguration.setAuthenticationRequired(dcrConfig.getAuthenticationRequired());
dcrConfiguration.setMandateSSA(dcrConfig.getMandateSSA());
+ dcrConfiguration.setFapiProfile(dcrConfig.getFapiProfile() == null ?
+ null : FapiProfileEnum.fromValue(dcrConfig.getFapiProfile().value()));
return dcrConfiguration;
}
}
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/function/FAPIConnectorUtil.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/function/FAPIConnectorUtil.java
new file mode 100644
index 0000000000..1cae2e2f50
--- /dev/null
+++ 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/function/FAPIConnectorUtil.java
@@ -0,0 +1,97 @@
+/*
+ * 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.function;
+
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiConfig;
+import org.wso2.carbon.identity.api.server.configs.v1.model.FapiProfile;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Utility class for converting between the API-layer FAPI model and the OAuth-bundle FAPI model.
+ */
+public class FAPIConnectorUtil {
+
+ private FAPIConnectorUtil() {
+ // Utility class — no instantiation.
+ }
+
+ /**
+ * Convert an OAuth-bundle {@link org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig}
+ * to the API-layer {@link FapiConfig}.
+ *
+ * @param oauthModel the OAuth bundle model; may be null.
+ * @return the API model; never null.
+ */
+ public static FapiConfig toApiModel(
+ org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig oauthModel) {
+
+ FapiConfig apiModel = new FapiConfig();
+ if (oauthModel == null) {
+ apiModel.setEnabled(false);
+ apiModel.setSupportedProfiles(Collections.emptyList());
+ return apiModel;
+ }
+ apiModel.setEnabled(oauthModel.isEnabled());
+ List oauthProfiles =
+ oauthModel.getSupportedProfiles();
+ if (oauthProfiles == null || oauthProfiles.isEmpty()) {
+ apiModel.setSupportedProfiles(Collections.emptyList());
+ } else {
+ apiModel.setSupportedProfiles(
+ oauthProfiles.stream()
+ .map(p -> FapiProfile.fromValue(p.value()))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ }
+ return apiModel;
+ }
+
+ /**
+ * Convert an API-layer {@link FapiConfig} to the OAuth-bundle
+ * {@link org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig}.
+ *
+ * @param apiModel the API layer model; may be null.
+ * @return the OAuth model; never null.
+ */
+ public static org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig toOAuthModel(FapiConfig apiModel) {
+
+ org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig oauthModel =
+ new org.wso2.carbon.identity.oauth2.fapi.models.FapiConfig();
+ if (apiModel == null) {
+ return oauthModel;
+ }
+ oauthModel.setEnabled(Boolean.TRUE.equals(apiModel.getEnabled()));
+ List apiProfiles = apiModel.getSupportedProfiles();
+ if (apiProfiles == null || apiProfiles.isEmpty()) {
+ oauthModel.setSupportedProfiles(Collections.emptyList());
+ } else {
+ oauthModel.setSupportedProfiles(
+ apiProfiles.stream()
+ .map(p -> org.wso2.carbon.identity.oauth2.fapi.models.FapiProfileEnum.fromValue(
+ p.value()))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ }
+ return oauthModel;
+ }
+}
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 8b73a9372e..ad63502c10 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
@@ -26,6 +26,7 @@
import org.wso2.carbon.identity.api.server.configs.v1.model.CORSPatch;
import org.wso2.carbon.identity.api.server.configs.v1.model.CompatibilitySettings;
import org.wso2.carbon.identity.api.server.configs.v1.model.DCRPatch;
+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.InboundAuthOAuth2Config;
@@ -127,6 +128,12 @@ public Response getDCRConfiguration() {
}
+ @Override
+ public Response getFAPIConfiguration() {
+
+ return Response.ok().entity(configManagementService.getFAPIConfiguration()).build();
+ }
+
@Override
public Response getRemoteLoggingConfig(String logType) {
@@ -169,6 +176,12 @@ public Response patchDCRConfiguration(List dcrPatch) {
return Response.ok().build();
}
+ @Override
+ public Response updateFAPIConfiguration(final FapiConfig fapiConfig) {
+
+ return Response.ok().entity(configManagementService.updateFAPIConfiguration(fapiConfig)).build();
+ }
+
@Override
public Response restoreServerRemoteLoggingConfiguration(String logType) {
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 79d41a9615..aa778f5b02 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
@@ -1497,6 +1497,89 @@ paths:
schema:
$ref: '#/components/schemas/Error'
+ /configs/fapi:
+ get:
+ tags:
+ - FAPI Configurations
+ summary: Retrieve the tenant FAPI configuration.
+ operationId: getFAPIConfiguration
+ description: |
+ Retrieve the tenant Financial-grade API (FAPI) configuration.
+ Scope (Permission) required: `internal_config_view`
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FapiConfig'
+ '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: Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ put:
+ tags:
+ - FAPI Configurations
+ summary: Put the tenant FAPI configuration.
+ operationId: putFAPIConfiguration
+ description: |
+ Put the tenant Financial-grade API (FAPI) configuration.
+ Scope (Permission) required:
+ * internal_config_update
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FapiConfig'
+ required: true
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/FapiConfig'
+ '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: Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+
/configs/compatibility-settings:
get:
tags:
@@ -2115,6 +2198,13 @@ components:
type: boolean
description: If true, a FAPI compliant app will be create with DCR create request.
example: true
+ fapiProfile:
+ description: |
+ The FAPI security profile to enforce for applications created via DCR. Applicable only when enableFapiEnforcement is true.
+ If enableFapiEnforcement is true and this property is omitted, `FAPI1_ADVANCED` will be applied by default.
+ allOf:
+ - $ref: '#/components/schemas/FapiProfile'
+ example: FAPI1_ADVANCED
mandateSSA:
type: boolean
description: If true, the software_statement parameter is mandatory for the DCR create request.
@@ -2508,3 +2598,32 @@ components:
type: string
description: The application resource ID.
example: "74070bae-df8c-42bf-8754-5173c237c936"
+ FapiProfile:
+ type: string
+ description: |
+ FAPI security profile.
+ - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile.
+ - `FAPI2_SECURITY`: FAPI 2.0 Security Profile.
+ enum:
+ - FAPI1_ADVANCED
+ - FAPI2_SECURITY
+ FapiConfig:
+ type: object
+ description: Financial-grade API (FAPI) configuration for the tenant.
+ properties:
+ enabled:
+ type: boolean
+ description: Indicates whether FAPI compliance enforcement is enabled for the tenant.
+ example: false
+ default: false
+ supportedProfiles:
+ type: array
+ description: |
+ List of FAPI security profiles supported by the tenant.
+ - `FAPI1_ADVANCED`: FAPI 1.0 Advanced Security Profile (Read and Write API Security Profile).
+ - `FAPI2_SECURITY`: FAPI 2.0 Security Profile (baseline profile for high-security OAuth 2.0 APIs).
+ items:
+ $ref: '#/components/schemas/FapiProfile'
+ example:
+ - FAPI1_ADVANCED
+ - FAPI2_SECURITY