Migration Guide: v3 → v4
This guide helps you migrate your Java application from AMS Client Library version 3.x to version 4.x.
Recommendation
We recommend all applications to upgrade to version 4 to benefit from simpler customization, stream-lined authorization strategies and security updates. Version 3 will not receive new features and will only get critical bug fixes until its end of maintenance.
Overview of Changes
Version 4 introduces significant changes to the API, lifecycle objects and dependency modules. However, breaking changes for CAP applications and Spring Boot applications are limited compared to plain Java integrations due to the annotation-based authorization checks.
Lifecycle Object Changes
The core API changed from PolicyDecisionPoint, Attributes and AttributesProcessor to the following interfaces:
Authorizations: Main API used for performing authorization checks
(1 instance per request context)AuthorizationManagementService: Library instantiation, configuration, event logging
(usually 1 instance per application)AuthorizationsProvider: Gives access toAuthorizationsfor current principal, implementing official authorization strategies for different token flows with customization options
(usually 1 instance per application)
Dependency Migration
- Remove the previous AMS maven dependencies for group id
com.sap.cloud.security.ams.client.* - Add the recommended dependencies for group id
com.sap.cloud.security.ams.
* You can keep the old dcl-compiler-plugin for now. However, there will be an improved dcl-compiler-plugin available very soon.
Runtime Code Migration
Initialization and Configuration
Replace PolicyDecisionPoint initialization with AuthorizationManagementService and AuthorizationsProvider as documented here.
TIP
If you are using a Spring Boot starter, the AuthorizationManagementService and AuthorizationsProvider are auto-configured and available for injection. You can customize them by overriding the beans or configuring them.
Startup Checks
- Remove manual startup checks and configuration properties.
- Implement startup check as described here
TIP
If you use a Spring Boot starter, it automatically integrates into Spring Boot readiness. Alternatively, you can use the Spring Boot 3 or 4 starter for health actuator integration.
AttributesProcessor Removal
- Remove any implementations of the
AttributesProcessorinterface and the meta data configuration for the service loader.
Typical use cases for AttributesProcessor such as technical communication, XSUAA scope mapping or custom user attribute injection can now be implemented much simpler via AuthorizationsProvider configuration.
PolicyDecisionPoint checks
- Rewrite
PolicyDecisionPoint#allowchecks withAuthorizations#checkPrivilegeas documented here.
Example:
import com.sap.cloud.security.ams.api.Principal;
import static com.sap.cloud.security.ams.dcl.client.pdp.Attributes.Names.APP;
import static com.sap.cloud.security.ams.dcl.client.pdp.Attributes.Names.ENV;
Principal principal = Principal.create();
// definite allow
Attributes attributes =
principal
.getAttributes()
.setAction("read")
.setResource("salesOrders");
boolean allowed = policyDecisionPoint.allow(attributes);
// allow that ignores any conditions
Attributes attributes =
principal
.getAttributes()
.setAction("read")
.setResource("salesOrders")
.setIgnores(List.of(APP, ENV));
boolean allowed = policyDecisionPoint.allow(attributes);import static com.sap.cloud.security.ams.api.Principal.fromSecurityContext;
Authorizations authorizations = authProvider
.getAuthorizations(fromSecurityContext());
// definite allow
boolean allowed = authorizations
.checkPrivilege("read", "salesOrders")
.isGranted();
// allow that ignores any conditions
boolean allowed = !authorizations
.checkPrivilege("read", "salesOrders")
.isDenied();Spring Route Security
- Replace
SecurityExpressionHandlerwithAmsRouteSecurity/AmsCdsRouteSecurity(CAP) bean inSecurityFilterChain. - Update route authorization checks based on following mapping:
| v3 Route Check Syntax | AmsRouteSecurity | AmsCdsRouteSecurity |
|---|---|---|
hasBaseAuthority("read", "products") | precheckPrivilege("read", "products") | — |
hasBaseAuthority("Admin", "$SCOPES") | precheckPrivilege("Admin", "$SCOPES") | precheckRole("Admin") |
forAction("read") | checkPrivilege("read", "*") | — |
forResource("products") | checkPrivilege("*", "products") | — |
forResourceAction("products", "read") | checkPrivilege("read", "products") | — |
forResourceAction("$SCOPES", "Admin") | checkPrivilege("Admin", "$SCOPES") | checkRole("Admin") |
forResourceAction("products", "read", attributes...) | use method security | use method security |
Example:
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http,
SecurityExpressionHandler<RequestAuthorizationContext> amsHttpExpressionHandler) {
WebExpressionAuthorizationManager readOrders =
new WebExpressionAuthorizationManager("hasBaseAuthority('read', 'orders')");
readOrders.setExpressionHandler(amsHttpExpressionHandler);
WebExpressionAuthorizationManager adminRole =
new WebExpressionAuthorizationManager("forResourceAction('$SCOPES', 'Admin')");
adminRole.setExpressionHandler(amsHttpExpressionHandler);
http.authorizeHttpRequests(authz -> authz
.requestMatchers(GET, "/orders/**").access(readOrders)
.requestMatchers("/admin/**").access(adminRole));
return http.build();
}@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AmsRouteSecurity via) {
http.authorizeHttpRequests(authz -> authz
.requestMatchers(GET, "/orders/**")
.access(via.precheckPrivilege("read", "orders"))
.requestMatchers("/admin/**")
.access(via.checkPrivilege("Admin", "$SCOPES")));
return http.build();
}@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AmsCdsRouteSecurity via) {
http.authorizeHttpRequests(authz -> authz
.requestMatchers(GET, "/orders/**")
.access(via.precheckPrivilege("read", "orders"))
.requestMatchers("/admin/**")
.access(via.checkRole("Admin")));
return http.build();
}Spring Method Security
- Replace
@PreAuthorizeannotations with v3 AMS expressions by the new AMS annotations. - For methods with attributes, use
@AmsAttributeon parameters to pass them to the authorization check.
| v3 Method Security Syntax | v4 Method Security Syntax |
|---|---|
@PreAuthorize("forAction('read')") | @CheckPrivilege(action = "read", resource = "*") |
@PreAuthorize("forResource('products')") | @CheckPrivilege(action = "*", resource = "products") |
@PreAuthorize("forResourceAction('products', 'read')") | @CheckPrivilege(action = "read", resource = "products") |
@PreAuthorize("forResourceAction('products', 'read')")
public List<Product> getProducts() { ... }
@PreAuthorize("forResourceAction('products', 'read', 'product.category:string=' + #category)")
public List<Product> getProductsByCategory(@PathVariable String category) { ... }@CheckPrivilege(action = "read", resource = "products")
public List<Product> getProducts() { ... }
@CheckPrivilege(action = "read", resource = "products")
public List<Product> getProductsByCategory(@AmsAttribute(name = "product.category") String category) { ... }Test Setup Migration
DCL Output Directory
Replace the DCL output directory with the new default output directory for AMS DCN test resources in DCL compiler maven plugin.
| v3 Output | v4 Output |
|---|---|
target/dcl_opa/ | target/generated-test-resources/ams/dcn/ |
CAP Java Configuration
- Remove test sources property from
application.yaml. It is no longer used:
cds:
security:
authorization:
ams:
test-sources: "" # empty uses default srv/target/dcl_opaTIP
In v4, the existence of spring-boot-starter-ams-cap-test on the classpath determines whether AMS will try to load local DCN. For this reason, make sure to keep it test-scoped.
Spring Security Tests
The MockOidcTokenRequestPostProcessor.userWithPolicies from jakarta-ams-test has been removed because now, the full AMS production code can be tested including the real AuthorizationProvider. It requires the definition of a policy assignments map from which AMS determines the used policies based on the app_tid and scim_id claims of the token, and for advanced token flows: other claims as needed.