Skip to content

Commit 99935c4

Browse files
committed
add the ability to store additional grant request parameters for the OIDC token exchange grant request
1 parent 0e3cfd1 commit 99935c4

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/InMemoryOAuth2AuthorizedClientService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRe
8686
return null;
8787
}
8888
return (T) new OAuth2AuthorizedClient(registration, cachedAuthorizedClient.getPrincipalName(),
89-
cachedAuthorizedClient.getAccessToken(), cachedAuthorizedClient.getRefreshToken());
89+
cachedAuthorizedClient.getAccessToken(), cachedAuthorizedClient.getRefreshToken(),
90+
cachedAuthorizedClient.getAttributes());
9091
}
9192

9293
@Override

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizationContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public final class OAuth2AuthorizationContext {
6060
*/
6161
public static final String PASSWORD_ATTRIBUTE_NAME = OAuth2AuthorizationContext.class.getName().concat(".PASSWORD");
6262

63+
public static final String ADDITIONAL_GRANT_REQUEST_PARAMETERS_ATTRIBUTE_NAME = OAuth2AuthorizationContext.class.getName().concat(".ADDITIONAL_GRANT_REQUEST_PARAMETERS");
64+
6365
private ClientRegistration clientRegistration;
6466

6567
private OAuth2AuthorizedClient authorizedClient;

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.security.oauth2.client;
1818

1919
import java.io.Serializable;
20+
import java.util.Map;
2021

2122
import org.springframework.lang.Nullable;
2223
import org.springframework.security.core.SpringSecurityCoreVersion;
@@ -53,6 +54,8 @@ public class OAuth2AuthorizedClient implements Serializable {
5354

5455
private final OAuth2RefreshToken refreshToken;
5556

57+
private final Map<String, Object> attributes;
58+
5659
/**
5760
* Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.
5861
* @param clientRegistration the authorized client's registration
@@ -73,13 +76,28 @@ public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String prin
7376
*/
7477
public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName,
7578
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
79+
this(clientRegistration, principalName, accessToken, refreshToken, null);
80+
}
81+
82+
/**
83+
* Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.
84+
* @param clientRegistration the authorized client's registration
85+
* @param principalName the name of the End-User {@code Principal} (Resource Owner)
86+
* @param accessToken the access token credential granted
87+
* @param refreshToken the refresh token credential granted
88+
* @param attributes associated with the client
89+
*/
90+
public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName,
91+
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken,
92+
@Nullable Map<String, Object> attributes) {
7693
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
7794
Assert.hasText(principalName, "principalName cannot be empty");
7895
Assert.notNull(accessToken, "accessToken cannot be null");
7996
this.clientRegistration = clientRegistration;
8097
this.principalName = principalName;
8198
this.accessToken = accessToken;
8299
this.refreshToken = refreshToken;
100+
this.attributes = attributes;
83101
}
84102

85103
/**
@@ -115,4 +133,21 @@ public OAuth2AccessToken getAccessToken() {
115133
return this.refreshToken;
116134
}
117135

136+
/**
137+
* Returns the {@link Map} attributes.
138+
* @return the {@link Map}
139+
* @since 6.5
140+
*/
141+
public @Nullable Map<String, Object> getAttributes() {
142+
return this.attributes;
143+
}
144+
145+
@Nullable
146+
@SuppressWarnings("unchecked")
147+
public <T> T getAttribute(String name) {
148+
if (this.getAttributes() == null) {
149+
return null;
150+
}
151+
return (T) this.getAttributes().get(name);
152+
}
118153
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/TokenExchangeOAuth2AuthorizedClientProvider.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.time.Clock;
2020
import java.time.Duration;
2121
import java.time.Instant;
22+
import java.util.*;
2223
import java.util.function.Function;
2324

