Browse Source

Add impersonation sample for token exchange

Closes gh-1604
Steve Riesenberg 1 năm trước cách đây
mục cha
commit
1cda1ca36d

+ 1 - 0
samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java

@@ -165,6 +165,7 @@ public class AuthorizationServerConfig {
 				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
 				.authorizationGrantType(new AuthorizationGrantType("urn:ietf:params:oauth:grant-type:token-exchange"))
 				.scope("message.read")
+				.scope("message.write")
 				.build();
 
 		RegisteredClient mtlsDemoClient = RegisteredClient.withId(UUID.randomUUID().toString())

+ 18 - 3
samples/demo-client/src/main/java/sample/web/AuthorizationController.java

@@ -134,12 +134,27 @@ public class AuthorizationController {
 		return "index";
 	}
 
-	@GetMapping(value = "/authorize", params = "grant_type=token_exchange")
-	public String tokenExchangeGrant(Model model) {
+	@GetMapping(value = "/authorize", params = {"grant_type=token_exchange", "use_case=delegation"})
+	public String tokenExchangeGrantUsingDelegation(Model model) {
 
 		String[] messages = this.defaultClientWebClient
 				.get()
-				.uri(this.userMessagesBaseUri)
+				.uri(this.userMessagesBaseUri + "?use_case=delegation")
+				.attributes(clientRegistrationId("user-client-authorization-code"))
+				.retrieve()
+				.bodyToMono(String[].class)
+				.block();
+		model.addAttribute("messages", messages);
+
+		return "index";
+	}
+
+	@GetMapping(value = "/authorize", params = {"grant_type=token_exchange", "use_case=impersonation"})
+	public String tokenExchangeGrantUsingImpersonation(Model model) {
+
+		String[] messages = this.defaultClientWebClient
+				.get()
+				.uri(this.userMessagesBaseUri + "?use_case=impersonation")
 				.attributes(clientRegistrationId("user-client-authorization-code"))
 				.retrieve()
 				.bodyToMono(String[].class)

+ 2 - 1
samples/demo-client/src/main/resources/templates/page-templates.html

@@ -27,7 +27,8 @@
                         <li><a class="dropdown-item" href="/authorize?grant_type=client_credentials&client_auth=client_secret" th:href="@{/authorize?grant_type=client_credentials&client_auth=client_secret}">Client Credentials (client_secret_basic)</a></li>
                         <li><a class="dropdown-item" href="/authorize?grant_type=client_credentials&client_auth=mtls" th:href="@{/authorize?grant_type=client_credentials&client_auth=mtls}">Client Credentials (tls_client_auth)</a></li>
                         <li><a class="dropdown-item" href="/authorize?grant_type=client_credentials&client_auth=self_signed_mtls" th:href="@{/authorize?grant_type=client_credentials&client_auth=self_signed_mtls}">Client Credentials (self_signed_tls_client_auth)</a></li>
-                        <li><a class="dropdown-item" href="/authorize?grant_type=token_exchange" th:href="@{/authorize?grant_type=token_exchange}">Token Exchange</a></li>
+                        <li><a class="dropdown-item" href="/authorize?grant_type=token_exchange" th:href="@{/authorize?grant_type=token_exchange&use_case=delegation}">Token Exchange (delegation)</a></li>
+                        <li><a class="dropdown-item" href="/authorize?grant_type=token_exchange" th:href="@{/authorize?grant_type=token_exchange&use_case=impersonation}">Token Exchange (impersonation)</a></li>
                         <li><a class="dropdown-item" href="/authorize?grant_type=device_code" th:href="@{/authorize?grant_type=device_code}">Device Code</a></li>
                     </ul>
                 </li>

+ 7 - 0
samples/users-resource/src/main/java/sample/config/TokenExchangeConfig.java

@@ -41,6 +41,8 @@ public class TokenExchangeConfig {
 
 	private static final String ACTOR_TOKEN_CLIENT_REGISTRATION_ID = "messaging-client-client-credentials";
 
+	private static final String IMPERSONATION_CLIENT_REGISTRATION_ID = "messaging-client-token-exchange-with-impersonation";
+
 	@Bean
 	public OAuth2AuthorizedClientProvider tokenExchange(
 			ClientRegistrationRepository clientRegistrationRepository,
@@ -87,6 +89,11 @@ public class TokenExchangeConfig {
 			OAuth2AuthorizedClientManager authorizedClientManager, String clientRegistrationId) {
 
 		return (context) -> {
+			// Do not provide an actor token for impersonation use case
+			if (IMPERSONATION_CLIENT_REGISTRATION_ID.equals(context.getClientRegistration().getRegistrationId())) {
+				return null;
+			}
+
 			// @formatter:off
 			OAuth2AuthorizeRequest authorizeRequest =
 					OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)

+ 14 - 6
samples/users-resource/src/main/java/sample/web/UserController.java

@@ -21,10 +21,8 @@ import java.util.List;
 import java.util.Objects;
 
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
 import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
-import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.client.RestClient;
@@ -44,11 +42,21 @@ public class UserController {
 				.build();
 	}
 
-	@GetMapping("/user/messages")
-	public List<String> getMessages(@AuthenticationPrincipal Jwt jwt,
-			@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange")
+	@GetMapping(value = "/user/messages", params = "use_case=delegation")
+	public List<String> getMessagesWithDelegation(
+			@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-delegation")
 					OAuth2AuthorizedClient authorizedClient) {
+		return getUserMessages(authorizedClient);
+	}
+
+	@GetMapping(value = "/user/messages", params = "use_case=impersonation")
+	public List<String> getMessagesWithImpersonation(
+			@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-impersonation")
+					OAuth2AuthorizedClient authorizedClient) {
+		return getUserMessages(authorizedClient);
+	}
 
+	private List<String> getUserMessages(OAuth2AuthorizedClient authorizedClient) {
 		// @formatter:off
 		String[] messages = Objects.requireNonNull(
 				this.restClient.get()
@@ -60,7 +68,7 @@ public class UserController {
 		// @formatter:on
 
 		List<String> userMessages = new ArrayList<>(Arrays.asList(messages));
-		userMessages.add("%s has %d unread messages".formatted(jwt.getSubject(), messages.length));
+		userMessages.add("%s has %d unread messages".formatted(authorizedClient.getPrincipalName(), messages.length));
 
 		return userMessages;
 	}

+ 9 - 2
samples/users-resource/src/main/resources/application.yml

@@ -19,13 +19,20 @@ spring:
             client-secret: secret
             authorization-grant-type: client_credentials
             client-name: messaging-client-client-credentials
-          messaging-client-token-exchange:
+          messaging-client-token-exchange-with-delegation:
+            provider: spring
+            client-id: token-client
+            client-secret: token
+            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
+            scope: message.read,message.write
+            client-name: messaging-client-token-exchange-with-delegation
+          messaging-client-token-exchange-with-impersonation:
             provider: spring
             client-id: token-client
             client-secret: token
             authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
             scope: message.read
-            client-name: messaging-client-token-exchange
+            client-name: messaging-client-token-exchange-with-impersonation
         provider:
           spring:
             issuer-uri: http://localhost:9000