Skip to main content

authentication-xsuaa

Invoke this skill to set up XSUAA authentication and Application Router. Detects <auth-method>FORM</auth-method> in web.xml, security-constraint definitions, or UserProvider usage in Java code. Replaces Neo built-in auth with CF XSUAA.

معلومات المصدر

المستودع
SAP-samples/btp-neo-java-app-migration
آخر نشاط في المصدر
٢٩ يوليو ٢٠٢٦ في ١٢:٤٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
authentication-xsuaa
description
Invoke this skill to set up XSUAA authentication and Application Router. Detects <auth-method>FORM</auth-method> in web.xml, security-constraint definitions, or UserProvider usage in Java code. Replaces Neo built-in auth with CF XSUAA.
disable-model-invocation
false
allowed-tools
Read, Edit, Write, Bash, Grep, Glob
# Authentication and Authorization Set up XSUAA-based authentication and Application Router for Cloud Foundry. ## Purpose Replace Neo's built-in FORM authentication and `UserManagementAccessor` with Cloud Foundry's XSUAA service and Application Router for secure web application access. ## Detection This skill applies if any of these patterns are found: ### In web.xml ```xml <auth-method>FORM</auth-method> <!-- OR --> <security-constraint> <web-resource-collection> <web-resource-name>Protected</web-resource-name> <url-pattern>/protected/*</url-pattern> </web-resource-collection> </security-constraint> <!-- OR --> <security-role> <role-name>Everyone</role-name> </security-role> ``` ### In Java source files ```java import com.sap.security.um.user.UserProvider; import com.sap.security.um.user.User; // OR request.getUserPrincipal(); request.isUserInRole("Everyone"); ``` ## Prerequisites > **Working directory:** This skill must run inside the `-cf-migration` copy of your app, created by `jakarta-java25-migration` or `neo-to-cf-migration-orchestrator`. If your current directory does not end in `-cf-migration`, switch to it before proceeding. Before invoking this skill, ensure you have invoked: 1. **sdk-replacement** - `Use the sdk-replacement skill` - Sets up SAP Cloud SDK - REQUIRED before this skill ## Transformation Steps ### Step 1: Create xs-security.json Create `xs-security.json` in your project root (or `cf/` folder) - see [assets/xs-security.json](assets/xs-security.json) for template: ```json { "xsappname": "${app-name}", "tenant-mode": "dedicated", "scopes": [ { "name": "$XSAPPNAME.Everyone", "description": "Everyone scope for authenticated users" } ], "role-templates": [ { "name": "Everyone", "scope-references": [ "$XSAPPNAME.Everyone" ] } ], "role-collections": [ { "name": "${app-name}-Everyone", "role-template-references": [ "$XSAPPNAME.Everyone" ] } ] } ``` > **Customize:** Replace `${app-name}` with your application name and `Everyone` with each actual role name from your Neo app. The naming convention for role collections is `<app-name>-<role-name>` (e.g. `myapp-Admin`, `myapp-Viewer`). Add one entry per role-template in the `role-collections` array. `subaccount-roles-import` reads these names to link deployed role-templates and assign users. ### Step 2: Update web.xml Authentication Method **Before:** ```xml <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/login-error.html</form-error-page> </form-login-config> </login-config> ``` **After:** ```xml <login-config> <auth-method>XSUAA</auth-method> </login-config> ``` ### Step 3: Add Security Library Dependency Add to `pom.xml`: ```xml <!-- SAP Cloud Security Java API (non-Spring applications) --> <dependency> <groupId>com.sap.cloud.security</groupId> <artifactId>java-api</artifactId> </dependency> <!-- For Spring Boot applications, use instead: <dependency> <groupId>com.sap.cloud.security</groupId> <artifactId>resourceserver-security-spring-boot-starter</artifactId> </dependency> --> ``` > **Note:** The `java-api` artifact is managed by the `cf-tomcat-bom` BOM, so no version is needed. ### Step 4: Create Application Router Create `approuter/` directory with these files: #### approuter/package.json Copy from [assets/package.json](assets/package.json): ```json { "name": "approuter", "dependencies": { "@sap/approuter": "^16.0.0" }, "scripts": { "start": "node node_modules/@sap/approuter/approuter.js" } } ``` #### approuter/xs-app.json See [assets/xs-app.json](assets/xs-app.json) for a complete template: ```json { "authenticationMethod": "route", "routes": [ { "source": "^/protected(/.*)?$", "target": "/protected$1", "destination": "backend-app-destination", "authenticationType": "xsuaa", "scope": "$XSAPPNAME.Everyone", "csrfProtection": false }, { "source": "^/(.*)$", "target": "/$1", "destination": "backend-app-destination", "authenticationType": "none", "csrfProtection": false } ], "logout": { "logoutEndpoint": "/logout", "logoutPage": "/" } } ``` > **Scopes in routes:** Use `"scope": "$XSAPPNAME.<ScopeName>"` on each protected route to enforce XSUAA scope checks at the approuter level. The `$XSAPPNAME` placeholder is resolved at runtime to the bound XSUAA service's `xsappname`. Routes without a `scope` field only require authentication (when `authenticationType` is `xsuaa`). ### Step 5: Update User Access Code #### Neo UserProvider → CF XSUAA Token/TokenClaims mapping | Neo `UserProvider` / `User` API | CF XSUAA equivalent | |----------------------------------|---------------------| | `user.getName()` | `principal.getName()` — returns `user_uuid` (IAS) or `user_name` (XSUAA) | | `user.getAttribute("email")` | `token.getClaimAsString(TokenClaims.EMAIL)` | | `user.getAttribute("firstname")` | `token.getClaimAsString(TokenClaims.GIVEN_NAME)` | | `user.getAttribute("lastname")` | `token.getClaimAsString(TokenClaims.FAMILY_NAME)` | | `user.getAttribute("displayName")` | `token.getClaimAsString(TokenClaims.SAP_GLOBAL_USER_DISPLAY_NAME)` | | `user.getId()` (unique ID) | `token.getClaimAsString(TokenClaims.SAP_GLOBAL_USER_ID)` | | `request.isUserInRole("Admin")` | `request.isUserInRole("Admin")` — unchanged, maps to XSUAA scope | | Neo tenant / account name | `token.getClaimAsString(TokenClaims.SAP_GLOBAL_ZONE_ID)` | | `user.getAttribute("logon_name")` | `token.getClaimAsString(TokenClaims.USER_NAME)` | | XSUAA scopes list | `token.getClaimAsStringList(TokenClaims.XSUAA.SCOPES)` — XSUAA-specific, not available for IAS tokens | **Before (Neo):** ```java import com.sap.security.um.user.UserProvider; import com.sap.security.um.user.User; @Resource private UserProvider userProvider; public void doGet(HttpServletRequest request, HttpServletResponse response) { User user = userProvider.getUser(request); String userName = user.getName(); String email = user.getAttribute("email"); String firstName = user.getAttribute("firstname"); boolean isAdmin = request.isUserInRole("Admin"); } ``` **After (Cloud Foundry — in JAX-RS endpoints or servlets):** ```java import com.sap.cloud.security.token.SecurityContext; import com.sap.cloud.security.token.Token; import com.sap.cloud.security.token.TokenClaims; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.Principal; import java.util.List; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. Basic identity — works with servlet security constraint in web.xml Principal principal = request.getUserPrincipal(); if (principal == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } String userName = principal.getName(); // user_uuid or user_name depending on IdP // 2. Read claims from the JWT token for richer user attributes Token token = SecurityContext.getToken(); if (token != null) { String email = token.getClaimAsString(TokenClaims.EMAIL); String firstName = token.getClaimAsString(TokenClaims.GIVEN_NAME); String lastName = token.getClaimAsString(TokenClaims.FAMILY_NAME); String userId = token.getClaimAsString(TokenClaims.SAP_GLOBAL_USER_ID); String logonName = token.getClaimAsString(TokenClaims.USER_NAME); // XSUAA scopes — only available for XSUAA tokens, null for IAS tokens List<String> scopes = token.getClaimAsStringList(TokenClaims.XSUAA.SCOPES); // Tenant (zone ID) — identifies the subaccount String zoneId = token.getClaimAsString(TokenClaims.SAP_GLOBAL_ZONE_ID); } // 3. Role / scope check — unchanged from Neo boolean isAdmin = request.isUserInRole("Admin"); } ``` > **Why `SecurityContext.getToken()`?** The SAP Java Buildpack's `XSSecurityAuthenticator` Catalina valve validates the JWT and stores the parsed `Token` object in a thread-local via `SecurityContext`. This is the correct and recommended way to access the token. `request.getAttribute(Token.class.getName())` does NOT work in this context. > **`Token` vs `Principal`:** `request.getUserPrincipal().getName()` is sufficient for identifying the user. Only reach for `Token` when you need claims that are not exposed through the standard servlet API — email, given name, family name, zone ID. > **Null safety:** `token.getClaimAsString(...)` returns `null` if the claim is absent (e.g., the IdP did not include it). Always null-check before use in production code. ### Step 6: Create MTA Descriptor with Approuter Create or update `mtad.yaml`: ```yaml _schema-version: "3.2" version: 0.0.1 ID: ${app-name} parameters: enable-parallel-deployments: true modules: # Java Backend Application - name: ${app-name} type: java.tomcat path: target/<artifactId>.war # substitute literal artifactId from pom.xml; app serves at / parameters: buildpack: sap_java_buildpack_jakarta disk-quota: 1024M memory: 1024M properties: ENABLE_SECURITY_JAVA_API_V2: true JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']" JBP_CONFIG_SAP_MACHINE_JRE: "{ version: 25.+ }" TARGET_RUNTIME: tomcat SET_LOGGING_LEVEL: 'ROOT: INFO' requires: - name: ${app-name}-xsuaa - name: ${app-name}-destination provides: - name: ${app-name}-java-app properties: neo-app-url: '${default-url}' # Application Router - name: ${app-name}-approuter type: nodejs path: approuter parameters: disk-quota: 256M memory: 256M routes: - route: '${protocol}://${app-name}.${default-domain}' protocol: http1 properties: XS_APP_LOG_LEVEL: debug TENANT_HOST_PATTERN: '(.*).cfapps.sap.hana.ondemand.com' CF_NODEJS_LOGGING_LEVEL: "info" requires: - name: ${app-name}-xsuaa - name: ${app-name}-java-app group: destinations properties: name: backend-app-destination url: '~{neo-app-url}' forwardAuthToken: true resources: # XSUAA Service - name: ${app-name}-xsuaa type: org.cloudfoundry.managed-service parameters: service: xsuaa service-plan: application path: ./xs-security.json # Destination Service - name: ${app-name}-destination type: org.cloudfoundry.managed-service parameters: service: destination service-plan: lite ``` > **Key points:**
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub