This is an automated email from the ASF dual-hosted git repository.

nscendoni pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-oauth-client.git


The following commit(s) were added to refs/heads/master by this push:
     new cb8d827  SLING-13047 Add RFC 8707 Resource Indicators support to Sling 
OIDC Au… (#40)
cb8d827 is described below

commit cb8d827af3fbf2bcbc6d9b086fc31ffe160f29b3
Author: Nicola Scendoni <[email protected]>
AuthorDate: Thu Jan 8 11:59:03 2026 +0100

    SLING-13047 Add RFC 8707 Resource Indicators support to Sling OIDC Au… (#40)
    
    * SLING-13047 Add RFC 8707 Resource Indicators support to Sling OIDC 
Authentication Handler
---
 .../oauth_client/impl/OAuthEntryPointServlet.java  |   2 +-
 .../impl/OidcAuthenticationHandler.java            |  13 +-
 .../auth/oauth_client/impl/RedirectHelper.java     |  14 +-
 .../impl/OidcAuthenticationHandlerTest.java        | 205 +++++++++++++++++++++
 .../auth/oauth_client/impl/RedirectHelperTest.java | 117 ++++++++++++
 5 files changed, 348 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/auth/oauth_client/impl/OAuthEntryPointServlet.java
 
b/src/main/java/org/apache/sling/auth/oauth_client/impl/OAuthEntryPointServlet.java
index 0f02b56..fcda20b 100644
--- 
a/src/main/java/org/apache/sling/auth/oauth_client/impl/OAuthEntryPointServlet.java
+++ 
b/src/main/java/org/apache/sling/auth/oauth_client/impl/OAuthEntryPointServlet.java
@@ -116,6 +116,6 @@ public class OAuthEntryPointServlet extends 
SlingAllMethodsServlet {
         OAuthCookieValue oAuthCookieValue = new 
OAuthCookieValue(perRequestKey, connection.name(), redirect);
 
         return RedirectHelper.buildRedirectTarget(
-                new String[] {PATH}, callbackUri, conn, oAuthCookieValue, 
cryptoService);
+                new String[] {PATH}, callbackUri, conn, oAuthCookieValue, 
cryptoService, null);
     }
 }
diff --git 
a/src/main/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandler.java
 
b/src/main/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandler.java
index 1bdf648..9eb31c7 100644
--- 
a/src/main/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandler.java
+++ 
b/src/main/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandler.java
@@ -109,6 +109,8 @@ public class OidcAuthenticationHandler extends 
DefaultAuthenticationFeedbackHand
 
     private final String[] path;
 
+    private final String[] resource;
+
     private final CryptoService cryptoService;
 
     @ObjectClassDefinition(
@@ -138,6 +140,14 @@ public class OidcAuthenticationHandler extends 
DefaultAuthenticationFeedbackHand
 
         @AttributeDefinition(name = "UserInfo Enabled", description = 
"UserInfo Enabled")
         boolean userInfoEnabled() default true;
+
+        @AttributeDefinition(
+                name = "Resource",
+                description = "Resource values to include in the 
authentication request. "
+                        + "This is used to request access tokens for specific 
resource servers or APIs. "
+                        + "Multiple values can be specified.",
+                cardinality = Integer.MAX_VALUE)
+        String[] resource() default {};
     }
 
     @Activate
@@ -159,6 +169,7 @@ public class OidcAuthenticationHandler extends 
DefaultAuthenticationFeedbackHand
         this.userInfoEnabled = config.userInfoEnabled();
         this.pkceEnabled = config.pkceEnabled();
         this.path = config.path();
+        this.resource = config.resource();
         this.cryptoService = cryptoService;
 
         logger.debug("activate: registering ExternalIdentityProvider");
@@ -537,7 +548,7 @@ public class OidcAuthenticationHandler extends 
DefaultAuthenticationFeedbackHand
         OAuthCookieValue oAuthCookieValue =
                 new OAuthCookieValue(perRequestKey, connection.name(), 
redirect, nonce, codeVerifier);
 
