From 1cda1ca36dadb3870b274ca5aed34d613e6ac397 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com> Date: Wed, 1 May 2024 17:46:12 -0500 Subject: [PATCH] Add impersonation sample for token exchange Closes gh-1604 --- .../config/AuthorizationServerConfig.java | 1 + .../sample/web/AuthorizationController.java | 21 ++++++++++++++++--- .../resources/templates/page-templates.html | 3 ++- .../sample/config/TokenExchangeConfig.java | 7 +++++++ .../main/java/sample/web/UserController.java | 20 ++++++++++++------ .../src/main/resources/application.yml | 11 ++++++++-- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java b/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java index 0608737f..f0d8b328 100644 --- a/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java +++ b/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()) diff --git a/samples/demo-client/src/main/java/sample/web/AuthorizationController.java b/samples/demo-client/src/main/java/sample/web/AuthorizationController.java index 38e12769..965c0932 100644 --- a/samples/demo-client/src/main/java/sample/web/AuthorizationController.java +++ b/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) diff --git a/samples/demo-client/src/main/resources/templates/page-templates.html b/samples/demo-client/src/main/resources/templates/page-templates.html index a22d3c5f..653919ca 100644 --- a/samples/demo-client/src/main/resources/templates/page-templates.html +++ b/samples/demo-client/src/main/resources/templates/page-templates.html @@ -27,7 +27,8 @@
  • Client Credentials (client_secret_basic)
  • Client Credentials (tls_client_auth)
  • Client Credentials (self_signed_tls_client_auth)
  • -
  • Token Exchange
  • +
  • Token Exchange (delegation)
  • +
  • Token Exchange (impersonation)
  • Device Code
  • diff --git a/samples/users-resource/src/main/java/sample/config/TokenExchangeConfig.java b/samples/users-resource/src/main/java/sample/config/TokenExchangeConfig.java index 48ea043a..2c64424c 100644 --- a/samples/users-resource/src/main/java/sample/config/TokenExchangeConfig.java +++ b/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) diff --git a/samples/users-resource/src/main/java/sample/web/UserController.java b/samples/users-resource/src/main/java/sample/web/UserController.java index 20a654b8..ef46f03d 100644 --- a/samples/users-resource/src/main/java/sample/web/UserController.java +++ b/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 getMessages(@AuthenticationPrincipal Jwt jwt, - @RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange") + @GetMapping(value = "/user/messages", params = "use_case=delegation") + public List getMessagesWithDelegation( + @RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-delegation") OAuth2AuthorizedClient authorizedClient) { + return getUserMessages(authorizedClient); + } + @GetMapping(value = "/user/messages", params = "use_case=impersonation") + public List getMessagesWithImpersonation( + @RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-impersonation") + OAuth2AuthorizedClient authorizedClient) { + return getUserMessages(authorizedClient); + } + + private List getUserMessages(OAuth2AuthorizedClient authorizedClient) { // @formatter:off String[] messages = Objects.requireNonNull( this.restClient.get() @@ -60,7 +68,7 @@ public class UserController { // @formatter:on List 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; } diff --git a/samples/users-resource/src/main/resources/application.yml b/samples/users-resource/src/main/resources/application.yml index 71acccef..c76aacfa 100644 --- a/samples/users-resource/src/main/resources/application.yml +++ b/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