From ec70b583a4365c5d22ab19c0ef8163d81a24c975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamila=20=C5=9Aroda?= Date: Wed, 6 May 2026 15:09:20 +0200 Subject: [PATCH 1/2] chore(samples/java): bump to Spring Boot 4.0.6 + spring-dotenv 5.1.0 + keytool 2.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings all 3 Java samples to the latest GA versions of every dep: - spring-boot-starter-parent 3.4.1 → 4.0.6 (latest GA — 4.1.x is RC) - keytool-maven-plugin 1.7 → 2.0.2 (latest) - me.paulschwarz:spring-dotenv 4.0.0 → springboot4-dotenv 5.1.0 (v5 split the artifact by Boot version; we now use the Boot 4 module) Required code changes for the Spring Boot 4 / Spring Security 7 jump: - AutoConfigureMockMvc moved from spring-boot-test-autoconfigure to a new spring-boot-webmvc-test module (added as test-scope dep) and changed package: org.springframework.boot.test.autoconfigure.web.servlet → org.springframework.boot.webmvc.test.autoconfigure. - Spring Security 7 dropped OpenSAML 4: OpenSaml4AuthenticationProvider → OpenSaml5AuthenticationProvider (same API surface, just renamed). - Spring Security 7 removed AntPathRequestMatcher from o.s.s.web.util.matcher: replaced with PathPatternRequestMatcher.withDefaults().matcher(path) at o.s.s.web.servlet.util.matcher (matches any HTTP method when no method is provided, matching the existing "GET on /logout" behavior). snippets.json regenerated because line numbers in saml-sp-login's Application.java shifted due to the matcher comment update. 11/11 tests pass across all 3 samples on the new stack: login-auth-code: 3/3, token-refresh: 5/5, saml-sp-login: 3/3. Co-Authored-By: Claude Opus 4.7 (1M context) --- samples/java/login-auth-code/pom.xml | 15 +++++++++++---- .../secureauth/quickstart/ApplicationTests.java | 2 +- samples/java/saml-sp-login/pom.xml | 15 +++++++++++---- .../com/secureauth/quickstart/Application.java | 14 ++++++++------ .../secureauth/quickstart/ApplicationTests.java | 2 +- samples/java/token-refresh/pom.xml | 15 +++++++++++---- .../secureauth/quickstart/ApplicationTests.java | 2 +- snippets.json | 12 ++++++------ 8 files changed, 50 insertions(+), 27 deletions(-) diff --git a/samples/java/login-auth-code/pom.xml b/samples/java/login-auth-code/pom.xml index 6b2d646..b5400d6 100644 --- a/samples/java/login-auth-code/pom.xml +++ b/samples/java/login-auth-code/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 3.4.1 + 4.0.6 @@ -35,8 +35,8 @@ me.paulschwarz - spring-dotenv - 4.0.0 + springboot4-dotenv + 5.1.0 @@ -44,6 +44,13 @@ spring-boot-starter-test test + + + org.springframework.boot + spring-boot-webmvc-test + test + org.springframework.security spring-security-test @@ -60,7 +67,7 @@ org.codehaus.mojo keytool-maven-plugin - 1.7 + 2.0.2 generate-dev-keystore diff --git a/samples/java/login-auth-code/src/test/java/com/secureauth/quickstart/ApplicationTests.java b/samples/java/login-auth-code/src/test/java/com/secureauth/quickstart/ApplicationTests.java index ffbcbce..11bc787 100644 --- a/samples/java/login-auth-code/src/test/java/com/secureauth/quickstart/ApplicationTests.java +++ b/samples/java/login-auth-code/src/test/java/com/secureauth/quickstart/ApplicationTests.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; diff --git a/samples/java/saml-sp-login/pom.xml b/samples/java/saml-sp-login/pom.xml index 51f2a8c..a09a06c 100644 --- a/samples/java/saml-sp-login/pom.xml +++ b/samples/java/saml-sp-login/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 3.4.1 + 4.0.6 @@ -44,8 +44,8 @@ me.paulschwarz - spring-dotenv - 4.0.0 + springboot4-dotenv + 5.1.0 @@ -53,6 +53,13 @@ spring-boot-starter-test test + + + org.springframework.boot + spring-boot-webmvc-test + test + org.springframework.security spring-security-test @@ -69,7 +76,7 @@ org.codehaus.mojo keytool-maven-plugin - 1.7 + 2.0.2 generate-dev-keystore diff --git a/samples/java/saml-sp-login/src/main/java/com/secureauth/quickstart/Application.java b/samples/java/saml-sp-login/src/main/java/com/secureauth/quickstart/Application.java index 4d701b6..fdb0292 100644 --- a/samples/java/saml-sp-login/src/main/java/com/secureauth/quickstart/Application.java +++ b/samples/java/saml-sp-login/src/main/java/com/secureauth/quickstart/Application.java @@ -23,7 +23,7 @@ import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; +import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; @@ -101,9 +101,9 @@ static class SecurityConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider(); - Converter defaultValidator = - OpenSaml4AuthenticationProvider.createDefaultResponseValidator(); + OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider(); + Converter defaultValidator = + OpenSaml5AuthenticationProvider.createDefaultResponseValidator(); provider.setResponseValidator(token -> { Saml2ResponseValidatorResult result = defaultValidator.convert(token); List filtered = result.getErrors().stream() @@ -138,7 +138,7 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception { } } final String expectedInResponseTo = assertionInResponseTo; - return OpenSaml4AuthenticationProvider.createDefaultAssertionValidatorWithParameters( + return OpenSaml5AuthenticationProvider.createDefaultAssertionValidatorWithParameters( params -> { params.put( org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters.SC_CHECK_ADDRESS, @@ -162,7 +162,9 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // requires POST + CSRF; for a quickstart we let a simple // link work directly). .logout(l -> l - .logoutRequestMatcher(new org.springframework.security.web.util.matcher.AntPathRequestMatcher("/logout")) + // Spring Security 7 dropped AntPathRequestMatcher; PathPatternRequestMatcher + // is the replacement. `.matcher(path)` (no HTTP method) matches any method. + .logoutRequestMatcher(org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults().matcher("/logout")) .logoutSuccessUrl("/")) // Disable CSRF for the demo. SAML2 ACS endpoint is already exempt by the // framework; this just removes the requirement on /logout. Production apps diff --git a/samples/java/saml-sp-login/src/test/java/com/secureauth/quickstart/ApplicationTests.java b/samples/java/saml-sp-login/src/test/java/com/secureauth/quickstart/ApplicationTests.java index c90e231..7e7d6bc 100644 --- a/samples/java/saml-sp-login/src/test/java/com/secureauth/quickstart/ApplicationTests.java +++ b/samples/java/saml-sp-login/src/test/java/com/secureauth/quickstart/ApplicationTests.java @@ -10,7 +10,7 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal; diff --git a/samples/java/token-refresh/pom.xml b/samples/java/token-refresh/pom.xml index 51050fc..a9c6ab1 100644 --- a/samples/java/token-refresh/pom.xml +++ b/samples/java/token-refresh/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 3.4.1 + 4.0.6 @@ -35,8 +35,8 @@ me.paulschwarz - spring-dotenv - 4.0.0 + springboot4-dotenv + 5.1.0 @@ -44,6 +44,13 @@ spring-boot-starter-test test + + + org.springframework.boot + spring-boot-webmvc-test + test + org.springframework.security spring-security-test @@ -60,7 +67,7 @@ org.codehaus.mojo keytool-maven-plugin - 1.7 + 2.0.2 generate-dev-keystore diff --git a/samples/java/token-refresh/src/test/java/com/secureauth/quickstart/ApplicationTests.java b/samples/java/token-refresh/src/test/java/com/secureauth/quickstart/ApplicationTests.java index 1966555..30bb700 100644 --- a/samples/java/token-refresh/src/test/java/com/secureauth/quickstart/ApplicationTests.java +++ b/samples/java/token-refresh/src/test/java/com/secureauth/quickstart/ApplicationTests.java @@ -10,7 +10,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; diff --git a/snippets.json b/snippets.json index 2bef818..427f955 100644 --- a/snippets.json +++ b/snippets.json @@ -325,7 +325,7 @@ ], "framework": "java", "lib": "spring-boot-starter-oauth2-client", - "lib_version": "3.4.1", + "lib_version": "4.0.6", "docs_url": "https://docs.spring.io/spring-security/reference/servlet/oauth2/login/index.html", "install": "", "repo_path": "samples/java/login-auth-code", @@ -483,7 +483,7 @@ ], "framework": "java", "lib": "spring-boot-starter-oauth2-client", - "lib_version": "3.4.1", + "lib_version": "4.0.6", "docs_url": "https://docs.spring.io/spring-security/reference/servlet/oauth2/login/index.html", "install": "", "repo_path": "samples/java/token-refresh", @@ -593,10 +593,10 @@ { "step": 2, "description": "Enable saml2Login and accept unsolicited (IdP-initiated) responses", - "code": " @Configuration\n static class SecurityConfig {\n\n @Bean\n SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();\n Converter defaultValidator =\n OpenSaml4AuthenticationProvider.createDefaultResponseValidator();\n provider.setResponseValidator(token -> {\n Saml2ResponseValidatorResult result = defaultValidator.convert(token);\n List filtered = result.getErrors().stream()\n .filter(e -> !Saml2ErrorCodes.INVALID_IN_RESPONSE_TO.equals(e.getErrorCode()))\n .toList();\n return filtered.isEmpty()\n ? Saml2ResponseValidatorResult.success()\n : Saml2ResponseValidatorResult.failure(filtered);\n });\n // CIAM's SubjectConfirmation interacts badly with Spring Security's strict bearer\n // validator in two ways:\n // 1. The Address attribute is host:port form (e.g. \"[::1]:57370\") rather than\n // IP-only. Disabled via SC_CHECK_ADDRESS=false.\n // 2. The session-stored AuthnRequest used to validate InResponseTo is unreliable\n // across the cross-site POST from CIAM (cookie/session loss). Worked around by\n // feeding the assertion's own InResponseTo back as the expected value, which\n // makes the comparison pass vacuously.\n // Production-grade SPs should fix the upstream Address format and rely on session\n // state for InResponseTo. For a CIAM quickstart, this is acceptable: response-level\n // signature validation (against the IdP signing cert) still runs at the response\n // validator above, so we still confirm the assertion came from CIAM.\n provider.setAssertionValidator(assertionToken -> {\n String assertionInResponseTo = null;\n var subject = assertionToken.getAssertion().getSubject();\n if (subject != null) {\n for (var sc : subject.getSubjectConfirmations()) {\n var data = sc.getSubjectConfirmationData();\n if (data != null && data.getInResponseTo() != null) {\n assertionInResponseTo = data.getInResponseTo();\n break;\n }\n }\n }\n final String expectedInResponseTo = assertionInResponseTo;\n return OpenSaml4AuthenticationProvider.createDefaultAssertionValidatorWithParameters(\n params -> {\n params.put(\n org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters.SC_CHECK_ADDRESS,\n Boolean.FALSE);\n if (expectedInResponseTo != null) {\n params.put(\n org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO,\n expectedInResponseTo);\n }\n })\n .convert(assertionToken);\n });\n return http\n .authorizeHttpRequests(auth -> auth\n .requestMatchers(\"/\").permitAll()\n .anyRequest().authenticated())\n .saml2Login(saml -> saml\n .authenticationManager(new ProviderManager(provider))\n .defaultSuccessUrl(\"/\", true))\n // Accept GET on /logout (Spring Security 6.4's default confirmation page\n // requires POST + CSRF; for a quickstart we let a simple \n // link work directly).\n .logout(l -> l\n .logoutRequestMatcher(new org.springframework.security.web.util.matcher.AntPathRequestMatcher(\"/logout\"))\n .logoutSuccessUrl(\"/\"))\n // Disable CSRF for the demo. SAML2 ACS endpoint is already exempt by the\n // framework; this just removes the requirement on /logout. Production apps\n // with state-changing endpoints should re-enable CSRF.\n .csrf(c -> c.disable())\n .build();\n }\n }", + "code": " @Configuration\n static class SecurityConfig {\n\n @Bean\n SecurityFilterChain filterChain(HttpSecurity http) throws Exception {\n OpenSaml5AuthenticationProvider provider = new OpenSaml5AuthenticationProvider();\n Converter defaultValidator =\n OpenSaml5AuthenticationProvider.createDefaultResponseValidator();\n provider.setResponseValidator(token -> {\n Saml2ResponseValidatorResult result = defaultValidator.convert(token);\n List filtered = result.getErrors().stream()\n .filter(e -> !Saml2ErrorCodes.INVALID_IN_RESPONSE_TO.equals(e.getErrorCode()))\n .toList();\n return filtered.isEmpty()\n ? Saml2ResponseValidatorResult.success()\n : Saml2ResponseValidatorResult.failure(filtered);\n });\n // CIAM's SubjectConfirmation interacts badly with Spring Security's strict bearer\n // validator in two ways:\n // 1. The Address attribute is host:port form (e.g. \"[::1]:57370\") rather than\n // IP-only. Disabled via SC_CHECK_ADDRESS=false.\n // 2. The session-stored AuthnRequest used to validate InResponseTo is unreliable\n // across the cross-site POST from CIAM (cookie/session loss). Worked around by\n // feeding the assertion's own InResponseTo back as the expected value, which\n // makes the comparison pass vacuously.\n // Production-grade SPs should fix the upstream Address format and rely on session\n // state for InResponseTo. For a CIAM quickstart, this is acceptable: response-level\n // signature validation (against the IdP signing cert) still runs at the response\n // validator above, so we still confirm the assertion came from CIAM.\n provider.setAssertionValidator(assertionToken -> {\n String assertionInResponseTo = null;\n var subject = assertionToken.getAssertion().getSubject();\n if (subject != null) {\n for (var sc : subject.getSubjectConfirmations()) {\n var data = sc.getSubjectConfirmationData();\n if (data != null && data.getInResponseTo() != null) {\n assertionInResponseTo = data.getInResponseTo();\n break;\n }\n }\n }\n final String expectedInResponseTo = assertionInResponseTo;\n return OpenSaml5AuthenticationProvider.createDefaultAssertionValidatorWithParameters(\n params -> {\n params.put(\n org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters.SC_CHECK_ADDRESS,\n Boolean.FALSE);\n if (expectedInResponseTo != null) {\n params.put(\n org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO,\n expectedInResponseTo);\n }\n })\n .convert(assertionToken);\n });\n return http\n .authorizeHttpRequests(auth -> auth\n .requestMatchers(\"/\").permitAll()\n .anyRequest().authenticated())\n .saml2Login(saml -> saml\n .authenticationManager(new ProviderManager(provider))\n .defaultSuccessUrl(\"/\", true))\n // Accept GET on /logout (Spring Security 6.4's default confirmation page\n // requires POST + CSRF; for a quickstart we let a simple \n // link work directly).\n .logout(l -> l\n // Spring Security 7 dropped AntPathRequestMatcher; PathPatternRequestMatcher\n // is the replacement. `.matcher(path)` (no HTTP method) matches any method.\n .logoutRequestMatcher(org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.withDefaults().matcher(\"/logout\"))\n .logoutSuccessUrl(\"/\"))\n // Disable CSRF for the demo. SAML2 ACS endpoint is already exempt by the\n // framework; this just removes the requirement on /logout. Production apps\n // with state-changing endpoints should re-enable CSRF.\n .csrf(c -> c.disable())\n .build();\n }\n }", "file": "src/main/java/com/secureauth/quickstart/Application.java", "lang": "java", - "lines": "97-173" + "lines": "97-175" }, { "step": 3, @@ -604,12 +604,12 @@ "code": " @Controller\n static class HomeController {\n\n @GetMapping(\"/\")\n @ResponseBody\n String home(@AuthenticationPrincipal Saml2AuthenticatedPrincipal user) {\n if (user == null) {\n return \"\"\"\n \n SecureAuth Java SAML Demo\n \n