-        return RedirectHelper.buildRedirectTarget(path, callbackUri, conn, 
oAuthCookieValue, cryptoService);
+        return RedirectHelper.buildRedirectTarget(path, callbackUri, conn, 
oAuthCookieValue, cryptoService, resource);
     }
 
     @Override
diff --git 
a/src/main/java/org/apache/sling/auth/oauth_client/impl/RedirectHelper.java 
b/src/main/java/org/apache/sling/auth/oauth_client/impl/RedirectHelper.java
index 296ecf4..7a7f2af 100644
--- a/src/main/java/org/apache/sling/auth/oauth_client/impl/RedirectHelper.java
+++ b/src/main/java/org/apache/sling/auth/oauth_client/impl/RedirectHelper.java
@@ -56,7 +56,8 @@ class RedirectHelper {
             @NotNull URI callbackUri,
             @NotNull ResolvedConnection conn,
             @NotNull OAuthCookieValue oAuthCookieValue,
-            @NotNull CryptoService cryptoService) {
+            @NotNull CryptoService cryptoService,
+            @Nullable String[] resource) {
 
         String path = findLongestPathMatching(paths, callbackUri.getPath());
 
@@ -87,6 +88,17 @@ class RedirectHelper {
             authRequestBuilder.codeChallenge(codeVerifier, 
CodeChallengeMethod.S256);
         }
 
+        // Add resource parameter(s) to the authentication request (RFC 8707)
+        if (resource != null) {
+            List<URI> resourceUris = java.util.Arrays.stream(resource)
+                    .filter(r -> r != null && !r.trim().isEmpty())
+                    .map(URI::create)
+                    .collect(Collectors.toList());
+            if (!resourceUris.isEmpty()) {
+                authRequestBuilder.resources(resourceUris.toArray(new URI[0]));
+            }
+        }
+
         List<String[]> parameters = 
conn.additionalAuthorizationParameters().stream()
                 .map(s -> s.split("="))
                 .filter(p -> p.length == 2)
diff --git 
a/src/test/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandlerTest.java
 
b/src/test/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandlerTest.java
index 67df3cb..f257fc8 100644
--- 
a/src/test/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandlerTest.java
+++ 
b/src/test/java/org/apache/sling/auth/oauth_client/impl/OidcAuthenticationHandlerTest.java
@@ -1289,4 +1289,209 @@ class OidcAuthenticationHandlerTest {
             return false;
         }));
     }
