blob: 1a6d89f92c413a622013996d7f5bf80bbdfbd673 [file] [log] [blame]
margarethaa0486272018-04-12 19:59:31 +02001package de.ids_mannheim.korap.web.controller;
2
margaretha05122312018-04-16 15:01:34 +02003import static org.junit.Assert.assertEquals;
margarethaf839dde2018-04-16 17:52:57 +02004import static org.junit.Assert.assertNotNull;
margaretha6f0b7382018-11-21 17:42:02 +01005import static org.junit.Assert.assertTrue;
margarethafb027f92018-04-23 20:00:13 +02006
7import java.net.URI;
margaretha977fabe2022-04-28 09:23:47 +02008import java.time.ZonedDateTime;
margaretha93e602e2019-08-07 15:19:56 +02009import java.util.Set;
margaretha05122312018-04-16 15:01:34 +020010
abcpro173fe8f22022-11-08 19:56:52 +000011import javax.ws.rs.client.Entity;
12import javax.ws.rs.core.Form;
margarethaa0486272018-04-12 19:59:31 +020013import javax.ws.rs.core.MultivaluedMap;
margaretha64ea6452023-01-30 12:39:19 +010014import javax.ws.rs.core.Response;
margaretha05122312018-04-16 15:01:34 +020015import javax.ws.rs.core.Response.Status;
margarethaa0486272018-04-12 19:59:31 +020016
17import org.apache.http.entity.ContentType;
margaretha05122312018-04-16 15:01:34 +020018import org.apache.oltu.oauth2.common.error.OAuthError;
margaretha03b82862018-07-12 20:09:26 +020019import org.apache.oltu.oauth2.common.message.types.GrantType;
margarethaf839dde2018-04-16 17:52:57 +020020import org.apache.oltu.oauth2.common.message.types.TokenType;
margarethaa0486272018-04-12 19:59:31 +020021import org.junit.Test;
margarethaa0486272018-04-12 19:59:31 +020022
margaretha05122312018-04-16 15:01:34 +020023import com.fasterxml.jackson.databind.JsonNode;
margarethaa0486272018-04-12 19:59:31 +020024import com.google.common.net.HttpHeaders;
margarethaa0486272018-04-12 19:59:31 +020025
26import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
margaretha43aceb52019-11-21 12:58:53 +010027import de.ids_mannheim.korap.config.Attributes;
margarethaa0486272018-04-12 19:59:31 +020028import de.ids_mannheim.korap.exceptions.KustvaktException;
margarethabe4c5c92018-05-03 18:55:49 +020029import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
margaretha93e602e2019-08-07 15:19:56 +020030import de.ids_mannheim.korap.oauth2.entity.AccessScope;
31import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
margaretha05122312018-04-16 15:01:34 +020032import de.ids_mannheim.korap.utils.JsonUtils;
margarethaa0486272018-04-12 19:59:31 +020033
34/**
35 * @author margaretha
36 *
37 */
margarethaf0085122018-08-16 16:19:53 +020038public class OAuth2ControllerTest extends OAuth2TestBase {
margarethaa0486272018-04-12 19:59:31 +020039
margarethaf0085122018-08-16 16:19:53 +020040 public String userAuthHeader;
margaretha35074692021-03-26 18:11:59 +010041
margarethaf0085122018-08-16 16:19:53 +020042 public OAuth2ControllerTest () throws KustvaktException {
43 userAuthHeader = HttpAuthorizationHandler
44 .createBasicAuthorizationHeaderValue("dory", "password");
margarethaf839dde2018-04-16 17:52:57 +020045 }
46
margarethafb027f92018-04-23 20:00:13 +020047 @Test
48 public void testAuthorizeConfidentialClient () throws KustvaktException {
margarethaffb89502022-04-20 12:03:16 +020049 // with registered redirect URI
abcpro173fe8f22022-11-08 19:56:52 +000050 Response response =
margarethaffb89502022-04-20 12:03:16 +020051 requestAuthorizationCode("code", confidentialClientId, "",
52 "match_info search client_info", state, userAuthHeader);
margarethadc515072018-08-03 17:01:19 +020053
54 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
55 response.getStatus());
56 URI redirectUri = response.getLocation();
margarethaffb89502022-04-20 12:03:16 +020057 MultivaluedMap<String, String> params =
58 getQueryParamsFromURI(redirectUri);
margarethadc515072018-08-03 17:01:19 +020059 assertNotNull(params.getFirst("code"));
margaretha9436ebe2022-04-22 11:48:37 +020060 assertEquals(state, params.getFirst("state"));
margarethaffb89502022-04-20 12:03:16 +020061 assertEquals("match_info search client_info", params.getFirst("scope"));
margarethadc515072018-08-03 17:01:19 +020062 }
63
64 @Test
65 public void testAuthorizePublicClient () throws KustvaktException {
margarethaffb89502022-04-20 12:03:16 +020066 // with registered redirect URI
margarethad67b4272022-04-11 17:34:19 +020067 String code = requestAuthorizationCode(publicClientId, userAuthHeader);
margarethaf0085122018-08-16 16:19:53 +020068 assertNotNull(code);
margarethafb027f92018-04-23 20:00:13 +020069 }
70
71 @Test
margaretha9436ebe2022-04-22 11:48:37 +020072 public void testAuthorizeWithRedirectUri () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +000073 Response response =
margarethaffb89502022-04-20 12:03:16 +020074 requestAuthorizationCode("code", publicClientId2,
margaretha64ea6452023-01-30 12:39:19 +010075 "https://public.com/redirect", "search match_info",
76 "", userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +020077 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
78 response.getStatus());
margaretha9436ebe2022-04-22 11:48:37 +020079
margarethaffb89502022-04-20 12:03:16 +020080 URI redirectUri = response.getLocation();
margaretha9436ebe2022-04-22 11:48:37 +020081 assertEquals("https", redirectUri.getScheme());
82 assertEquals("public.com", redirectUri.getHost());
83 assertEquals("/redirect", redirectUri.getPath());
margarethaffb89502022-04-20 12:03:16 +020084
85 String[] queryParts = redirectUri.getQuery().split("&");
86 assertTrue(queryParts[0].startsWith("code="));
margaretha9436ebe2022-04-22 11:48:37 +020087 assertEquals("scope=match_info+search", queryParts[1]);
margarethaffb89502022-04-20 12:03:16 +020088 }
margaretha9436ebe2022-04-22 11:48:37 +020089
margarethaffb89502022-04-20 12:03:16 +020090 @Test
91 public void testAuthorizeWithoutScope () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +000092 Response response = requestAuthorizationCode("code",
margarethaffb89502022-04-20 12:03:16 +020093 confidentialClientId, "", "", "", userAuthHeader);
94 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
95 response.getStatus());
96
97 URI redirectUri = response.getLocation();
98 assertEquals(redirectUri.getScheme(), "https");
99 assertEquals(redirectUri.getHost(), "third.party.com");
100 assertEquals(redirectUri.getPath(), "/confidential/redirect");
101
102 String[] queryParts = redirectUri.getQuery().split("&");
margaretha64ea6452023-01-30 12:39:19 +0100103 assertTrue(queryParts[0].startsWith("error_description=scope+is+required"));
104 assertEquals(queryParts[1], "error=invalid_scope");
margarethaffb89502022-04-20 12:03:16 +0200105 }
106
107 @Test
108 public void testAuthorizeMissingClientId () throws KustvaktException {
margaretha64ea6452023-01-30 12:39:19 +0100109 Response response = requestAuthorizationCode("code", "", "", "search",
margarethaffb89502022-04-20 12:03:16 +0200110 "", userAuthHeader);
111 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000112 String entity = response.readEntity(String.class);
margarethaffb89502022-04-20 12:03:16 +0200113 JsonNode node = JsonUtils.readTree(entity);
114 assertEquals("Missing parameters: client_id",
115 node.at("/error_description").asText());
116 }
117
118 @Test
119 public void testAuthorizeMissingRedirectUri () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000120 Response response = requestAuthorizationCode("code",
margaretha64ea6452023-01-30 12:39:19 +0100121 publicClientId2, "", "search", state, userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +0200122 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
123
abcpro173fe8f22022-11-08 19:56:52 +0000124 String entity = response.readEntity(String.class);
margarethaffb89502022-04-20 12:03:16 +0200125 JsonNode node = JsonUtils.readTree(entity);
126 assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
127 node.at("/error").asText());
margaretha590acf62022-05-06 10:44:56 +0200128 assertEquals("Missing parameter: redirect URI",
margarethaffb89502022-04-20 12:03:16 +0200129 node.at("/error_description").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200130 assertEquals(state, node.at("/state").asText());
margarethaffb89502022-04-20 12:03:16 +0200131 }
132
133 @Test
margaretha0034a0c2022-05-17 10:37:41 +0200134 public void testAuthorizeMissingResponseType() throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000135 Response response = requestAuthorizationCode("",
margaretha64ea6452023-01-30 12:39:19 +0100136 confidentialClientId, "", "search", "", userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +0200137 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
138 response.getStatus());
139
140 assertEquals("https://third.party.com/confidential/redirect?"
141 + "error_description=Missing+parameters%3A+response_type&"
142 + "error=invalid_request", response.getLocation().toString());
143 }
margaretha0034a0c2022-05-17 10:37:41 +0200144
145 @Test
146 public void testAuthorizeMissingResponseTypeWithoutClientId () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000147 Response response = requestAuthorizationCode("",
margaretha64ea6452023-01-30 12:39:19 +0100148 "", "", "search", "", userAuthHeader);
margaretha0034a0c2022-05-17 10:37:41 +0200149
150 assertEquals(Status.BAD_REQUEST.getStatusCode(),
151 response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000152 String entity = response.readEntity(String.class);
margaretha0034a0c2022-05-17 10:37:41 +0200153 JsonNode node = JsonUtils.readTree(entity);
154
155 assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
156 node.at("/error").asText());
157 assertEquals("Missing parameters: response_type client_id",
158 node.at("/error_description").asText());
159 }
margarethaffb89502022-04-20 12:03:16 +0200160
161 @Test
162 public void testAuthorizeInvalidClientId () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000163 Response response = requestAuthorizationCode("code",
margaretha64ea6452023-01-30 12:39:19 +0100164 "unknown-client-id", "", "search", "", userAuthHeader);
margaretha9436ebe2022-04-22 11:48:37 +0200165 assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000166 String entity = response.readEntity(String.class);
margarethaffb89502022-04-20 12:03:16 +0200167 JsonNode node = JsonUtils.readTree(entity);
margaretha9436ebe2022-04-22 11:48:37 +0200168 assertEquals(OAuth2Error.INVALID_CLIENT, node.at("/error").asText());
margaretha7da23902022-05-02 08:38:45 +0200169 assertEquals("Unknown client: unknown-client-id",
margarethaffb89502022-04-20 12:03:16 +0200170 node.at("/error_description").asText());
171 }
172
173 @Test
margaretha9436ebe2022-04-22 11:48:37 +0200174 public void testAuthorizeDifferentRedirectUri () throws KustvaktException {
margarethafb027f92018-04-23 20:00:13 +0200175 String redirectUri = "https://different.uri/redirect";
abcpro173fe8f22022-11-08 19:56:52 +0000176 Response response = requestAuthorizationCode("code",
margaretha64ea6452023-01-30 12:39:19 +0100177 confidentialClientId, redirectUri, "search", state, userAuthHeader);
abcpro173fe8f22022-11-08 19:56:52 +0000178 testInvalidRedirectUri(response.readEntity(String.class), true,
margaretha9436ebe2022-04-22 11:48:37 +0200179 response.getStatus());
180 }
margarethafb027f92018-04-23 20:00:13 +0200181
margaretha9436ebe2022-04-22 11:48:37 +0200182 @Test
183 public void testAuthorizeWithRedirectUriLocalhost ()
184 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000185 Response response =
margaretha9436ebe2022-04-22 11:48:37 +0200186 requestAuthorizationCode("code", publicClientId2,
margaretha64ea6452023-01-30 12:39:19 +0100187 "http://localhost:1410", "search", state, userAuthHeader);
abcpro173fe8f22022-11-08 19:56:52 +0000188 testInvalidRedirectUri(response.readEntity(String.class), true,
margaretha9436ebe2022-04-22 11:48:37 +0200189 response.getStatus()); }
margarethafb027f92018-04-23 20:00:13 +0200190
margaretha9436ebe2022-04-22 11:48:37 +0200191 @Test
192 public void testAuthorizeWithRedirectUriFragment ()
193 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000194 Response response = requestAuthorizationCode("code",
margaretha64ea6452023-01-30 12:39:19 +0100195 publicClientId2, "http://public.com/index.html#redirect", "search",
margaretha9436ebe2022-04-22 11:48:37 +0200196 state, userAuthHeader);
abcpro173fe8f22022-11-08 19:56:52 +0000197 testInvalidRedirectUri(response.readEntity(String.class), true,
margaretha9436ebe2022-04-22 11:48:37 +0200198 response.getStatus());
199 }
200
201 @Test
202 public void testAuthorizeInvalidRedirectUri () throws KustvaktException {
203 // host not allowed by Apache URI Validator
204 String redirectUri = "https://public.uri/redirect";
abcpro173fe8f22022-11-08 19:56:52 +0000205 Response response = requestAuthorizationCode("code",
margaretha64ea6452023-01-30 12:39:19 +0100206 publicClientId2, redirectUri, "search", state, userAuthHeader);
abcpro173fe8f22022-11-08 19:56:52 +0000207 testInvalidRedirectUri(response.readEntity(String.class), true,
margaretha9436ebe2022-04-22 11:48:37 +0200208 response.getStatus());
margarethafb027f92018-04-23 20:00:13 +0200209 }
margarethafb027f92018-04-23 20:00:13 +0200210
211 @Test
212 public void testAuthorizeInvalidResponseType () throws KustvaktException {
margarethaffb89502022-04-20 12:03:16 +0200213 // without redirect URI in the request
abcpro173fe8f22022-11-08 19:56:52 +0000214 Response response = requestAuthorizationCode("string",
margaretha64ea6452023-01-30 12:39:19 +0100215 confidentialClientId, "", "search", state, userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +0200216 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
217 response.getStatus());
218
219 assertEquals("https://third.party.com/confidential/redirect?"
220 + "error_description=Invalid+response_type+parameter+"
221 + "value&state=thisIsMyState&" + "error=invalid_request",
222 response.getLocation().toString());
223
224 // with redirect URI, and no registered redirect URI
225 response = requestAuthorizationCode("string", publicClientId2,
226 "https://public.client.com/redirect", "", state,
227 userAuthHeader);
228 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
229 response.getStatus());
230
231 assertEquals("https://public.client.com/redirect?error_description="
232 + "Invalid+response_type+parameter+value&state=thisIsMyState&"
233 + "error=invalid_request", response.getLocation().toString());
234
235 // with different redirect URI
236 String redirectUri = "https://different.uri/redirect";
237 response = requestAuthorizationCode("string", confidentialClientId,
238 redirectUri, "", state, userAuthHeader);
margarethafb027f92018-04-23 20:00:13 +0200239 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
240
abcpro173fe8f22022-11-08 19:56:52 +0000241 JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
margarethafb027f92018-04-23 20:00:13 +0200242 assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
243 node.at("/error").asText());
margarethaffb89502022-04-20 12:03:16 +0200244 assertEquals("Invalid redirect URI",
245 node.at("/error_description").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200246 assertEquals(state, node.at("/state").asText());
margarethaffb89502022-04-20 12:03:16 +0200247
248 // without redirect URI in the request and no registered
249 // redirect URI
250 response = requestAuthorizationCode("string", publicClientId2, "", "",
251 state, userAuthHeader);
252 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
253
abcpro173fe8f22022-11-08 19:56:52 +0000254 node = JsonUtils.readTree(response.readEntity(String.class));
margarethaffb89502022-04-20 12:03:16 +0200255 assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
256 node.at("/error").asText());
margaretha590acf62022-05-06 10:44:56 +0200257 assertEquals("Missing parameter: redirect URI",
margarethafb027f92018-04-23 20:00:13 +0200258 node.at("/error_description").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200259 assertEquals(state, node.at("/state").asText());
margarethafb027f92018-04-23 20:00:13 +0200260 }
261
margarethabe4c5c92018-05-03 18:55:49 +0200262 @Test
263 public void testAuthorizeInvalidScope () throws KustvaktException {
margarethad67b4272022-04-11 17:34:19 +0200264 String scope = "read_address";
abcpro173fe8f22022-11-08 19:56:52 +0000265 Response response = requestAuthorizationCode("code",
margarethad67b4272022-04-11 17:34:19 +0200266 confidentialClientId, "", scope, state, userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +0200267 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
268 response.getStatus());
margarethabe4c5c92018-05-03 18:55:49 +0200269
margarethaffb89502022-04-20 12:03:16 +0200270 assertEquals(
271 "https://third.party.com/confidential/redirect?"
margaretha9436ebe2022-04-22 11:48:37 +0200272 + "error_description=read_address+is+an+invalid+scope&"
273 + "state=thisIsMyState&error=invalid_scope",
margarethaffb89502022-04-20 12:03:16 +0200274 response.getLocation().toString());
275 }
276
277 @Test
278 public void testAuthorizeUnsupportedTokenResponseType ()
279 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000280 Response response = requestAuthorizationCode("token",
margaretha64ea6452023-01-30 12:39:19 +0100281 confidentialClientId, "", "search", state, userAuthHeader);
margarethaffb89502022-04-20 12:03:16 +0200282 assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
283 response.getStatus());
284
285 assertEquals("https://third.party.com/confidential/redirect?"
286 + "error_description=response_type+token+is+not+"
287 + "supported&state=thisIsMyState&error=unsupported_"
288 + "response_type", response.getLocation().toString());
margarethabe4c5c92018-05-03 18:55:49 +0200289 }
290
margaretha6374f722018-04-17 18:45:57 +0200291 @Test
margarethaf0085122018-08-16 16:19:53 +0200292 public void testRequestTokenAuthorizationPublic ()
293 throws KustvaktException {
margaretha9436ebe2022-04-22 11:48:37 +0200294 String code = requestAuthorizationCode(publicClientId, userAuthHeader);
margaretha835178d2018-08-15 19:04:03 +0200295
abcpro173fe8f22022-11-08 19:56:52 +0000296 Response response = requestTokenWithAuthorizationCodeAndForm(
margarethaf0085122018-08-16 16:19:53 +0200297 publicClientId, clientSecret, code);
abcpro173fe8f22022-11-08 19:56:52 +0000298 String entity = response.readEntity(String.class);
margaretha835178d2018-08-15 19:04:03 +0200299 JsonNode node = JsonUtils.readTree(entity);
margarethaf0085122018-08-16 16:19:53 +0200300
margaretha835178d2018-08-15 19:04:03 +0200301 String accessToken = node.at("/access_token").asText();
margarethaf0085122018-08-16 16:19:53 +0200302
margaretha835178d2018-08-15 19:04:03 +0200303 assertEquals(TokenType.BEARER.toString(),
304 node.at("/token_type").asText());
margaretha0afd44a2020-02-05 10:49:21 +0100305 assertEquals(31536000, node.at("/expires_in").asInt());
margarethaf0085122018-08-16 16:19:53 +0200306
margaretha35074692021-03-26 18:11:59 +0100307 testRevokeToken(accessToken, publicClientId, null, ACCESS_TOKEN_TYPE);
margarethaf0085122018-08-16 16:19:53 +0200308
margaretha0afd44a2020-02-05 10:49:21 +0100309 assertTrue(node.at("/refresh_token").isMissingNode());
margaretha835178d2018-08-15 19:04:03 +0200310 }
margarethaf0085122018-08-16 16:19:53 +0200311
margaretha835178d2018-08-15 19:04:03 +0200312 @Test
margarethaa452c5e2018-04-25 22:48:09 +0200313 public void testRequestTokenAuthorizationConfidential ()
314 throws KustvaktException {
margarethabe4c5c92018-05-03 18:55:49 +0200315
margarethad67b4272022-04-11 17:34:19 +0200316 String scope = "search";
abcpro173fe8f22022-11-08 19:56:52 +0000317 Response response = requestAuthorizationCode("code",
margarethad67b4272022-04-11 17:34:19 +0200318 confidentialClientId, "", scope, state, userAuthHeader);
margarethabe4c5c92018-05-03 18:55:49 +0200319 MultivaluedMap<String, String> params =
margarethaffb89502022-04-20 12:03:16 +0200320 getQueryParamsFromURI(response.getLocation());
margarethabe4c5c92018-05-03 18:55:49 +0200321 String code = params.get("code").get(0);
322 String scopes = params.get("scope").get(0);
323
margaretha20f31232018-07-09 17:49:39 +0200324 assertEquals(scopes, "search");
margarethabe4c5c92018-05-03 18:55:49 +0200325
margarethaf0085122018-08-16 16:19:53 +0200326 response = requestTokenWithAuthorizationCodeAndForm(
327 confidentialClientId, clientSecret, code);
abcpro173fe8f22022-11-08 19:56:52 +0000328 String entity = response.readEntity(String.class);
margarethaa452c5e2018-04-25 22:48:09 +0200329 JsonNode node = JsonUtils.readTree(entity);
330 assertNotNull(node.at("/access_token").asText());
331 assertNotNull(node.at("/refresh_token").asText());
332 assertEquals(TokenType.BEARER.toString(),
333 node.at("/token_type").asText());
334 assertNotNull(node.at("/expires_in").asText());
margarethabe4c5c92018-05-03 18:55:49 +0200335
margarethaf0085122018-08-16 16:19:53 +0200336 testRequestTokenWithUsedAuthorization(code);
margaretha35074692021-03-26 18:11:59 +0100337
margaretha6f0b7382018-11-21 17:42:02 +0100338 String refreshToken = node.at("/refresh_token").asText();
margaretha977fabe2022-04-28 09:23:47 +0200339
340 testRefreshTokenExpiry(refreshToken);
margaretha0afd44a2020-02-05 10:49:21 +0100341 testRequestRefreshTokenInvalidScope(confidentialClientId, refreshToken);
342 testRequestRefreshTokenInvalidClient(refreshToken);
343 testRequestRefreshTokenInvalidRefreshToken(confidentialClientId);
margaretha35074692021-03-26 18:11:59 +0100344
margaretha9436ebe2022-04-22 11:48:37 +0200345 testRequestRefreshToken(confidentialClientId, clientSecret,
346 refreshToken);
margarethaa452c5e2018-04-25 22:48:09 +0200347 }
margarethabe4c5c92018-05-03 18:55:49 +0200348
margarethaf0085122018-08-16 16:19:53 +0200349 private void testRequestTokenWithUsedAuthorization (String code)
350 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000351 Response response = requestTokenWithAuthorizationCodeAndForm(
margarethaf0085122018-08-16 16:19:53 +0200352 confidentialClientId, clientSecret, code);
abcpro173fe8f22022-11-08 19:56:52 +0000353 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200354
355 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
356
357 JsonNode node = JsonUtils.readTree(entity);
358 assertEquals(OAuthError.TokenResponse.INVALID_GRANT,
359 node.at("/error").asText());
360 assertEquals("Invalid authorization",
361 node.at("/error_description").asText());
362 }
363
364 @Test
365 public void testRequestTokenInvalidAuthorizationCode ()
366 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000367 Response response = requestTokenWithAuthorizationCodeAndForm(
margarethaf0085122018-08-16 16:19:53 +0200368 confidentialClientId, clientSecret, "blahblah");
abcpro173fe8f22022-11-08 19:56:52 +0000369 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200370
371 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
372
373 JsonNode node = JsonUtils.readTree(entity);
374 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
375 node.at("/error").asText());
376 }
377
378 @Test
379 public void testRequestTokenAuthorizationReplyAttack ()
380 throws KustvaktException {
margarethad67b4272022-04-11 17:34:19 +0200381 String redirect_uri = "https://third.party.com/confidential/redirect";
382 String scope = "search";
margarethabe4c5c92018-05-03 18:55:49 +0200383
abcpro173fe8f22022-11-08 19:56:52 +0000384 Response response =
margarethad67b4272022-04-11 17:34:19 +0200385 requestAuthorizationCode("code", confidentialClientId,
386 redirect_uri, scope, state, userAuthHeader);
margarethabe4c5c92018-05-03 18:55:49 +0200387 MultivaluedMap<String, String> params =
margarethaffb89502022-04-20 12:03:16 +0200388 getQueryParamsFromURI(response.getLocation());
margarethabe4c5c92018-05-03 18:55:49 +0200389 String code = params.get("code").get(0);
390
391 testRequestTokenAuthorizationInvalidClient(code);
392 testRequestTokenAuthorizationInvalidRedirectUri(code);
margarethad67b4272022-04-11 17:34:19 +0200393 testRequestTokenAuthorizationRevoked(code, redirect_uri);
margarethabe4c5c92018-05-03 18:55:49 +0200394 }
395
396 private void testRequestTokenAuthorizationInvalidClient (String code)
397 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000398 Response response = requestTokenWithAuthorizationCodeAndForm(
margarethaf0085122018-08-16 16:19:53 +0200399 confidentialClientId, "wrong_secret", code);
abcpro173fe8f22022-11-08 19:56:52 +0000400 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200401 JsonNode node = JsonUtils.readTree(entity);
402 assertEquals(OAuth2Error.INVALID_CLIENT, node.at("/error").asText());
403 }
404
405 private void testRequestTokenAuthorizationInvalidRedirectUri (String code)
406 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000407 Form tokenForm = new Form();
408 tokenForm.param("grant_type", "authorization_code");
409 tokenForm.param("client_id", confidentialClientId);
410 tokenForm.param("client_secret", "secret");
411 tokenForm.param("code", code);
412 tokenForm.param("redirect_uri", "https://blahblah.com");
margarethabe4c5c92018-05-03 18:55:49 +0200413
abcpro173fe8f22022-11-08 19:56:52 +0000414 Response response = requestToken(tokenForm);
415 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200416 JsonNode node = JsonUtils.readTree(entity);
417 assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
418 }
419
420 private void testRequestTokenAuthorizationRevoked (String code, String uri)
421 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000422 Form tokenForm = new Form();
423 tokenForm.param("grant_type", "authorization_code");
424 tokenForm.param("client_id", confidentialClientId);
425 tokenForm.param("client_secret", "secret");
426 tokenForm.param("code", code);
427 tokenForm.param("redirect_uri", uri);
margarethabe4c5c92018-05-03 18:55:49 +0200428
abcpro173fe8f22022-11-08 19:56:52 +0000429 Response response = requestToken(tokenForm);
430 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200431 JsonNode node = JsonUtils.readTree(entity);
432 assertEquals(OAuthError.TokenResponse.INVALID_GRANT,
433 node.at("/error").asText());
434 assertEquals("Invalid authorization",
435 node.at("/error_description").asText());
436 }
437
margarethaa452c5e2018-04-25 22:48:09 +0200438 @Test
margarethaf0085122018-08-16 16:19:53 +0200439 public void testRequestTokenPasswordGrantConfidentialSuper ()
margarethafb027f92018-04-23 20:00:13 +0200440 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000441 Response response =
margaretha230effb2018-11-29 17:28:18 +0100442 requestTokenWithDoryPassword(superClientId, clientSecret);
margaretha6374f722018-04-17 18:45:57 +0200443
margarethaf0085122018-08-16 16:19:53 +0200444 assertEquals(Status.OK.getStatusCode(), response.getStatus());
445
abcpro173fe8f22022-11-08 19:56:52 +0000446 String entity = response.readEntity(String.class);
margaretha6374f722018-04-17 18:45:57 +0200447 JsonNode node = JsonUtils.readTree(entity);
448 assertNotNull(node.at("/access_token").asText());
margaretha6374f722018-04-17 18:45:57 +0200449 assertEquals(TokenType.BEARER.toString(),
450 node.at("/token_type").asText());
451 assertNotNull(node.at("/expires_in").asText());
margaretha35074692021-03-26 18:11:59 +0100452
margaretha977fabe2022-04-28 09:23:47 +0200453 String refresh = node.at("/refresh_token").asText();
454 RefreshToken refreshToken =
455 refreshTokenDao.retrieveRefreshToken(refresh);
margaretha93e602e2019-08-07 15:19:56 +0200456 Set<AccessScope> scopes = refreshToken.getScopes();
457 assertEquals(1, scopes.size());
458 assertEquals("[all]", scopes.toString());
margaretha977fabe2022-04-28 09:23:47 +0200459
460 testRefreshTokenExpiry(refresh);
margaretha6374f722018-04-17 18:45:57 +0200461 }
462
463 @Test
margarethaf0085122018-08-16 16:19:53 +0200464 public void testRequestTokenPasswordGrantConfidentialNonSuper ()
465 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000466 Response response = requestTokenWithDoryPassword(
margaretha35074692021-03-26 18:11:59 +0100467 confidentialClientId, clientSecret);
abcpro173fe8f22022-11-08 19:56:52 +0000468 String entity = response.readEntity(String.class);
margarethaf0085122018-08-16 16:19:53 +0200469 assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
470
471 JsonNode node = JsonUtils.readTree(entity);
472 assertEquals(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT,
473 node.at("/error").asText());
474 assertEquals("Password grant is not allowed for third party clients",
475 node.at("/error_description").asText());
476 }
477
478 @Test
479 public void testRequestTokenPasswordGrantPublic ()
480 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000481 Response response =
margaretha35074692021-03-26 18:11:59 +0100482 requestTokenWithDoryPassword(publicClientId, "");
abcpro173fe8f22022-11-08 19:56:52 +0000483 String entity = response.readEntity(String.class);
margarethaf0085122018-08-16 16:19:53 +0200484
485 assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
486
487 JsonNode node = JsonUtils.readTree(entity);
488 assertEquals(OAuth2Error.UNAUTHORIZED_CLIENT,
489 node.at("/error").asText());
490 assertEquals("Password grant is not allowed for third party clients",
491 node.at("/error_description").asText());
492 }
493
494 @Test
margaretha00c28c02018-07-05 18:09:09 +0200495 public void testRequestTokenPasswordGrantAuthorizationHeader ()
496 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000497 Form form = new Form();
498 form.param("grant_type", "password");
499 form.param("client_id", superClientId);
500 form.param("username", "dory");
501 form.param("password", "password");
margaretha00c28c02018-07-05 18:09:09 +0200502
abcpro173fe8f22022-11-08 19:56:52 +0000503 Response response =
504 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000505 .request()
margaretha35074692021-03-26 18:11:59 +0100506 .header(HttpHeaders.AUTHORIZATION,
507 "Basic ZkNCYlFrQXlZekk0TnpVeE1nOnNlY3JldA==")
508 .header(HttpHeaders.CONTENT_TYPE,
509 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000510 .post(Entity.form(form));
511 String entity = response.readEntity(String.class);
margaretha00c28c02018-07-05 18:09:09 +0200512 JsonNode node = JsonUtils.readTree(entity);
513 assertNotNull(node.at("/access_token").asText());
514 assertNotNull(node.at("/refresh_token").asText());
515 assertEquals(TokenType.BEARER.toString(),
516 node.at("/token_type").asText());
517 assertNotNull(node.at("/expires_in").asText());
518 }
519
margaretha0a45be12018-07-12 15:06:30 +0200520 /**
521 * In case, client_id is specified both in Authorization header
522 * and request body, client_id in the request body is ignored.
523 *
524 * @throws KustvaktException
525 */
526 @Test
527 public void testRequestTokenPasswordGrantDifferentClientIds ()
528 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000529 Form form = new Form();
530 form.param("grant_type", "password");
531 form.param("client_id", "9aHsGW6QflV13ixNpez");
532 form.param("username", "dory");
533 form.param("password", "password");
margaretha0a45be12018-07-12 15:06:30 +0200534
abcpro173fe8f22022-11-08 19:56:52 +0000535 Response response =
536 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000537 .request()
margaretha35074692021-03-26 18:11:59 +0100538 .header(HttpHeaders.AUTHORIZATION,
539 "Basic ZkNCYlFrQXlZekk0TnpVeE1nOnNlY3JldA==")
540 .header(HttpHeaders.CONTENT_TYPE,
541 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000542 .post(Entity.form(form));
543 String entity = response.readEntity(String.class);
margaretha0a45be12018-07-12 15:06:30 +0200544 JsonNode node = JsonUtils.readTree(entity);
545 assertNotNull(node.at("/access_token").asText());
546 assertNotNull(node.at("/refresh_token").asText());
547 assertEquals(TokenType.BEARER.toString(),
548 node.at("/token_type").asText());
549 assertNotNull(node.at("/expires_in").asText());
550 }
551
margaretha00c28c02018-07-05 18:09:09 +0200552 @Test
margarethafb027f92018-04-23 20:00:13 +0200553 public void testRequestTokenPasswordGrantMissingClientSecret ()
554 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000555 Response response =
margaretha230effb2018-11-29 17:28:18 +0100556 requestTokenWithDoryPassword(confidentialClientId, "");
margarethafb027f92018-04-23 20:00:13 +0200557 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
margaretha6374f722018-04-17 18:45:57 +0200558
abcpro173fe8f22022-11-08 19:56:52 +0000559 String entity = response.readEntity(String.class);
margaretha6374f722018-04-17 18:45:57 +0200560 JsonNode node = JsonUtils.readTree(entity);
margarethafb027f92018-04-23 20:00:13 +0200561 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
margaretha6374f722018-04-17 18:45:57 +0200562 node.at("/error").asText());
margaretha7da23902022-05-02 08:38:45 +0200563 assertEquals("Missing parameter: client_secret",
margarethafb027f92018-04-23 20:00:13 +0200564 node.at("/error_description").asText());
margaretha6374f722018-04-17 18:45:57 +0200565 }
566
567 @Test
568 public void testRequestTokenPasswordGrantMissingClientId ()
margarethafb027f92018-04-23 20:00:13 +0200569 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000570 Response response =
margaretha35074692021-03-26 18:11:59 +0100571 requestTokenWithDoryPassword(null, clientSecret);
abcpro173fe8f22022-11-08 19:56:52 +0000572 String entity = response.readEntity(String.class);
margaretha6374f722018-04-17 18:45:57 +0200573 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
574
575 JsonNode node = JsonUtils.readTree(entity);
576 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
577 node.at("/error").asText());
margarethafb027f92018-04-23 20:00:13 +0200578 assertEquals("Missing parameters: client_id",
margaretha6374f722018-04-17 18:45:57 +0200579 node.at("/error_description").asText());
580 }
margarethabe4c5c92018-05-03 18:55:49 +0200581
margarethafb027f92018-04-23 20:00:13 +0200582 @Test
margarethaf839dde2018-04-16 17:52:57 +0200583 public void testRequestTokenClientCredentialsGrant ()
margarethafb027f92018-04-23 20:00:13 +0200584 throws KustvaktException {
margarethaf839dde2018-04-16 17:52:57 +0200585
abcpro173fe8f22022-11-08 19:56:52 +0000586 Form form = new Form();
587 form.param("grant_type", "client_credentials");
588 form.param("client_id", confidentialClientId);
589 form.param("client_secret", "secret");
590 Response response = requestToken(form);
591 String entity = response.readEntity(String.class);
margarethaf839dde2018-04-16 17:52:57 +0200592 assertEquals(Status.OK.getStatusCode(), response.getStatus());
593
594 JsonNode node = JsonUtils.readTree(entity);
595 // length?
596 assertNotNull(node.at("/access_token").asText());
597 assertNotNull(node.at("/refresh_token").asText());
598 assertEquals(TokenType.BEARER.toString(),
599 node.at("/token_type").asText());
600 assertNotNull(node.at("/expires_in").asText());
601 }
602
margarethadc515072018-08-03 17:01:19 +0200603 /**
604 * Client credentials grant is only allowed for confidential
605 * clients.
606 */
607 @Test
608 public void testRequestTokenClientCredentialsGrantPublic ()
609 throws KustvaktException {
610
abcpro173fe8f22022-11-08 19:56:52 +0000611 Form form = new Form();
612 form.param("grant_type", "client_credentials");
613 form.param("client_id", publicClientId);
614 form.param("client_secret", "");
615 Response response = requestToken(form);
margarethadc515072018-08-03 17:01:19 +0200616
abcpro173fe8f22022-11-08 19:56:52 +0000617 String entity = response.readEntity(String.class);
margarethadc515072018-08-03 17:01:19 +0200618 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
619 JsonNode node = JsonUtils.readTree(entity);
620 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
621 node.at("/error").asText());
622 assertEquals("Missing parameters: client_secret",
623 node.at("/error_description").asText());
624 }
625
margarethaf839dde2018-04-16 17:52:57 +0200626 @Test
margarethabe4c5c92018-05-03 18:55:49 +0200627 public void testRequestTokenClientCredentialsGrantReducedScope ()
628 throws KustvaktException {
629
abcpro173fe8f22022-11-08 19:56:52 +0000630 Form form = new Form();
631 form.param("grant_type", "client_credentials");
632 form.param("client_id", confidentialClientId);
633 form.param("client_secret", "secret");
634 form.param("scope", "preferred_username client_info");
margarethabe4c5c92018-05-03 18:55:49 +0200635
abcpro173fe8f22022-11-08 19:56:52 +0000636 Response response = requestToken(form);
637 String entity = response.readEntity(String.class);
margarethabe4c5c92018-05-03 18:55:49 +0200638 assertEquals(Status.OK.getStatusCode(), response.getStatus());
639
640 JsonNode node = JsonUtils.readTree(entity);
641 // length?
642 assertNotNull(node.at("/access_token").asText());
643 assertNotNull(node.at("/refresh_token").asText());
644 assertEquals(TokenType.BEARER.toString(),
645 node.at("/token_type").asText());
646 assertNotNull(node.at("/expires_in").asText());
margaretha9c78e1a2018-06-27 14:12:35 +0200647 assertEquals("client_info", node.at("/scope").asText());
margarethabe4c5c92018-05-03 18:55:49 +0200648 }
649
650 @Test
margarethafb027f92018-04-23 20:00:13 +0200651 public void testRequestTokenMissingGrantType () throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000652 Form form = new Form();
653 Response response = requestToken(form);
margarethaf839dde2018-04-16 17:52:57 +0200654 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
655
abcpro173fe8f22022-11-08 19:56:52 +0000656 String entity = response.readEntity(String.class);
margarethaf839dde2018-04-16 17:52:57 +0200657 JsonNode node = JsonUtils.readTree(entity);
658 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
659 node.at("/error").asText());
660 }
margarethaa0486272018-04-12 19:59:31 +0200661
662 @Test
margarethafb027f92018-04-23 20:00:13 +0200663 public void testRequestTokenUnsupportedGrant () throws KustvaktException {
margarethaa0486272018-04-12 19:59:31 +0200664
abcpro173fe8f22022-11-08 19:56:52 +0000665 Form form = new Form();
666 form.param("grant_type", "blahblah");
margaretha05122312018-04-16 15:01:34 +0200667
abcpro173fe8f22022-11-08 19:56:52 +0000668 Response response =
669 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000670 .request()
margaretha35074692021-03-26 18:11:59 +0100671 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
672 .header(HttpHeaders.CONTENT_TYPE,
673 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000674 .post(Entity.form(form));
margarethaa0486272018-04-12 19:59:31 +0200675
abcpro173fe8f22022-11-08 19:56:52 +0000676 String entity = response.readEntity(String.class);
margaretha05122312018-04-16 15:01:34 +0200677 assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
678
679 JsonNode node = JsonUtils.readTree(entity);
margarethaf839dde2018-04-16 17:52:57 +0200680 assertEquals("Invalid grant_type parameter value",
margaretha05122312018-04-16 15:01:34 +0200681 node.get("error_description").asText());
margarethaf839dde2018-04-16 17:52:57 +0200682 assertEquals(OAuthError.TokenResponse.INVALID_REQUEST,
683 node.get("error").asText());
margarethaa0486272018-04-12 19:59:31 +0200684 }
685
margaretha03b82862018-07-12 20:09:26 +0200686 private void testRequestRefreshTokenInvalidScope (String clientId,
687 String refreshToken) throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000688 Form form = new Form();
689 form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
690 form.param("client_id", clientId);
691 form.param("client_secret", clientSecret);
692 form.param("refresh_token", refreshToken);
693 form.param("scope", "search serialize_query");
margaretha03b82862018-07-12 20:09:26 +0200694
abcpro173fe8f22022-11-08 19:56:52 +0000695 Response response =
696 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000697 .request()
margaretha35074692021-03-26 18:11:59 +0100698 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
699 .header(HttpHeaders.CONTENT_TYPE,
700 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000701 .post(Entity.form(form));
margaretha03b82862018-07-12 20:09:26 +0200702
abcpro173fe8f22022-11-08 19:56:52 +0000703 String entity = response.readEntity(String.class);
margaretha03b82862018-07-12 20:09:26 +0200704 JsonNode node = JsonUtils.readTree(entity);
705 assertEquals(OAuth2Error.INVALID_SCOPE, node.at("/error").asText());
706 }
707
margaretha35074692021-03-26 18:11:59 +0100708 private void testRequestRefreshToken (String clientId, String clientSecret,
margaretha03b82862018-07-12 20:09:26 +0200709 String refreshToken) throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000710 Form form = new Form();
711 form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
712 form.param("client_id", clientId);
713 form.param("client_secret", clientSecret);
714 form.param("refresh_token", refreshToken);
margaretha03b82862018-07-12 20:09:26 +0200715
abcpro173fe8f22022-11-08 19:56:52 +0000716 Response response =
717 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000718 .request()
margaretha35074692021-03-26 18:11:59 +0100719 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
720 .header(HttpHeaders.CONTENT_TYPE,
721 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000722 .post(Entity.form(form));
margaretha03b82862018-07-12 20:09:26 +0200723
abcpro173fe8f22022-11-08 19:56:52 +0000724 String entity = response.readEntity(String.class);
margaretha03b82862018-07-12 20:09:26 +0200725 assertEquals(Status.OK.getStatusCode(), response.getStatus());
726
727 JsonNode node = JsonUtils.readTree(entity);
728 assertNotNull(node.at("/access_token").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200729
margaretha35074692021-03-26 18:11:59 +0100730 String newRefreshToken = node.at("/refresh_token").asText();
731 assertNotNull(newRefreshToken);
margaretha03b82862018-07-12 20:09:26 +0200732 assertEquals(TokenType.BEARER.toString(),
733 node.at("/token_type").asText());
734 assertNotNull(node.at("/expires_in").asText());
margaretha6f0b7382018-11-21 17:42:02 +0100735
margaretha35074692021-03-26 18:11:59 +0100736 assertTrue(!newRefreshToken.equals(refreshToken));
margaretha9436ebe2022-04-22 11:48:37 +0200737
margaretha35074692021-03-26 18:11:59 +0100738 testRequestTokenWithRevokedRefreshToken(clientId, clientSecret,
739 refreshToken);
margaretha9436ebe2022-04-22 11:48:37 +0200740
margaretha35074692021-03-26 18:11:59 +0100741 testRevokeToken(newRefreshToken, clientId, clientSecret,
742 REFRESH_TOKEN_TYPE);
743 testRequestTokenWithRevokedRefreshToken(clientId, clientSecret,
744 newRefreshToken);
margaretha03b82862018-07-12 20:09:26 +0200745 }
746
747 private void testRequestRefreshTokenInvalidClient (String refreshToken)
748 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000749 Form form = new Form();
750 form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
751 form.param("client_id", "iBr3LsTCxOj7D2o0A5m");
752 form.param("refresh_token", refreshToken);
margaretha03b82862018-07-12 20:09:26 +0200753
abcpro173fe8f22022-11-08 19:56:52 +0000754 Response response =
755 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000756 .request()
margaretha35074692021-03-26 18:11:59 +0100757 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
758 .header(HttpHeaders.CONTENT_TYPE,
759 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000760 .post(Entity.form(form));
margaretha03b82862018-07-12 20:09:26 +0200761
abcpro173fe8f22022-11-08 19:56:52 +0000762 String entity = response.readEntity(String.class);
margaretha03b82862018-07-12 20:09:26 +0200763 JsonNode node = JsonUtils.readTree(entity);
764 assertEquals(OAuth2Error.INVALID_CLIENT, node.at("/error").asText());
765 }
766
767 private void testRequestRefreshTokenInvalidRefreshToken (String clientId)
768 throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000769 Form form = new Form();
770 form.param("grant_type", GrantType.REFRESH_TOKEN.toString());
771 form.param("client_id", clientId);
772 form.param("client_secret", clientSecret);
773 form.param("refresh_token", "Lia8s8w8tJeZSBlaQDrYV8ion3l");
margaretha03b82862018-07-12 20:09:26 +0200774
abcpro173fe8f22022-11-08 19:56:52 +0000775 Response response =
776 target().path(API_VERSION).path("oauth2").path("token")
abcpro1241bc4f2022-11-07 20:13:57 +0000777 .request()
margaretha35074692021-03-26 18:11:59 +0100778 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
779 .header(HttpHeaders.CONTENT_TYPE,
780 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000781 .post(Entity.form(form));
margaretha03b82862018-07-12 20:09:26 +0200782
abcpro173fe8f22022-11-08 19:56:52 +0000783 String entity = response.readEntity(String.class);
margaretha03b82862018-07-12 20:09:26 +0200784 JsonNode node = JsonUtils.readTree(entity);
785 assertEquals(OAuth2Error.INVALID_GRANT, node.at("/error").asText());
786 }
787
margaretha35074692021-03-26 18:11:59 +0100788 private JsonNode requestTokenList (String userAuthHeader, String tokenType,
789 String clientId) throws KustvaktException {
abcpro173fe8f22022-11-08 19:56:52 +0000790 Form form = new Form();
791 form.param("super_client_id", superClientId);
792 form.param("super_client_secret", clientSecret);
793 form.param("token_type", tokenType);
margaretha35074692021-03-26 18:11:59 +0100794
margaretha9436ebe2022-04-22 11:48:37 +0200795 if (clientId != null && !clientId.isEmpty()) {
abcpro173fe8f22022-11-08 19:56:52 +0000796 form.param("client_id", clientId);
margaretha35074692021-03-26 18:11:59 +0100797 }
margaretha9436ebe2022-04-22 11:48:37 +0200798
abcpro173fe8f22022-11-08 19:56:52 +0000799 Response response = target().path(API_VERSION).path("oauth2")
margaretha35074692021-03-26 18:11:59 +0100800 .path("token").path("list")
abcpro1241bc4f2022-11-07 20:13:57 +0000801 .request()
margaretha35074692021-03-26 18:11:59 +0100802 .header(Attributes.AUTHORIZATION, userAuthHeader)
margarethadc515072018-08-03 17:01:19 +0200803 .header(HttpHeaders.CONTENT_TYPE,
804 ContentType.APPLICATION_FORM_URLENCODED)
abcpro173fe8f22022-11-08 19:56:52 +0000805 .post(Entity.form(form));
margarethaf0085122018-08-16 16:19:53 +0200806
margarethadc515072018-08-03 17:01:19 +0200807 assertEquals(Status.OK.getStatusCode(), response.getStatus());
margaretha35074692021-03-26 18:11:59 +0100808
abcpro173fe8f22022-11-08 19:56:52 +0000809 String entity = response.readEntity(String.class);
margaretha35074692021-03-26 18:11:59 +0100810 return JsonUtils.readTree(entity);
811 }
margaretha9436ebe2022-04-22 11:48:37 +0200812
margaretha35074692021-03-26 18:11:59 +0100813 private JsonNode requestTokenList (String userAuthHeader, String tokenType)
814 throws KustvaktException {
815 return requestTokenList(userAuthHeader, tokenType, null);
margaretha7497adf2019-11-26 13:13:57 +0100816 }
margaretha9436ebe2022-04-22 11:48:37 +0200817
margaretha43aceb52019-11-21 12:58:53 +0100818 @Test
margaretha9436ebe2022-04-22 11:48:37 +0200819 public void testListRefreshTokenConfidentialClient ()
820 throws KustvaktException {
margaretha43aceb52019-11-21 12:58:53 +0100821 String username = "gurgle";
822 String password = "pwd";
823 userAuthHeader = HttpAuthorizationHandler
824 .createBasicAuthorizationHeaderValue(username, password);
825
826 // super client
abcpro173fe8f22022-11-08 19:56:52 +0000827 Response response = requestTokenWithPassword(superClientId,
margaretha43aceb52019-11-21 12:58:53 +0100828 clientSecret, username, password);
829 assertEquals(Status.OK.getStatusCode(), response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000830 JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
margaretha43aceb52019-11-21 12:58:53 +0100831 String refreshToken1 = node.at("/refresh_token").asText();
832
833 // client 1
margarethad67b4272022-04-11 17:34:19 +0200834 String code =
835 requestAuthorizationCode(confidentialClientId, userAuthHeader);
margaretha0afd44a2020-02-05 10:49:21 +0100836 response = requestTokenWithAuthorizationCodeAndForm(
837 confidentialClientId, clientSecret, code);
margaretha43aceb52019-11-21 12:58:53 +0100838 assertEquals(Status.OK.getStatusCode(), response.getStatus());
839
840 // client 2
margarethad67b4272022-04-11 17:34:19 +0200841 code = requestAuthorizationCode(confidentialClientId2,
842 clientRedirectUri, userAuthHeader);
margaretha0afd44a2020-02-05 10:49:21 +0100843 response = requestTokenWithAuthorizationCodeAndForm(
margarethad67b4272022-04-11 17:34:19 +0200844 confidentialClientId2, clientSecret, code, clientRedirectUri);
margaretha0afd44a2020-02-05 10:49:21 +0100845 assertEquals(Status.OK.getStatusCode(), response.getStatus());
846
847 // list
margaretha35074692021-03-26 18:11:59 +0100848 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
margaretha0afd44a2020-02-05 10:49:21 +0100849 assertEquals(2, node.size());
margaretha4f4b9ab2021-04-30 17:33:21 +0200850 assertEquals(confidentialClientId, node.at("/0/client_id").asText());
851 assertEquals(confidentialClientId2, node.at("/1/client_id").asText());
margaretha35074692021-03-26 18:11:59 +0100852
margaretha0afd44a2020-02-05 10:49:21 +0100853 // client 1
margarethad67b4272022-04-11 17:34:19 +0200854 code = requestAuthorizationCode(confidentialClientId, userAuthHeader);
margaretha43aceb52019-11-21 12:58:53 +0100855 response = requestTokenWithAuthorizationCodeAndForm(
856 confidentialClientId, clientSecret, code);
857 assertEquals(Status.OK.getStatusCode(), response.getStatus());
858
margaretha7497adf2019-11-26 13:13:57 +0100859 // another user
860 String darlaAuthHeader = HttpAuthorizationHandler
861 .createBasicAuthorizationHeaderValue("darla", "pwd");
margaretha35074692021-03-26 18:11:59 +0100862
863 // test listing clients
864 node = requestTokenList(darlaAuthHeader, REFRESH_TOKEN_TYPE);
865 assertEquals(0, node.size());
866
margaretha43aceb52019-11-21 12:58:53 +0100867 // client 1
margarethad67b4272022-04-11 17:34:19 +0200868 code = requestAuthorizationCode(confidentialClientId, darlaAuthHeader);
margaretha43aceb52019-11-21 12:58:53 +0100869 assertEquals(Status.OK.getStatusCode(), response.getStatus());
870 response = requestTokenWithAuthorizationCodeAndForm(
margaretha0afd44a2020-02-05 10:49:21 +0100871 confidentialClientId, clientSecret, code);
margaretha35074692021-03-26 18:11:59 +0100872
abcpro173fe8f22022-11-08 19:56:52 +0000873 node = JsonUtils.readTree(response.readEntity(String.class));
margaretha43aceb52019-11-21 12:58:53 +0100874 String refreshToken5 = node.at("/refresh_token").asText();
margaretha35074692021-03-26 18:11:59 +0100875
876 // list all refresh tokens
877 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
margaretha43aceb52019-11-21 12:58:53 +0100878 assertEquals(3, node.size());
margaretha35074692021-03-26 18:11:59 +0100879
880 // list refresh tokens from client 1
margaretha9436ebe2022-04-22 11:48:37 +0200881 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE,
882 confidentialClientId);
margaretha35074692021-03-26 18:11:59 +0100883 assertEquals(2, node.size());
margaretha9436ebe2022-04-22 11:48:37 +0200884
margaretha43aceb52019-11-21 12:58:53 +0100885 testRevokeToken(refreshToken1, superClientId, clientSecret,
886 REFRESH_TOKEN_TYPE);
margaretha35074692021-03-26 18:11:59 +0100887 testRevokeToken(node.at("/0/token").asText(), confidentialClientId,
margaretha0afd44a2020-02-05 10:49:21 +0100888 clientSecret, REFRESH_TOKEN_TYPE);
889 testRevokeToken(node.at("/1/token").asText(), confidentialClientId2,
margaretha43aceb52019-11-21 12:58:53 +0100890 clientSecret, REFRESH_TOKEN_TYPE);
margaretha35074692021-03-26 18:11:59 +0100891
892 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
margaretha43aceb52019-11-21 12:58:53 +0100893 assertEquals(1, node.size());
margaretha35074692021-03-26 18:11:59 +0100894
895 testRevokeTokenViaSuperClient(node.at("/0/token").asText(),
896 userAuthHeader);
897 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
margaretha7497adf2019-11-26 13:13:57 +0100898 assertEquals(0, node.size());
margaretha35074692021-03-26 18:11:59 +0100899
margaretha7497adf2019-11-26 13:13:57 +0100900 // try revoking a token belonging to another user
901 // should not return any errors
902 testRevokeTokenViaSuperClient(refreshToken5, userAuthHeader);
margaretha35074692021-03-26 18:11:59 +0100903 node = requestTokenList(darlaAuthHeader, REFRESH_TOKEN_TYPE);
margaretha7497adf2019-11-26 13:13:57 +0100904 assertEquals(1, node.size());
margaretha35074692021-03-26 18:11:59 +0100905
margaretha7497adf2019-11-26 13:13:57 +0100906 testRevokeTokenViaSuperClient(refreshToken5, darlaAuthHeader);
margaretha35074692021-03-26 18:11:59 +0100907 node = requestTokenList(darlaAuthHeader, REFRESH_TOKEN_TYPE);
margaretha7497adf2019-11-26 13:13:57 +0100908 assertEquals(0, node.size());
margaretha43aceb52019-11-21 12:58:53 +0100909 }
margaretha43aceb52019-11-21 12:58:53 +0100910
margaretha35074692021-03-26 18:11:59 +0100911 @Test
912 public void testListTokenPublicClient () throws KustvaktException {
913 String username = "nemo";
914 String password = "pwd";
915 userAuthHeader = HttpAuthorizationHandler
916 .createBasicAuthorizationHeaderValue(username, password);
917
918 // access token 1
margarethad67b4272022-04-11 17:34:19 +0200919 String code = requestAuthorizationCode(publicClientId, userAuthHeader);
abcpro173fe8f22022-11-08 19:56:52 +0000920 Response response = requestTokenWithAuthorizationCodeAndForm(
margaretha9436ebe2022-04-22 11:48:37 +0200921 publicClientId, "", code);
margaretha43aceb52019-11-21 12:58:53 +0100922 assertEquals(Status.OK.getStatusCode(), response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000923 JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
margaretha35074692021-03-26 18:11:59 +0100924 String accessToken1 = node.at("/access_token").asText();
margaretha43aceb52019-11-21 12:58:53 +0100925
margaretha35074692021-03-26 18:11:59 +0100926 // access token 2
margarethad67b4272022-04-11 17:34:19 +0200927 code = requestAuthorizationCode(publicClientId, userAuthHeader);
margaretha35074692021-03-26 18:11:59 +0100928 response = requestTokenWithAuthorizationCodeAndForm(publicClientId, "",
929 code);
930 assertEquals(Status.OK.getStatusCode(), response.getStatus());
abcpro173fe8f22022-11-08 19:56:52 +0000931 node = JsonUtils.readTree(response.readEntity(String.class));
margaretha35074692021-03-26 18:11:59 +0100932 String accessToken2 = node.at("/access_token").asText();
margaretha9436ebe2022-04-22 11:48:37 +0200933
margaretha35074692021-03-26 18:11:59 +0100934 // list access tokens
935 node = requestTokenList(userAuthHeader, ACCESS_TOKEN_TYPE);
936 assertEquals(2, node.size());
margaretha9436ebe2022-04-22 11:48:37 +0200937
margaretha35074692021-03-26 18:11:59 +0100938 // list refresh tokens
939 node = requestTokenList(userAuthHeader, REFRESH_TOKEN_TYPE);
940 assertEquals(0, node.size());
margaretha9436ebe2022-04-22 11:48:37 +0200941
margaretha35074692021-03-26 18:11:59 +0100942 testRevokeTokenViaSuperClient(accessToken1, userAuthHeader);
943 node = requestTokenList(userAuthHeader, ACCESS_TOKEN_TYPE);
margaretha9436ebe2022-04-22 11:48:37 +0200944 // System.out.println(node);
margaretha35074692021-03-26 18:11:59 +0100945 assertEquals(1, node.size());
margaretha4f4b9ab2021-04-30 17:33:21 +0200946 assertEquals(accessToken2, node.at("/0/token").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200947 assertTrue(node.at("/0/scope").size() > 0);
margaretha4f4b9ab2021-04-30 17:33:21 +0200948 assertNotNull(node.at("/0/created_date").asText());
949 assertNotNull(node.at("/0/expires_in").asLong());
950 assertNotNull(node.at("/0/user_authentication_time").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200951
margaretha4f4b9ab2021-04-30 17:33:21 +0200952 assertEquals(publicClientId, node.at("/0/client_id").asText());
953 assertNotNull(node.at("/0/client_name").asText());
954 assertNotNull(node.at("/0/client_description").asText());
955 assertNotNull(node.at("/0/client_url").asText());
margaretha9436ebe2022-04-22 11:48:37 +0200956
margaretha35074692021-03-26 18:11:59 +0100957 testRevokeTokenViaSuperClient(accessToken2, userAuthHeader);
958 node = requestTokenList(userAuthHeader, ACCESS_TOKEN_TYPE);
959 assertEquals(0, node.size());
margaretha43aceb52019-11-21 12:58:53 +0100960 }
margaretha977fabe2022-04-28 09:23:47 +0200961
962 private void testRefreshTokenExpiry (String refreshToken)
963 throws KustvaktException {
964 RefreshToken token = refreshTokenDao.retrieveRefreshToken(refreshToken);
965 ZonedDateTime expiry = token.getCreatedDate().plusYears(1);
966 assertTrue(expiry.equals(token.getExpiryDate()));
967 }
margarethaa0486272018-04-12 19:59:31 +0200968}