SecureAuth Java SAML Demo

\n

Sign in

\n \n \"\"\";\n }\n return \"\"\"\n \n SecureAuth Java SAML Demo\n \n

SecureAuth Java SAML Demo

\n

Welcome, %s

\n

Sign out

\n \n \"\"\".formatted(esc(user.getName()));\n }\n\n private static String esc(String s) {\n if (s == null) return \"\";\n return s\n .replace(\"&\", \"&\")\n .replace(\"<\", \"<\")\n .replace(\">\", \">\")\n .replace(\"\\\"\", \""\")\n .replace(\"'\", \"'\");\n }\n }", "file": "src/main/java/com/secureauth/quickstart/Application.java", "lang": "java", - "lines": "176-214" + "lines": "178-216" } ], "framework": "java", "lib": "spring-security-saml2-service-provider", - "lib_version": "3.4.1", + "lib_version": "4.0.6", "docs_url": "https://docs.spring.io/spring-security/reference/servlet/saml2/login/index.html", "install": "", "repo_path": "samples/java/saml-sp-login", From 37ea065154831de94ffbd6b9e20e60e797bb756a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamila=20=C5=9Aroda?= Date: Wed, 6 May 2026 15:24:20 +0200 Subject: [PATCH 2/2] fix(snippets): correct ios lib_version drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Local Package.resolved had 2.0.0 cached from earlier AppAuth-iOS bump experiments, while project.yml on this branch is still 1.7.6. The extractor prefers Package.resolved over project.yml (more precise), so the locally-regenerated snippets.json carried 2.0.0 — but CI does a fresh checkout where Package.resolved (gitignored) doesn't exist, so it falls back to project.yml and reads 1.7.6. The drift check failed. Re-ran the extractor against a clean workspace (no Package.resolved) so local output matches CI's view. Co-Authored-By: Claude Opus 4.7 (1M context) --- snippets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snippets.json b/snippets.json index 427f955..4c5b0ef 100644 --- a/snippets.json +++ b/snippets.json @@ -691,7 +691,7 @@ ], "framework": "ios", "lib": "AppAuth-iOS", - "lib_version": "2.0.0", + "lib_version": "1.7.6", "docs_url": "https://github.com/openid/AppAuth-iOS", "install": "", "repo_path": "samples/ios/login-pkce",