Imports Neo application IdP trust configuration into Cloud Foundry subaccount from .migration/neo-trust-config.json. Automates IAS SAML trusts via XSUAA API, role collections via BTP CLI; generates manual checklists for third-party IdPs.
npx claudepluginhub sap-samples/btp-neo-java-app-migration --plugin sap-btp-neo-migrationThis skill is limited to using the following tools:
Import Neo application IdP trust configuration into a Cloud Foundry subaccount.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Import Neo application IdP trust configuration into a Cloud Foundry subaccount.
This skill reads the structured trust export produced by subaccount-trust-export and creates the equivalent trust configuration in a CF subaccount. It:
btp security api-credential createapiaccess REST API (POST /sap/rest/identity-providers)equals attribute conditions via BTP CLI.migration/neo-trust-import-report.jsonScope: Application IdPs only. This skill mirrors the scope of
subaccount-trust-export— it configures identity providers used for end-user authentication to applications. Platform IdP configuration (used for BTP cockpit and CLI access) is out of scope.
IAS IdPs only — automated via XSUAA SAML trust API. SAML trust with IAS tenants is created via the internal XSUAA
apiaccessREST API, not thebtpCLI. Thebtp create security/trustcommand handles OIDC only; this skill uses the SAML path which is what Neo uses. Third-party IdPs (e.g. Microsoft Azure AD) cannot be directly registered as application IdPs in a CF subaccount — they must be configured as corporate IdPs inside an IAS tenant, which then acts as a proxy. This skill will flag all third-party IdPs as manual steps.
IAS-side SP registration is manual. After this skill creates SAML trust in XSUAA (registering IAS as the IdP), you must also register XSUAA as a Service Provider (SP) in the IAS admin console. This skill will fetch the XSUAA SAML metadata URL and provide exact instructions, but cannot automate the IAS-side configuration.
subaccount-trust-export must have run — .migration/neo-trust-config.json must exist
BTP CLI installed and logged in:
btp target
If not logged in, instruct the user to run:
btp login --sso
Do not prompt for credentials — only SSO login is supported to avoid credentials passing through the LLM.
curl and jq available on PATH — used for REST API calls and JSON parsing
Target CF subaccount ID — the GUID of the CF subaccount to import trust into (find in BTP cockpit > subaccount Overview, or via btp list accounts/subaccount)
No Neo credentials are required for this skill — it only reads from the local export file and writes to CF via the XSUAA REST API and BTP CLI.
0a. Read the export file:
cat .migration/neo-trust-config.json
If the file does not exist, stop and instruct the user to run subaccount-trust-export first.
0b. Check for existing CF config:
if [ -f .migration/cf-migration-config.json ]; then
cat .migration/cf-migration-config.json
fi
Read cfSubaccountId and cfRegion if present.
0c. Check the user's prompt for any values provided inline.
0d. Ask the user for any values still missing:
btp list accounts/subaccount)0e. Save CF config if not already present, using the template from assets/cf-trust-config-template.json.
1a. Verify BTP CLI session:
btp target
If this fails or shows no target, stop and tell the user:
"No active BTP CLI session found. Please run
btp login --ssoin your terminal, then re-invoke this skill."
1b. Validate the export file has IdPs to process:
Read identityProviders[] from the export. If empty, inform the user:
"The Neo subaccount uses the default SAP identity provider (accounts.sap.com) with no custom IdPs configured. No trust import is needed."
Then stop — this is a successful no-op.
1c. Check configurationType:
Read configurationType from the export:
"Custom" — subaccount has custom IdP configuration; proceed with import"Default" or absent — subaccount uses the platform default IdP; no import needed, stopFor each IdP in identityProviders[], classify into one of three queues:
| Condition | Queue |
|---|---|
enabled == false | skip — disabled in Neo, not imported |
type == "IAS" | automate — create SAML trust via XSUAA apiaccess API |
type == "ThirdParty" | manual — cannot automate; add to checklist |
Keep track of which IdP was the defaultIdentityProviderName in the export's localServiceProvider — it will need to be set as default after import (manually, in BTP Cockpit).
The XSUAA SAML trust API requires credentials from a BTP security/api-credential — this is an XSUAA service instance with the apiaccess plan, managed at the BTP account level.
3a. Check for existing credentials:
btp --format json list security/api-credential --subaccount "${CF_SUBACCOUNT_ID}" 2>/dev/null | \
jq -r '.[] | select(.name == "migration-api-credential") | .name'
3b. Delete stale credential if it exists (to ensure fresh secrets):
btp delete security/api-credential \
--name migration-api-credential \
--subaccount "${CF_SUBACCOUNT_ID}" \
--force 2>/dev/null || true
3c. Create a fresh credential:
API_CRED=$(btp --format json create security/api-credential \
--name migration-api-credential \
--subaccount "${CF_SUBACCOUNT_ID}")
Extract fields from the JSON output:
CLIENT_ID=$(echo "$API_CRED" | jq -r '.clientid')
CLIENT_SECRET=$(echo "$API_CRED" | jq -r '.clientsecret')
TOKEN_URL=$(echo "$API_CRED" | jq -r '.tokenurl')
API_URL=$(echo "$API_CRED" | jq -r '.apiurl')
If jq is unavailable, parse the JSON fields manually from the output.
Security note: These credentials are temporary and will be deleted at the end of this skill (Step 8). They are valid only for this migration session.
3d. Acquire a Bearer token from XSUAA:
export MSYS_NO_PATHCONV=1
TOKEN_RESPONSE=$(curl -s -X POST "${TOKEN_URL}" \
-u "${CLIENT_ID}:${CLIENT_SECRET}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}&grant_type=client_credentials")
BEARER_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token // empty')
If BEARER_TOKEN is empty, show the error response and stop.
3e. Derive the XSUAA tenant URL (needed for XSUAA SP metadata):
The XSUAA tenant URL is the tokenurl with the /oauth/token path stripped:
XSUAA_TENANT_URL=$(echo "$TOKEN_URL" | sed 's|/oauth/token||')
For example: https://my-subdomain.authentication.eu10.hana.ondemand.com
For each IdP in the automate queue:
The IAS SAML metadata is publicly available — no authentication required:
IAS_HOST="${idp.iasHost}" # e.g. mytenant.accounts.ondemand.com
export MSYS_NO_PATHCONV=1
IAS_METADATA=$(curl -s "https://${IAS_HOST}/saml2/metadata")
Verify the response is valid XML (starts with <?xml or <md:EntitiesDescriptor or <EntityDescriptor). If not, mark this IdP as failed and continue.
Construct the JSON payload for the XSUAA trust API:
IDP_NAME="${idp.name}"
TENANT_NAME=$(echo "$IAS_HOST" | cut -d. -f1) # e.g. "mytenant" from "mytenant.accounts.ondemand.com"
ORIGIN_KEY="${TENANT_NAME}.migrated"
PAYLOAD=$(cat <<EOF
{
"name": "Neo migrated IAS Tenant",
"type": "saml",
"originKey": "${ORIGIN_KEY}",
"isActive": true,
"config": {
"idpEntityAlias": "${IAS_HOST}",
"metaDataLocation": $(echo "$IAS_METADATA" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read()))"),
"addShadowUserOnLogin": true,
"attributeMappings": {
"given_name": "first_name",
"family_name": "last_name",
"email": "mail"
}
}
}
EOF
)
Note on
metaDataLocation: The IAS metadata XML must be embedded as a JSON string (with quotes escaped). Thepython3 -ccall above handles this encoding. If python3 is unavailable, usejq -Rs .instead:META_ENCODED=$(echo "$IAS_METADATA" | jq -Rs .)Then embed
$META_ENCODEDdirectly in the JSON without the outer quotes from thecatheredoc.
export MSYS_NO_PATHCONV=1
TRUST_RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST "${API_URL}/sap/rest/identity-providers" \
-H "Authorization: Bearer ${BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-d "${PAYLOAD}")
HTTP_STATUS=$(echo "$TRUST_RESPONSE" | tail -1 | tr -d '\r')
RESPONSE_BODY=$(echo "$TRUST_RESPONSE" | sed '$d')
Handle responses:
| HTTP Status | Action |
|---|---|
200 or 201 | Mark as imported in the report |
409 Conflict | Mark as already_configured — treat as success (trust already exists with this originKey) |
| Any other status | Mark as failed, capture $RESPONSE_BODY as error, continue with remaining IdPs |
The token expires after ~12 hours but migrations are typically fast. If you receive a 401 on the trust API call, re-fetch the token (Step 3d) and retry once.
For each successfully imported IAS IdP, check its assertionBasedGroups array.
For each group entry where at least one rule has operation == "equals":
5a. Create a role collection for the group (skip if it already exists):
btp create security/role-collection "${GROUP_NAME}-Group" \
--subaccount "${CF_SUBACCOUNT_ID}" 2>/dev/null || true
5b. Assign each equals rule as an attribute condition:
btp assign security/role-collection "${GROUP_NAME}-Group" \
--to-user-attribute "${ASSERTION_ATTRIBUTE}" \
--attribute-value "${VALUE}" \
--of-idp "${ORIGIN_KEY}" \
--subaccount "${CF_SUBACCOUNT_ID}"
Where:
GROUP_NAME = assertionBasedGroups[].groupASSERTION_ATTRIBUTE = assertionBasedGroups[].rules[].assertionAttributeVALUE = assertionBasedGroups[].rules[].valueORIGIN_KEY = the originKey used when creating the trust (e.g. mytenant.migrated)Rules with operation == "regexp" cannot be automated — add them to the manual checklist.
Note: The flag for specifying the IdP origin in role-collection assignment is
--of-idp, not--origin.
Collect all items that could not be automated:
| Condition | Manual Step |
|---|---|
| Any IAS IdP was imported | "Register XSUAA as Service Provider in IAS — for each imported IAS IdP, open the IAS admin console (https://{iasHost}/admin), navigate to Applications > Create Application > Upload Metadata, and upload XSUAA's SP metadata from: {XSUAA_TENANT_URL}/saml/metadata" |
| Any IAS IdP was imported | "Set default IdP — the btp CLI cannot set a default IdP. If {defaultIdentityProviderName} should be the default, go to BTP Cockpit → subaccount → Security → Trust Configuration → find {defaultIdentityProviderName} → Edit → enable 'Default Identity Provider'." |
Any ThirdParty IdP | "{name} is a third-party IdP and cannot be directly configured in a CF subaccount. To migrate it: (1) Configure it as a corporate IdP inside your IAS tenant via the IAS admin console, (2) Ensure the IAS tenant is trusted in this subaccount, (3) Route authentication through IAS." |
Any IdP with assertionBasedAttributes non-empty | "{name}: {N} assertion-based attribute mapping(s) must be configured in the IAS admin console under Applications > {app} > Authentication > Attributes." |
Any IdP with defaultAttributes non-empty | "{name}: {N} default attribute(s) must be configured in the IAS admin console under Applications > {app} > Authentication > Default Attributes." |
Any regexp group rules | "{name} / group {group}: assertion-based group rule with regexp operation cannot be automated — configure it manually in the IAS admin console under Applications > {app} > Authentication > Groups." |
Any IdP with onlyForIdpInitiatedSSO == true | "{name}: IdP-initiated SSO requires explicit configuration in IAS. Verify the application's SSO endpoint is registered and the flow is enabled." |
Any IdP with signatureAlgorithm == "SHA-1" | "{name}: uses SHA-1 signature algorithm. Consider upgrading to SHA-256 in the IAS admin console." |
| Any disabled IdP that was skipped | "{name}: was disabled in Neo and was not imported. Enable it manually in CF trust configuration if needed." |
Save to .migration/neo-trust-import-report.json using the Write tool:
{
"targetSubaccount": "<CF subaccount GUID>",
"importTimestamp": "<ISO 8601 timestamp>",
"sourceFile": ".migration/neo-trust-config.json",
"xsuaaTenantUrl": "<XSUAA_TENANT_URL>",
"imported": [
{
"name": "<idp-name>",
"iasHost": "<host>",
"originKey": "<tenant>.migrated",
"groupCollectionsCreated": ["<group>-Group"]
}
],
"alreadyConfigured": [
{
"name": "<idp-name>",
"iasHost": "<host>",
"originKey": "<tenant>.migrated"
}
],
"skipped": [
{
"name": "<idp-name>",
"reason": "disabled in Neo"
}
],
"failed": [
{
"name": "<idp-name>",
"error": "<error message>"
}
],
"manualSteps": [
"<step 1>",
"<step 2>"
],
"requiresManualSteps": true
}
Delete the temporary API credential created in Step 3:
btp delete security/api-credential \
--name migration-api-credential \
--subaccount "${CF_SUBACCOUNT_ID}" \
--force
Inform the user: "Temporary XSUAA API credentials have been deleted."
If deletion fails, warn the user:
"Warning: Could not delete the temporary API credential
migration-api-credential. Please delete it manually in BTP Cockpit → subaccount → Security → API credentials, or run:btp delete security/api-credential --name migration-api-credential --subaccount ${CF_SUBACCOUNT_ID} --force"
After saving the report and cleaning up, display a summary to the user:
Trust Import Complete
=====================
Target subaccount: <ID>
Source: .migration/neo-trust-config.json
Results:
Imported: <count>
Already configured: <count>
Skipped (disabled): <count>
Failed: <count>
[For each imported IdP:]
✓ <name> (<iasHost>) — origin key: <tenant>.migrated
Role collections created: <list or "none">
[For each failed IdP:]
✗ <name>: <error>
Manual Steps Required: <yes/no>
[If yes:]
The following items could not be automated and require manual configuration:
1. <step>
2. <step>
...
XSUAA SP metadata URL (needed for IAS registration):
<XSUAA_TENANT_URL>/saml/metadata
Report saved to: .migration/neo-trust-import-report.json
Temporary API credentials: deleted
| File | Location | Purpose |
|---|---|---|
neo-trust-config.json | .migration/ | Input — trust export from subaccount-trust-export |
cf-migration-config.json | .migration/ | CF target subaccount details |
neo-trust-import-report.json | .migration/ | Output — import results and manual checklist |
After the skill completes, verify trust was created via BTP CLI:
btp --format json list security/trust --subaccount <CF_SUBACCOUNT_ID>
Verify via XSUAA API (using a fresh token if needed):
curl -s "${API_URL}/sap/rest/identity-providers" \
-H "Authorization: Bearer ${BEARER_TOKEN}" | jq '.[].originKey'
Verify role collections were created:
btp list security/role-collection --subaccount <CF_SUBACCOUNT_ID>
Cause: BTP CLI is not installed or not on PATH. Solution: Download and install from SAP BTP Tools. Ensure it is on PATH.
Cause: BTP CLI session has expired.
Solution: Run btp login --sso in your terminal. The skill will not prompt for credentials.
btp create security/api-credential fails with "not found"Cause: The security api-credential sub-command requires BTP CLI 2.x. Older versions may use a different path.
Solution: Update BTP CLI to the latest version. Alternatively, create an xsuaa service instance with apiaccess plan manually in CF and obtain the service key — extract clientid, clientsecret, url (→ tokenurl), and apiurl from the binding credentials.
Cause: The API credential was already deleted or expired. Solution: Re-run from Step 3 — delete and recreate the credential.
Cause: A SAML trust with the same originKey already exists in this subaccount.
Solution: This is treated as success — the existing trust is left unchanged and marked already_configured in the report. If you need to update it, delete the existing trust in BTP Cockpit first.
Cause: Wrong IAS host, or the IAS tenant does not exist.
Solution: Verify the iasHost in .migration/neo-trust-config.json. Test manually: curl https://<iasHost>/saml2/metadata. The endpoint is public and requires no authentication.
Cause: The IAS metadata XML failed validation in XSUAA (e.g. encoding issue, truncated response).
Solution: Save the metadata to a file and inspect it: curl -o /tmp/ias-meta.xml https://<iasHost>/saml2/metadata. Ensure it is complete and starts with <?xml.
Cause: A role collection with that name already exists. Solution: The skill continues — existing role collections are not modified.
--of-idp flag not recognizedCause: The role-collection assignment command uses --of-idp for custom IdP origins. An older BTP CLI may not support this flag.
Solution: Update BTP CLI to the latest version. Alternatively, configure attribute-based assignment manually in BTP Cockpit.
After completing this skill:
subaccount-roles skill — export Neo roles and create CF role collections; group collections created by this skill can be extended with application roles