@@ -1318,6 +1383,9 @@ class TokenManager extends React.Component {
callbackError={hasError}
setValidating={this.setValidating}
defaultTokenEndpoint={defaultTokenEndpoint}
+ formConfig={this.props.formConfig?.keyManagers?.[keymanager.name]
+ ? { keyGeneration: this.props.formConfig.keyManagers[keymanager.name] }
+ : null}
/>
{key ? 'Update keys' : 'Generate Keys'}
@@ -1354,6 +1423,9 @@ class TokenManager extends React.Component {
color='primary'
className={classes.button}
onClick={key ? this.updateKeys : this.handleGenerateKeysClick}
+ disabled={!hasSelectedGrantTypes || hasError
+ || (isLoading || !keymanager.enableOAuthAppCreation)
+ || (mode && mode === 'MAPPED')}
>
{key ? 'Update' : 'Generate Keys'}
@@ -1383,7 +1455,7 @@ class TokenManager extends React.Component {
>
)}
{(tokenType === 'EXCHANGED' && isResidentKeyManagerTokensAvailable) && (
-
+
{ },
summary: false,
+ formConfig: null,
};
TokenManager.propTypes = {
classes: PropTypes.instanceOf(Object).isRequired,
@@ -1497,6 +1570,7 @@ TokenManager.propTypes = {
updateSubscriptionData: PropTypes.func,
intl: PropTypes.shape({ formatMessage: PropTypes.func }).isRequired,
summary: PropTypes.bool,
+ formConfig: PropTypes.shape({}),
};
export default injectIntl((TokenManager));
diff --git a/portals/devportal/src/main/webapp/source/src/app/data/AuthManager.jsx b/portals/devportal/src/main/webapp/source/src/app/data/AuthManager.jsx
index b52b3dba61c..63be2e060f6 100644
--- a/portals/devportal/src/main/webapp/source/src/app/data/AuthManager.jsx
+++ b/portals/devportal/src/main/webapp/source/src/app/data/AuthManager.jsx
@@ -318,7 +318,7 @@ class AuthManager {
AuthManager.CONST = {
USER_SCOPES:
'apim:api_key apim:app_manage apim:app_update apim:dedicated_gateway apim:self-signup '
- + 'apim:store_settings apim:sub_alert_manage apim:sub_manage apim:subscribe openid',
+ + 'apim:gov_template_read apim:store_settings apim:sub_alert_manage apim:sub_manage apim:subscribe openid',
};
export default AuthManager;
diff --git a/portals/devportal/src/main/webapp/source/src/app/data/api.jsx b/portals/devportal/src/main/webapp/source/src/app/data/api.jsx
index fd18d76770c..bc87c780641 100644
--- a/portals/devportal/src/main/webapp/source/src/app/data/api.jsx
+++ b/portals/devportal/src/main/webapp/source/src/app/data/api.jsx
@@ -1286,4 +1286,88 @@ export default class API extends Resource {
return client.apis.Users.organizationInformation(this._requestMetaData());
});
}
+
+ /**
+ * Fetch published Devportal Governance Templates from the governance REST API.
+ * The governance API lives outside the devportal swagger spec, so this method
+ * uses a direct fetch() with the same auth headers the swagger-client interceptor uses.
+ * Only PUBLISHED templates are returned; the list is empty on any error so callers
+ * can fail open to the un-governed form.
+ *
+ * @param {Object} params - Optional query params: { limit, offset }
+ * @returns {Promise<{body: {list: Array}}>} Resolves to swagger-client-shaped response
+ */
+ getDevportalGovernanceTemplates(params = {}) {
+ const token = Utils.getCookie('WSO2_AM_TOKEN_1', Utils.getEnvironment().label) || '';
+
+ const queryParams = new URLSearchParams({
+ limit: params.limit ?? 25,
+ offset: params.offset ?? 0,
+ });
+
+ // Mirror the tenant header logic from APIClient._getRequestInterceptor
+ const headers = {
+ Accept: 'application/json',
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ };
+ if (window.location) {
+ const search = new URLSearchParams(window.location.search);
+ const tenant = search.get('tenant');
+ if (tenant) headers['X-WSO2-Tenant'] = tenant;
+ }
+
+ return fetch(`/api/am/governance/v1/templates?${queryParams}`, {
+ credentials: 'include',
+ headers,
+ })
+ .then((res) => {
+ if (!res.ok) {
+ const err = new Error(res.statusText);
+ err.status = res.status;
+ throw err;
+ }
+ return res.json();
+ })
+ .then((json) => ({
+ body: {
+ list: (json.list ?? []).filter((t) => t.status === 'PUBLISHED'),
+ count: json.count ?? 0,
+ },
+ }));
+ }
+
+ /**
+ * Fetch a single Devportal Governance Template by ID.
+ * Used by the DevPortal to load the formConfig for an existing application's template.
+ *
+ * @param {string} templateId - The template UUID
+ * @returns {Promise<{body: Object}>} Resolves to swagger-client-shaped response with the template DTO
+ */
+ getDevportalGovernanceTemplateById(templateId) {
+ const token = Utils.getCookie('WSO2_AM_TOKEN_1', Utils.getEnvironment().label) || '';
+
+ const headers = {
+ Accept: 'application/json',
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ };
+ if (window.location) {
+ const search = new URLSearchParams(window.location.search);
+ const tenant = search.get('tenant');
+ if (tenant) headers['X-WSO2-Tenant'] = tenant;
+ }
+
+ return fetch(`/api/am/governance/v1/templates/${encodeURIComponent(templateId)}`, {
+ credentials: 'include',
+ headers,
+ })
+ .then((res) => {
+ if (!res.ok) {
+ const err = new Error(res.statusText);
+ err.status = res.status;
+ throw err;
+ }
+ return res.json();
+ })
+ .then((json) => ({ body: json }));
+ }
}