Skip to content

Commit 1cda1ca

Browse files
committed
Add impersonation sample for token exchange
Closes gh-1604
1 parent 76322dc commit 1cda1ca

File tree

6 files changed

+51
-12
lines changed

6 files changed

+51
-12
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ public JdbcRegisteredClientRepository registeredClientRepository(JdbcTemplate jd
165165
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
166166
.authorizationGrantType(new AuthorizationGrantType("urn:ietf:params:oauth:grant-type:token-exchange"))
167167
.scope("message.read")
168+
.scope("message.write")
168169
.build();
169170

170171
RegisteredClient mtlsDemoClient = RegisteredClient.withId(UUID.randomUUID().toString())

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,27 @@ public String clientCredentialsGrantUsingSelfSignedMutualTLS(Model model) {
134134
return "index";
135135
}
136136

137-
@GetMapping(value = "/authorize", params = "grant_type=token_exchange")
138-
public String tokenExchangeGrant(Model model) {
137+
@GetMapping(value = "/authorize", params = {"grant_type=token_exchange", "use_case=delegation"})
138+
public String tokenExchangeGrantUsingDelegation(Model model) {
139139

140140
String[] messages = this.defaultClientWebClient
141141
.get()
142-
.uri(this.userMessagesBaseUri)
142+
.uri(this.userMessagesBaseUri + "?use_case=delegation")
143+
.attributes(clientRegistrationId("user-client-authorization-code"))
144+
.retrieve()
145+
.bodyToMono(String[].class)
146+
.block();
147+
model.addAttribute("messages", messages);
148+
149+
return "index";
150+
}
151+
152+
@GetMapping(value = "/authorize", params = {"grant_type=token_exchange", "use_case=impersonation"})
153+
public String tokenExchangeGrantUsingImpersonation(Model model) {
154+
155+
String[] messages = this.defaultClientWebClient
156+
.get()
157+
.uri(this.userMessagesBaseUri + "?use_case=impersonation")
143158
.attributes(clientRegistrationId("user-client-authorization-code"))
144159
.retrieve()
145160
.bodyToMono(String[].class)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
<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>
2828
<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>
2929
<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>
30-
<li><a class="dropdown-item" href="/authorize?grant_type=token_exchange" th:href="@{/authorize?grant_type=token_exchange}">Token Exchange</a></li>
30+
<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>
31+
<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>
3132
<li><a class="dropdown-item" href="/authorize?grant_type=device_code" th:href="@{/authorize?grant_type=device_code}">Device Code</a></li>
3233
</ul>
3334
</li>

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class TokenExchangeConfig {
4141

4242
private static final String ACTOR_TOKEN_CLIENT_REGISTRATION_ID = "messaging-client-client-credentials";
4343

44+
private static final String IMPERSONATION_CLIENT_REGISTRATION_ID = "messaging-client-token-exchange-with-impersonation";
45+
4446
@Bean
4547
public OAuth2AuthorizedClientProvider tokenExchange(
4648
ClientRegistrationRepository clientRegistrationRepository,
@@ -87,6 +89,11 @@ private static Function<OAuth2AuthorizationContext, OAuth2Token> createTokenReso
8789
OAuth2AuthorizedClientManager authorizedClientManager, String clientRegistrationId) {
8890

8991
return (context) -> {
92+
// Do not provide an actor token for impersonation use case
93+
if (IMPERSONATION_CLIENT_REGISTRATION_ID.equals(context.getClientRegistration().getRegistrationId())) {
94+
return null;
95+
}
96+
9097
// @formatter:off
9198
OAuth2AuthorizeRequest authorizeRequest =
9299
OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
import java.util.Objects;
2222

2323
import org.springframework.beans.factory.annotation.Value;
24-
import org.springframework.security.core.annotation.AuthenticationPrincipal;
2524
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
2625
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
27-
import org.springframework.security.oauth2.jwt.Jwt;
2826
import org.springframework.web.bind.annotation.GetMapping;
2927
import org.springframework.web.bind.annotation.RestController;
3028
import org.springframework.web.client.RestClient;
@@ -44,11 +42,21 @@ public UserController(@Value("${messages.base-uri}") String baseUrl) {
4442
.build();
4543
}
4644

47-
@GetMapping("/user/messages")
48-
public List<String> getMessages(@AuthenticationPrincipal Jwt jwt,
49-
@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange")
45+
@GetMapping(value = "/user/messages", params = "use_case=delegation")
46+
public List<String> getMessagesWithDelegation(
47+
@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-delegation")
5048
OAuth2AuthorizedClient authorizedClient) {
49+
return getUserMessages(authorizedClient);
50+
}
51+
52+
@GetMapping(value = "/user/messages", params = "use_case=impersonation")
53+
public List<String> getMessagesWithImpersonation(
54+
@RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange-with-impersonation")
55+
OAuth2AuthorizedClient authorizedClient) {
56+
return getUserMessages(authorizedClient);
57+
}
5158

59+
private List<String> getUserMessages(OAuth2AuthorizedClient authorizedClient) {
5260
// @formatter:off
5361
String[] messages = Objects.requireNonNull(
5462
this.restClient.get()
@@ -60,7 +68,7 @@ public List<String> getMessages(@AuthenticationPrincipal Jwt jwt,
6068
// @formatter:on
6169

6270
List<String> userMessages = new ArrayList<>(Arrays.asList(messages));
63-
userMessages.add("%s has %d unread messages".formatted(jwt.getSubject(), messages.length));
71+
userMessages.add("%s has %d unread messages".formatted(authorizedClient.getPrincipalName(), messages.length));
6472

6573
return userMessages;
6674
}

samples/users-resource/src/main/resources/application.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@ spring:
1919
client-secret: secret
2020
authorization-grant-type: client_credentials
2121
client-name: messaging-client-client-credentials
22-
messaging-client-token-exchange:
22+
messaging-client-token-exchange-with-delegation:
23+
provider: spring
24+
client-id: token-client
25+
client-secret: token
26+
authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
27+
scope: message.read,message.write
28+
client-name: messaging-client-token-exchange-with-delegation
29+
messaging-client-token-exchange-with-impersonation:
2330
provider: spring
2431
client-id: token-client
2532
client-secret: token
2633
authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
2734
scope: message.read
28-
client-name: messaging-client-token-exchange
35+
client-name: messaging-client-token-exchange-with-impersonation
2936
provider:
3037
spring:
3138
issuer-uri: http://localhost:9000

0 commit comments

Comments
 (0)