+
+    @Test
+    void requestCredentialsWithResourceAttribute() {
+        // This is the class used by Sling to configure the Authentication 
Handler
+        OidcProviderMetadataRegistry oidcProviderMetadataRegistry = 
mock(OidcProviderMetadataRegistry.class);
+        String mockIdPUrl = "http://localhost:8080";;
+        
when(oidcProviderMetadataRegistry.getJWKSetURI(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/jwks.json"));
+        
when(oidcProviderMetadataRegistry.getIssuer(mockIdPUrl)).thenReturn(ISSUER);
+        when(oidcProviderMetadataRegistry.getAuthorizationEndpoint(mockIdPUrl))
+                .thenReturn(URI.create(mockIdPUrl + "/authorize"));
+        
when(oidcProviderMetadataRegistry.getTokenEndpoint(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/token"));
+
+        connections.add(new MockOidcConnection(
+                new String[] {"openid"},
+                MOCK_OIDC_PARAM,
+                "client-id",
+                "client-secret",
+                "http://localhost:8080";,
+                new String[] {"access_type=offline"},
+                oidcProviderMetadataRegistry));
+
+        when(config.defaultConnectionName()).thenReturn(MOCK_OIDC_PARAM);
+        when(config.callbackUri()).thenReturn("http://redirect";);
+        when(config.pkceEnabled()).thenReturn(false);
+        when(config.path()).thenReturn(new String[] {"/"});
+        when(config.resource()).thenReturn(new String[] 
{"https://api.example.com"});
+
+        when(request.getRequestURI()).thenReturn("/");
+        MockSlingHttpServletResponse mockResponse = new 
MockSlingHttpServletResponse();
+
+        createOidcAuthenticationHandler();
+        assertTrue(oidcAuthenticationHandler.requestCredentials(request, 
mockResponse));
+
+        // Verify that the resource parameter is present in the redirect URL
+        assertEquals(302, mockResponse.getStatus());
+        String location = mockResponse.getHeader("location");
+        assertTrue(
+                location.contains("resource=https%3A%2F%2Fapi.example.com"),
+                "Expected resource parameter in redirect URL but got: " + 
location);
+    }
+
+    @Test
+    void requestCredentialsWithMultipleResourceAttributes() {
+        // This is the class used by Sling to configure the Authentication 
Handler
+        OidcProviderMetadataRegistry oidcProviderMetadataRegistry = 
mock(OidcProviderMetadataRegistry.class);
+        String mockIdPUrl = "http://localhost:8080";;
+        
when(oidcProviderMetadataRegistry.getJWKSetURI(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/jwks.json"));
+        
when(oidcProviderMetadataRegistry.getIssuer(mockIdPUrl)).thenReturn(ISSUER);
+        when(oidcProviderMetadataRegistry.getAuthorizationEndpoint(mockIdPUrl))
+                .thenReturn(URI.create(mockIdPUrl + "/authorize"));
+        
when(oidcProviderMetadataRegistry.getTokenEndpoint(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/token"));
+
+        connections.add(new MockOidcConnection(
+                new String[] {"openid"},
+                MOCK_OIDC_PARAM,
+                "client-id",
+                "client-secret",
+                "http://localhost:8080";,
+                new String[] {"access_type=offline"},
+                oidcProviderMetadataRegistry));
+
+        when(config.defaultConnectionName()).thenReturn(MOCK_OIDC_PARAM);
+        when(config.callbackUri()).thenReturn("http://redirect";);
+        when(config.pkceEnabled()).thenReturn(false);
+        when(config.path()).thenReturn(new String[] {"/"});
+        when(config.resource()).thenReturn(new String[] 
{"https://api1.example.com";, "https://api2.example.com"});
+
+        when(request.getRequestURI()).thenReturn("/");
+        MockSlingHttpServletResponse mockResponse = new 
MockSlingHttpServletResponse();
+
+        createOidcAuthenticationHandler();
+        assertTrue(oidcAuthenticationHandler.requestCredentials(request, 
mockResponse));
+
+        // Verify that both resource parameters are present in the redirect URL
+        assertEquals(302, mockResponse.getStatus());
+        String location = mockResponse.getHeader("location");
+        assertTrue(
+                location.contains("resource=https%3A%2F%2Fapi1.example.com"),
+                "Expected first resource parameter in redirect URL but got: " 
+ location);
+        assertTrue(
+                location.contains("resource=https%3A%2F%2Fapi2.example.com"),
+                "Expected second resource parameter in redirect URL but got: " 
+ location);
+    }
+
+    @Test
+    void requestCredentialsWithEmptyResourceAttribute() {
+        // This is the class used by Sling to configure the Authentication 
Handler
+        OidcProviderMetadataRegistry oidcProviderMetadataRegistry = 
mock(OidcProviderMetadataRegistry.class);
+        String mockIdPUrl = "http://localhost:8080";;
+        
when(oidcProviderMetadataRegistry.getJWKSetURI(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/jwks.json"));
+        
when(oidcProviderMetadataRegistry.getIssuer(mockIdPUrl)).thenReturn(ISSUER);
+        when(oidcProviderMetadataRegistry.getAuthorizationEndpoint(mockIdPUrl))
+                .thenReturn(URI.create(mockIdPUrl + "/authorize"));
+        
when(oidcProviderMetadataRegistry.getTokenEndpoint(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/token"));
+
+        connections.add(new MockOidcConnection(
+                new String[] {"openid"},
+                MOCK_OIDC_PARAM,
+                "client-id",
+                "client-secret",
+                "http://localhost:8080";,
+                new String[] {"access_type=offline"},
+                oidcProviderMetadataRegistry));
+
+        when(config.defaultConnectionName()).thenReturn(MOCK_OIDC_PARAM);
+        when(config.callbackUri()).thenReturn("http://redirect";);
+        when(config.pkceEnabled()).thenReturn(false);
+        when(config.path()).thenReturn(new String[] {"/"});
+        when(config.resource()).thenReturn(new String[] {});
+
+        when(request.getRequestURI()).thenReturn("/");
+        MockSlingHttpServletResponse mockResponse = new 
MockSlingHttpServletResponse();
+
+        createOidcAuthenticationHandler();
+        assertTrue(oidcAuthenticationHandler.requestCredentials(request, 
mockResponse));
+
+        // Verify that no resource parameter is present in the redirect URL
+        assertEquals(302, mockResponse.getStatus());
+        String location = mockResponse.getHeader("location");
+        assertFalse(
+                location.contains("resource="), "Expected no resource 
parameter in redirect URL but got: " + location);
+    }
+
+    @Test
+    void requestCredentialsWithNullResourceAttribute() {
+        // This is the class used by Sling to configure the Authentication 
Handler
+        OidcProviderMetadataRegistry oidcProviderMetadataRegistry = 
mock(OidcProviderMetadataRegistry.class);
+        String mockIdPUrl = "http://localhost:8080";;
+        
when(oidcProviderMetadataRegistry.getJWKSetURI(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/jwks.json"));
+        
when(oidcProviderMetadataRegistry.getIssuer(mockIdPUrl)).thenReturn(ISSUER);
+        when(oidcProviderMetadataRegistry.getAuthorizationEndpoint(mockIdPUrl))
+                .thenReturn(URI.create(mockIdPUrl + "/authorize"));
+        
when(oidcProviderMetadataRegistry.getTokenEndpoint(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/token"));
+
+        connections.add(new MockOidcConnection(
+                new String[] {"openid"},
+                MOCK_OIDC_PARAM,
+                "client-id",
+                "client-secret",
+                "http://localhost:8080";,
+                new String[] {"access_type=offline"},
+                oidcProviderMetadataRegistry));
+
+        when(config.defaultConnectionName()).thenReturn(MOCK_OIDC_PARAM);
+        when(config.callbackUri()).thenReturn("http://redirect";);
+        when(config.pkceEnabled()).thenReturn(false);
+        when(config.path()).thenReturn(new String[] {"/"});
+        when(config.resource()).thenReturn(null);
+
+        when(request.getRequestURI()).thenReturn("/");
+        MockSlingHttpServletResponse mockResponse = new 
MockSlingHttpServletResponse();
+
+        createOidcAuthenticationHandler();
+        assertTrue(oidcAuthenticationHandler.requestCredentials(request, 
mockResponse));
+
+        // Verify that no resource parameter is present in the redirect URL
+        assertEquals(302, mockResponse.getStatus());
+        String location = mockResponse.getHeader("location");
+        assertFalse(
+                location.contains("resource="), "Expected no resource 
parameter in redirect URL but got: " + location);
+    }
+
+    @Test
+    void requestCredentialsWithResourceAttributeContainingEmptyStrings() {
+        // This is the class used by Sling to configure the Authentication 
Handler
+        OidcProviderMetadataRegistry oidcProviderMetadataRegistry = 
mock(OidcProviderMetadataRegistry.class);
+        String mockIdPUrl = "http://localhost:8080";;
+        
when(oidcProviderMetadataRegistry.getJWKSetURI(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/jwks.json"));
+        
when(oidcProviderMetadataRegistry.getIssuer(mockIdPUrl)).thenReturn(ISSUER);
+        when(oidcProviderMetadataRegistry.getAuthorizationEndpoint(mockIdPUrl))
+                .thenReturn(URI.create(mockIdPUrl + "/authorize"));
+        
when(oidcProviderMetadataRegistry.getTokenEndpoint(mockIdPUrl)).thenReturn(URI.create(mockIdPUrl
 + "/token"));
+
+        connections.add(new MockOidcConnection(
+                new String[] {"openid"},
+                MOCK_OIDC_PARAM,
+                "client-id",
+                "client-secret",
+                "http://localhost:8080";,
+                new String[] {"access_type=offline"},
+                oidcProviderMetadataRegistry));
+
+        when(config.defaultConnectionName()).thenReturn(MOCK_OIDC_PARAM);
+        when(config.callbackUri()).thenReturn("http://redirect";);
+        when(config.pkceEnabled()).thenReturn(false);
+        when(config.path()).thenReturn(new String[] {"/"});
+        // Array with empty strings and whitespace should be filtered out
+        when(config.resource()).thenReturn(new String[] {"", "  ", 
"https://api.example.com"});
+
+        when(request.getRequestURI()).thenReturn("/");
+        MockSlingHttpServletResponse mockResponse = new 
MockSlingHttpServletResponse();
+
+        createOidcAuthenticationHandler();
+        assertTrue(oidcAuthenticationHandler.requestCredentials(request, 
mockResponse));
+
+        // Verify that only the valid resource parameter is present in the 
redirect URL
+        assertEquals(302, mockResponse.getStatus());
+        String location = mockResponse.getHeader("location");
+        assertTrue(
+                location.contains("resource=https%3A%2F%2Fapi.example.com"),
+                "Expected valid resource parameter in redirect URL but got: " 
+ location);
+        // Count occurrences of "resource=" - should be exactly 1
+        int count = location.split("resource=", -1).length - 1;
+        assertEquals(1, count, "Expected exactly one resource parameter but 
found " + count);
+    }
 }
diff --git 
a/src/test/java/org/apache/sling/auth/oauth_client/impl/RedirectHelperTest.java 
b/src/test/java/org/apache/sling/auth/oauth_client/impl/RedirectHelperTest.java
index 4c21870..ef4266b 100644
--- 
a/src/test/java/org/apache/sling/auth/oauth_client/impl/RedirectHelperTest.java
+++ 
b/src/test/java/org/apache/sling/auth/oauth_client/impl/RedirectHelperTest.java
@@ -18,11 +18,18 @@
  */
 package org.apache.sling.auth.oauth_client.impl;
 
+import java.net.URI;
+import java.util.List;
+
+import com.nimbusds.openid.connect.sdk.Nonce;
+import org.apache.sling.commons.crypto.CryptoService;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 class RedirectHelperTest {
 
@@ -134,4 +141,114 @@ class RedirectHelperTest {
         assertTrue(exception.getMessage().contains("Invalid redirect URL"));
         assertTrue(exception.getCause() instanceof IllegalArgumentException);
     }
+
+    @Test
+    void testBuildRedirectTargetWithSingleAudience() {
+        ResolvedConnection conn = createMockResolvedConnection();
+        CryptoService cryptoService = new StubCryptoService();
+        OAuthCookieValue oAuthCookieValue =
+                new OAuthCookieValue("perRequestKey", "connectionName", 
"/redirect", new Nonce("nonce"), null);
+        String[] audience = new String[] {"https://api.example.com"};
+
+        RedirectTarget result = RedirectHelper.buildRedirectTarget(
+                new String[] {"/"}, URI.create("/callback"), conn, 
oAuthCookieValue, cryptoService, audience);
+
+        assertNotNull(result);
+        assertNotNull(result.uri());
+        String uriString = result.uri().toString();
+        assertTrue(
+                uriString.contains("resource=https%3A%2F%2Fapi.example.com"),
+                "Expected resource parameter in URI but got: " + uriString);
+    }
+
+    @Test
+    void testBuildRedirectTargetWithMultipleAudiences() {
+        ResolvedConnection conn = createMockResolvedConnection();
+        CryptoService cryptoService = new StubCryptoService();
+        OAuthCookieValue oAuthCookieValue =
+                new OAuthCookieValue("perRequestKey", "connectionName", 
"/redirect", new Nonce("nonce"), null);
+        String[] audience = new String[] {"https://api1.example.com";, 
"https://api2.example.com"};
+
+        RedirectTarget result = RedirectHelper.buildRedirectTarget(
+                new String[] {"/"}, URI.create("/callback"), conn, 
oAuthCookieValue, cryptoService, audience);
+
+        assertNotNull(result);
+        assertNotNull(result.uri());
+        String uriString = result.uri().toString();
+        // Using Nimbus SDK resources() method properly handles multiple 
resource values
+        assertTrue(
+                uriString.contains("resource=https%3A%2F%2Fapi1.example.com"),
+                "Expected first resource parameter in URI but got: " + 
uriString);
+        assertTrue(
+                uriString.contains("resource=https%3A%2F%2Fapi2.example.com"),
+                "Expected second resource parameter in URI but got: " + 
uriString);
+    }
+
+    @Test
+    void testBuildRedirectTargetWithEmptyAudience() {
+        ResolvedConnection conn = createMockResolvedConnection();
+        CryptoService cryptoService = new StubCryptoService();
+        OAuthCookieValue oAuthCookieValue =
+                new OAuthCookieValue("perRequestKey", "connectionName", 
"/redirect", new Nonce("nonce"), null);
+        String[] audience = new String[] {};
+
+        RedirectTarget result = RedirectHelper.buildRedirectTarget(
+                new String[] {"/"}, URI.create("/callback"), conn, 
oAuthCookieValue, cryptoService, audience);
+
+        assertRedirectTargetHasNoResourceParameter(result);
+    }
+
+    @Test
+    void testBuildRedirectTargetWithNullAudience() {
+        ResolvedConnection conn = createMockResolvedConnection();
+        CryptoService cryptoService = new StubCryptoService();
+        OAuthCookieValue oAuthCookieValue =
+                new OAuthCookieValue("perRequestKey", "connectionName", 
"/redirect", new Nonce("nonce"), null);
+
+        RedirectTarget result = RedirectHelper.buildRedirectTarget(
+                new String[] {"/"}, URI.create("/callback"), conn, 
oAuthCookieValue, cryptoService, null);
+
+        assertRedirectTargetHasNoResourceParameter(result);
+    }
+
+    @Test
+    void testBuildRedirectTargetWithAudienceContainingEmptyStrings() {
+        ResolvedConnection conn = createMockResolvedConnection();
+        CryptoService cryptoService = new StubCryptoService();
+        OAuthCookieValue oAuthCookieValue =
+                new OAuthCookieValue("perRequestKey", "connectionName", 
"/redirect", new Nonce("nonce"), null);
+        // Array with empty strings, whitespace, and one valid value
+        String[] audience = new String[] {"", "  ", "https://api.example.com";, 
null};
+
+        RedirectTarget result = RedirectHelper.buildRedirectTarget(
+                new String[] {"/"}, URI.create("/callback"), conn, 
oAuthCookieValue, cryptoService, audience);
+
+        assertNotNull(result);
+        assertNotNull(result.uri());
+        String uriString = result.uri().toString();
+        assertTrue(
+                uriString.contains("resource=https%3A%2F%2Fapi.example.com"),
+                "Expected valid resource parameter in URI but got: " + 
uriString);
+        // Count occurrences of "resource=" - should be exactly 1
+        int count = uriString.split("resource=", -1).length - 1;
+        assertEquals(1, count, "Expected exactly one resource parameter but 
found " + count);
+    }
+
+    private ResolvedConnection createMockResolvedConnection() {
+        ResolvedConnection conn = mock(ResolvedConnection.class);
+        
when(conn.authorizationEndpoint()).thenReturn("http://localhost:8080/authorize";);
+        when(conn.tokenEndpoint()).thenReturn("http://localhost:8080/token";);
+        when(conn.clientId()).thenReturn("client-id");
+        when(conn.clientSecret()).thenReturn("client-secret");
+        when(conn.scopes()).thenReturn(List.of("openid"));
+        when(conn.additionalAuthorizationParameters()).thenReturn(List.of());
+        return conn;
+    }
+
+    private void assertRedirectTargetHasNoResourceParameter(RedirectTarget 
result) {
+        assertNotNull(result);
+        assertNotNull(result.uri());
+        String uriString = result.uri().toString();
+        assertFalse(uriString.contains("resource="), "Expected no resource 
parameter in URI but got: " + uriString);
+    }
 }

Reply via email to