Skip to content

Commit 9adec8e

Browse files
graziangmposolda
authored andcommitted
Specification parameters tests for standard token exchange v2
Closes #37114 Signed-off-by: Giuseppe Graziano <[email protected]> request and response parameters tests for token exchange Closes #37114 Signed-off-by: Giuseppe Graziano <[email protected]>
1 parent 510f8f7 commit 9adec8e

File tree

3 files changed

+102
-12
lines changed

3 files changed

+102
-12
lines changed

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/oauth/OAuthClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ public TokenExchangeRequest tokenExchangeRequest(String subjectToken) {
291291
return new TokenExchangeRequest(subjectToken, this);
292292
}
293293

294+
public TokenExchangeRequest tokenExchangeRequest(String subjectToken, String subjectTokenType) {
295+
return new TokenExchangeRequest(subjectToken, subjectTokenType, this);
296+
}
297+
294298
/**
295299
* @deprecated Set clientId and clientSecret using {@link #client(String, String)} and use {@link #tokenExchangeRequest(String)}
296300
*/

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/oauth/TokenExchangeRequest.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,25 @@
77
import java.io.IOException;
88
import java.util.List;
99
import java.util.Map;
10+
import java.util.Optional;
1011

1112
public class TokenExchangeRequest extends AbstractHttpPostRequest<TokenExchangeRequest, AccessTokenResponse> {
1213

1314
private final String subjectToken;
15+
private final String subjectTokenType;
1416
private List<String> audience;
1517
private Map<String, String> additionalParams;
1618

1719
TokenExchangeRequest(String subjectToken, OAuthClient client) {
1820
super(client);
1921
this.subjectToken = subjectToken;
22+
this.subjectTokenType = OAuth2Constants.ACCESS_TOKEN_TYPE;
23+
}
24+
25+
TokenExchangeRequest(String subjectToken, String subjectTokenType, OAuthClient client) {
26+
super(client);
27+
this.subjectToken = subjectToken;
28+
this.subjectTokenType = subjectTokenType;
2029
}
2130

2231
@Override
@@ -38,7 +47,7 @@ protected void initRequest() {
3847
parameter(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE);
3948

4049
parameter(OAuth2Constants.SUBJECT_TOKEN, subjectToken);
41-
parameter(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE);
50+
parameter(OAuth2Constants.SUBJECT_TOKEN_TYPE, subjectTokenType != null ? subjectTokenType : OAuth2Constants.ACCESS_TOKEN_TYPE);
4251

4352
if (audience != null) {
4453
audience.forEach(a -> parameter(OAuth2Constants.AUDIENCE, a));

testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/tokenexchange/StandardTokenExchangeV2Test.java

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@
3636
import org.keycloak.representations.idm.ClientRepresentation;
3737
import org.keycloak.representations.idm.RealmRepresentation;
3838
import org.keycloak.testsuite.AbstractKeycloakTest;
39-
import org.keycloak.testsuite.Assert;
4039
import org.keycloak.testsuite.AssertEvents;
4140
import org.keycloak.testsuite.admin.ApiUtil;
4241
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
4342
import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected;
4443
import org.keycloak.testsuite.pages.ConsentPage;
4544
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
4645
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
46+
import org.keycloak.testsuite.util.oauth.TokenExchangeRequest;
4747
import org.keycloak.util.TokenUtil;
4848

4949
import java.util.Collections;
@@ -110,6 +110,83 @@ private AccessTokenResponse tokenExchange(String subjectToken, String clientId,
110110
return oauth.tokenExchangeRequest(subjectToken).client(clientId, secret).audience(audience).additionalParams(additionalParams).send();
111111
}
112112

113+
@Test
114+
@UncaughtServerErrorExpected
115+
public void testSubjectTokenType() throws Exception {
116+
oauth.realm(TEST);
117+
String accessToken = resourceOwnerLogin("john", "password", "subject-client", "secret");
118+
119+
TokenExchangeRequest request = oauth.tokenExchangeRequest(accessToken, OAuth2Constants.ACCESS_TOKEN_TYPE);
120+
AccessTokenResponse response = request.send();
121+
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
122+
123+
request = oauth.tokenExchangeRequest(accessToken, OAuth2Constants.REFRESH_TOKEN_TYPE);
124+
response = request.send();
125+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
126+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
127+
128+
request = oauth.tokenExchangeRequest(accessToken, OAuth2Constants.ID_TOKEN_TYPE);
129+
response = request.send();
130+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
131+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
132+
133+
request = oauth.tokenExchangeRequest(accessToken, OAuth2Constants.SAML2_TOKEN_TYPE);
134+
response = request.send();
135+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
136+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
137+
138+
request = oauth.tokenExchangeRequest(accessToken, OAuth2Constants.JWT_TOKEN_TYPE);
139+
response = request.send();
140+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
141+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
142+
143+
request = oauth.tokenExchangeRequest(accessToken, "WRONG_TOKEN_TYPE");
144+
response = request.send();
145+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
146+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
147+
}
148+
149+
@Test
150+
@UncaughtServerErrorExpected
151+
public void testRequestedTokenType() throws Exception {
152+
oauth.realm(TEST);
153+
String accessToken = resourceOwnerLogin("john", "password", "subject-client", "secret");
154+
155+
AccessTokenResponse response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE));
156+
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
157+
assertNotNull(response.getAccessToken());
158+
assertEquals(TokenUtil.TOKEN_TYPE_BEARER, response.getTokenType());
159+
assertEquals(OAuth2Constants.ACCESS_TOKEN_TYPE, response.getIssuedTokenType());
160+
161+
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.REFRESH_TOKEN_TYPE));
162+
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
163+
assertNotNull(response.getAccessToken());
164+
assertEquals(TokenUtil.TOKEN_TYPE_BEARER, response.getTokenType());
165+
assertEquals(OAuth2Constants.REFRESH_TOKEN_TYPE, response.getIssuedTokenType());
166+
167+
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ID_TOKEN_TYPE));
168+
assertEquals(Response.Status.OK.getStatusCode(), response.getStatusCode());
169+
assertNotNull(response.getAccessToken());
170+
assertEquals(TokenUtil.TOKEN_TYPE_NA, response.getTokenType());
171+
assertEquals(OAuth2Constants.ID_TOKEN_TYPE, response.getIssuedTokenType());
172+
173+
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE));
174+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
175+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
176+
assertEquals("requested_token_type unsupported", response.getErrorDescription());
177+
178+
//TODO: saml token type should not be supported
179+
// response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.SAML2_TOKEN_TYPE));
180+
// assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
181+
// assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
182+
//assertEquals("requested_token_type unsupported", response.getErrorDescription());
183+
184+
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, "WRONG_TOKEN_TYPE"));
185+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
186+
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
187+
assertEquals("requested_token_type unsupported", response.getErrorDescription());
188+
}
189+
113190
@Test
114191
@UncaughtServerErrorExpected
115192
public void testExchange() throws Exception {
@@ -158,7 +235,7 @@ public void testExchangeForIdToken() throws Exception {
158235
.parse().getToken();
159236
assertEquals(TokenUtil.TOKEN_TYPE_BEARER, exchangedToken.getType());
160237

161-
Assert.assertNotNull("ID Token is null, but was expected to be present", response.getIdToken());
238+
assertNotNull("ID Token is null, but was expected to be present", response.getIdToken());
162239
IDToken exchangedIdToken = TokenVerifier.create(response.getIdToken(), IDToken.class)
163240
.parse().getToken();
164241
assertEquals(TokenUtil.TOKEN_TYPE_ID, exchangedIdToken.getType());
@@ -170,15 +247,15 @@ public void testExchangeForIdToken() throws Exception {
170247
oauth.scope(null);
171248
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE));
172249
assertEquals(OAuth2Constants.ACCESS_TOKEN_TYPE, response.getIssuedTokenType());
173-
Assert.assertNotNull(response.getAccessToken());
174-
Assert.assertNull("ID Token was present, but should not be present", response.getIdToken());
250+
assertNotNull(response.getAccessToken());
251+
assertNull("ID Token was present, but should not be present", response.getIdToken());
175252

176253
// Exchange request requesting id-token. ID Token should be issued inside "access_token" parameter (as per token-exchange specification https://datatracker.ietf.org/doc/html/rfc8693#name-successful-response - parameter "access_token")
177254
response = tokenExchange(accessToken, "requester-client", "secret", null, Map.of(OAuth2Constants.REQUESTED_TOKEN_TYPE, OAuth2Constants.ID_TOKEN_TYPE));
178255
assertEquals(OAuth2Constants.ID_TOKEN_TYPE, response.getIssuedTokenType());
179256
assertEquals(TokenUtil.TOKEN_TYPE_NA, response.getTokenType());
180-
Assert.assertNotNull(response.getAccessToken());
181-
Assert.assertNull("ID Token was present, but should not be present", response.getIdToken());
257+
assertNotNull(response.getAccessToken());
258+
assertNull("ID Token was present, but should not be present", response.getIdToken());
182259

183260
exchangedIdToken = TokenVerifier.create(response.getAccessToken(), IDToken.class)
184261
.parse().getToken();
@@ -196,13 +273,13 @@ public void testExchangeUsingServiceAccount() throws Exception {
196273
String accessToken = response.getAccessToken();
197274
TokenVerifier<AccessToken> accessTokenVerifier = TokenVerifier.create(accessToken, AccessToken.class);
198275
AccessToken token = accessTokenVerifier.parse().getToken();
199-
Assert.assertNull(token.getSessionId());
276+
assertNull(token.getSessionId());
200277
response = tokenExchange(accessToken, "requester-client", "secret", null, null);
201278
assertEquals(OAuth2Constants.ACCESS_TOKEN_TYPE, response.getIssuedTokenType());
202279
String exchangedTokenString = response.getAccessToken();
203280
TokenVerifier<AccessToken> verifier = TokenVerifier.create(exchangedTokenString, AccessToken.class);
204281
AccessToken exchangedToken = verifier.parse().getToken();
205-
Assert.assertNull(exchangedToken.getSessionId());
282+
assertNull(exchangedToken.getSessionId());
206283
assertEquals("requester-client", exchangedToken.getIssuedFor());
207284

208285
}
@@ -272,9 +349,9 @@ public void testClientExchangeToItselfWithConsents() throws Exception {
272349
public void testExchangeWithPublicClient() throws Exception {
273350
String accessToken = resourceOwnerLogin("john", "password","subject-client", "secret");
274351
AccessTokenResponse response = tokenExchange(accessToken, "requester-client-public", null, null, null);
275-
org.junit.Assert.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
276-
org.junit.Assert.assertEquals(OAuthErrorException.INVALID_CLIENT, response.getError());
277-
org.junit.Assert.assertEquals("Public client is not allowed to exchange token", response.getErrorDescription());
352+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusCode());
353+
assertEquals(OAuthErrorException.INVALID_CLIENT, response.getError());
354+
assertEquals("Public client is not allowed to exchange token", response.getErrorDescription());
278355
}
279356

280357
@Test

0 commit comments

Comments
 (0)