2425
import org.springframework.lang.Nullable;
@@ -73,9 +74,19 @@ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
7374
if (!AuthorizationGrantType.TOKEN_EXCHANGE.equals(clientRegistration.getAuthorizationGrantType())) {
7475
return null;
7576
}
77+
78+
Map<String, Object> contextAdditionalGrantRequestParameters =
79+
context.getAttribute(OAuth2AuthorizationContext.ADDITIONAL_GRANT_REQUEST_PARAMETERS_ATTRIBUTE_NAME);
80+
7681
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
77-
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
78-
// If client is already authorized but access token is NOT expired than no
82+
Map<String, Object> authorizedClientAdditionalGrantRequestParameters =
83+
authorizedClient != null
84+
? authorizedClient.getAttribute(OAuth2AuthorizationContext.ADDITIONAL_GRANT_REQUEST_PARAMETERS_ATTRIBUTE_NAME)
85+
: null;
86+
if (authorizedClient != null
87+
&& !hasTokenExpired(authorizedClient.getAccessToken())
88+
&& Objects.equals(authorizedClientAdditionalGrantRequestParameters, contextAdditionalGrantRequestParameters)) {
89+
// If client is already authorized but access token is NOT expired and the attributes are equal, then no
7990
// need for re-authorization
8091
return null;
8192
}
@@ -86,11 +97,14 @@ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
8697

8798
OAuth2Token actorToken = this.actorTokenResolver.apply(context);
8899
TokenExchangeGrantRequest grantRequest = new TokenExchangeGrantRequest(clientRegistration, subjectToken,
89-
actorToken);
100+
actorToken, contextAdditionalGrantRequestParameters);
90101
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, grantRequest);
91102

103+
Map<String, Object> authorizedClientAttributes = new HashMap<>();
104+
authorizedClientAttributes.put(OAuth2AuthorizationContext.ADDITIONAL_GRANT_REQUEST_PARAMETERS_ATTRIBUTE_NAME, contextAdditionalGrantRequestParameters);
105+
92106
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
93-
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
107+
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken(), authorizedClientAttributes);
94108
}
95109

96110
private OAuth2Token resolveSubjectToken(OAuth2AuthorizationContext context) {

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/TokenExchangeGrantRequest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import org.springframework.util.MultiValueMap;
2828
import org.springframework.util.StringUtils;
2929

30+
import java.util.Map;
31+
3032
/**
3133
* A Token Exchange Grant request that holds the {@link OAuth2Token subject token} and
3234
* optional {@link OAuth2Token actor token}.
@@ -53,6 +55,8 @@ public class TokenExchangeGrantRequest extends AbstractOAuth2AuthorizationGrantR
5355

5456
private final OAuth2Token actorToken;
5557

58+
private final Map<String, Object> additionalParameters;
59+
5660
/**
5761
* Constructs a {@code TokenExchangeGrantRequest} using the provided parameters.
5862
* @param clientRegistration the client registration
@@ -61,12 +65,24 @@ public class TokenExchangeGrantRequest extends AbstractOAuth2AuthorizationGrantR
6165
*/
6266
public TokenExchangeGrantRequest(ClientRegistration clientRegistration, OAuth2Token subjectToken,
6367
OAuth2Token actorToken) {
68+
this(clientRegistration,subjectToken,actorToken,null);
69+
}
70+
71+
/**
72+
* Constructs a {@code TokenExchangeGrantRequest} using the provided parameters.
73+
* @param clientRegistration the client registration
74+
* @param subjectToken the subject token
75+
* @param actorToken the actor token
76+
*/
77+
public TokenExchangeGrantRequest(ClientRegistration clientRegistration, OAuth2Token subjectToken,
78+
OAuth2Token actorToken, Map<String, Object> additionalParameters) {
6479
super(AuthorizationGrantType.TOKEN_EXCHANGE, clientRegistration);
6580
Assert.isTrue(AuthorizationGrantType.TOKEN_EXCHANGE.equals(clientRegistration.getAuthorizationGrantType()),
6681
"clientRegistration.authorizationGrantType must be AuthorizationGrantType.TOKEN_EXCHANGE");
6782
Assert.notNull(subjectToken, "subjectToken cannot be null");
6883
this.subjectToken = subjectToken;
6984
this.actorToken = actorToken;
85+
this.additionalParameters = additionalParameters;
7086
}
7187

7288
/**
@@ -85,6 +101,14 @@ public OAuth2Token getActorToken() {
85101
return this.actorToken;
86102
}
87103

104+
/**
105+
* Returns the {@link Map additional parameters}.
106+
* @return the {@link Map additional parameters}
107+
*/
108+
public Map<String, Object> getAdditionalParameters() {
109+
return this.additionalParameters;
110+
}
111+
88112
/**
89113
* Populate default parameters for the Token Exchange Grant.
90114
* @param grantRequest the authorization grant request

0 commit comments

Comments
 (0)