Skip to content

Commit c1f9e84

Browse files
author
Steve Riesenberg
committed
Add tests for OAuth 2.0 Device Authorization Grant
This commit adds tests for the following components: * AuthenticationConverters * AuthenticationProviders * Endpoint Filters Issue spring-projectsgh-44 Issue spring-projectsgh-1127
1 parent 1bfc54f commit c1f9e84

10 files changed

+3144
-0
lines changed

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java

Lines changed: 444 additions & 0 deletions
Large diffs are not rendered by default.

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java

Lines changed: 351 additions & 0 deletions
Large diffs are not rendered by default.

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java

Lines changed: 431 additions & 0 deletions
Large diffs are not rendered by default.

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java

Lines changed: 326 additions & 0 deletions
Large diffs are not rendered by default.

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java

Lines changed: 423 additions & 0 deletions
Large diffs are not rendered by default.

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java

Lines changed: 460 additions & 0 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/*
2+
* Copyright 2020-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization.web.authentication;
17+
18+
import org.junit.jupiter.api.AfterEach;
19+
import org.junit.jupiter.api.BeforeEach;
20+
import org.junit.jupiter.api.Test;
21+
22+
import org.springframework.http.HttpMethod;
23+
import org.springframework.mock.web.MockHttpServletRequest;
24+
import org.springframework.security.authentication.AnonymousAuthenticationToken;
25+
import org.springframework.security.authentication.TestingAuthenticationToken;
26+
import org.springframework.security.core.context.SecurityContextHolder;
27+
import org.springframework.security.core.context.SecurityContextImpl;
28+
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
29+
import org.springframework.security.oauth2.core.OAuth2Error;
30+
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
31+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
32+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;
33+
34+
import static java.util.Map.entry;
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
37+
38+
/**
39+
* Tests for {@link OAuth2DeviceAuthorizationConsentAuthenticationConverter}.
40+
*
41+
* @author Steve Riesenberg
42+
*/
43+
public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests {
44+
private static final String VERIFICATION_URI = "/oauth2/device_verification";
45+
private static final String USER_CODE = "BCDF-GHJK";
46+
private static final String CLIENT_ID = "client-1";
47+
private static final String STATE = "abc123";
48+
49+
private OAuth2DeviceAuthorizationConsentAuthenticationConverter converter;
50+
51+
@BeforeEach
52+
public void setUp() {
53+
this.converter = new OAuth2DeviceAuthorizationConsentAuthenticationConverter();
54+
}
55+
56+
@AfterEach
57+
public void tearDown() {
58+
SecurityContextHolder.clearContext();
59+
}
60+
61+
@Test
62+
public void convertWhenGetThenReturnNull() {
63+
MockHttpServletRequest request = createRequest();
64+
request.setMethod(HttpMethod.GET.name());
65+
assertThat(this.converter.convert(request)).isNull();
66+
}
67+
68+
@Test
69+
public void convertWhenMissingStateThenReturnNull() {
70+
MockHttpServletRequest request = createRequest();
71+
assertThat(this.converter.convert(request)).isNull();
72+
}
73+
74+
@Test
75+
public void convertWhenMissingClientIdThenInvalidRequestError() {
76+
MockHttpServletRequest request = createRequest();
77+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
78+
// @formatter:off
79+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
80+
.isThrownBy(() -> this.converter.convert(request))
81+
.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)
82+
.extracting(OAuth2AuthenticationException::getError)
83+
.extracting(OAuth2Error::getErrorCode)
84+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
85+
// @formatter:on
86+
}
87+
88+
@Test
89+
public void convertWhenBlankClientIdThenInvalidRequestError() {
90+
MockHttpServletRequest request = createRequest();
91+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
92+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "");
93+
// @formatter:off
94+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
95+
.isThrownBy(() -> this.converter.convert(request))
96+
.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)
97+
.extracting(OAuth2AuthenticationException::getError)
98+
.extracting(OAuth2Error::getErrorCode)
99+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
100+
// @formatter:on
101+
}
102+
103+
@Test
104+
public void convertWhenMultipleClientIdParametersThenInvalidRequestError() {
105+
MockHttpServletRequest request = createRequest();
106+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
107+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
108+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "another");
109+
// @formatter:off
110+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
111+
.isThrownBy(() -> this.converter.convert(request))
112+
.withMessageContaining(OAuth2ParameterNames.CLIENT_ID)
113+
.extracting(OAuth2AuthenticationException::getError)
114+
.extracting(OAuth2Error::getErrorCode)
115+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
116+
// @formatter:on
117+
}
118+
119+
@Test
120+
public void convertWhenMissingUserCodeThenInvalidRequestError() {
121+
MockHttpServletRequest request = createRequest();
122+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
123+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
124+
// @formatter:off
125+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
126+
.isThrownBy(() -> this.converter.convert(request))
127+
.withMessageContaining(OAuth2ParameterNames.USER_CODE)
128+
.extracting(OAuth2AuthenticationException::getError)
129+
.extracting(OAuth2Error::getErrorCode)
130+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
131+
// @formatter:on
132+
}
133+
134+
@Test
135+
public void convertWhenBlankUserCodeThenInvalidRequestError() {
136+
MockHttpServletRequest request = createRequest();
137+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
138+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
139+
request.addParameter(OAuth2ParameterNames.USER_CODE, "");
140+
// @formatter:off
141+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
142+
.isThrownBy(() -> this.converter.convert(request))
143+
.withMessageContaining(OAuth2ParameterNames.USER_CODE)
144+
.extracting(OAuth2AuthenticationException::getError)
145+
.extracting(OAuth2Error::getErrorCode)
146+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
147+
// @formatter:on
148+
}
149+
150+
@Test
151+
public void convertWhenMultipleUserCodeParametersThenInvalidRequestError() {
152+
MockHttpServletRequest request = createRequest();
153+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
154+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
155+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
156+
request.addParameter(OAuth2ParameterNames.USER_CODE, "another");
157+
// @formatter:off
158+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
159+
.isThrownBy(() -> this.converter.convert(request))
160+
.withMessageContaining(OAuth2ParameterNames.USER_CODE)
161+
.extracting(OAuth2AuthenticationException::getError)
162+
.extracting(OAuth2Error::getErrorCode)
163+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
164+
// @formatter:on
165+
}
166+
167+
@Test
168+
public void convertWhenBlankStateParameterThenInvalidRequestError() {
169+
MockHttpServletRequest request = createRequest();
170+
request.addParameter(OAuth2ParameterNames.STATE, "");
171+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
172+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
173+
// @formatter:off
174+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
175+
.isThrownBy(() -> this.converter.convert(request))
176+
.withMessageContaining(OAuth2ParameterNames.STATE)
177+
.extracting(OAuth2AuthenticationException::getError)
178+
.extracting(OAuth2Error::getErrorCode)
179+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
180+
// @formatter:on
181+
}
182+
183+
@Test
184+
public void convertWhenMultipleStateParametersThenInvalidRequestError() {
185+
MockHttpServletRequest request = createRequest();
186+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
187+
request.addParameter(OAuth2ParameterNames.STATE, "another");
188+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
189+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
190+
// @formatter:off
191+
assertThatExceptionOfType(OAuth2AuthenticationException.class)
192+
.isThrownBy(() -> this.converter.convert(request))
193+
.withMessageContaining(OAuth2ParameterNames.STATE)
194+
.extracting(OAuth2AuthenticationException::getError)
195+
.extracting(OAuth2Error::getErrorCode)
196+
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
197+
// @formatter:on
198+
}
199+
200+
@Test
201+
public void convertWhenMissingPrincipalThenReturnDeviceAuthorizationConsentAuthentication() {
202+
MockHttpServletRequest request = createRequest();
203+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
204+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
205+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
206+
207+
OAuth2DeviceAuthorizationConsentAuthenticationToken authentication =
208+
(OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request);
209+
assertThat(authentication).isNotNull();
210+
assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);
211+
assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);
212+
assertThat(authentication.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class);
213+
assertThat(authentication.getUserCode()).isEqualTo(USER_CODE);
214+
assertThat(authentication.getScopes()).isEmpty();
215+
assertThat(authentication.getAdditionalParameters()).isEmpty();
216+
}
217+
218+
@Test
219+
public void convertWhenMissingScopeThenReturnDeviceAuthorizationConsentAuthentication() {
220+
MockHttpServletRequest request = createRequest();
221+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
222+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
223+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
224+
225+
SecurityContextImpl securityContext = new SecurityContextImpl();
226+
securityContext.setAuthentication(new TestingAuthenticationToken("user", null));
227+
SecurityContextHolder.setContext(securityContext);
228+
229+
OAuth2DeviceAuthorizationConsentAuthenticationToken authentication =
230+
(OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request);
231+
assertThat(authentication).isNotNull();
232+
assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);
233+
assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);
234+
assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);
235+
assertThat(authentication.getUserCode()).isEqualTo(USER_CODE);
236+
assertThat(authentication.getScopes()).isEmpty();
237+
assertThat(authentication.getAdditionalParameters()).isEmpty();
238+
}
239+
240+
@Test
241+
public void convertWhenAllParametersThenReturnDeviceAuthorizationConsentAuthentication() {
242+
MockHttpServletRequest request = createRequest();
243+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
244+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
245+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE);
246+
request.addParameter(OAuth2ParameterNames.SCOPE, "message.read");
247+
request.addParameter(OAuth2ParameterNames.SCOPE, "message.write");
248+
request.addParameter("param-1", "value-1");
249+
request.addParameter("param-2", "value-2");
250+
251+
SecurityContextImpl securityContext = new SecurityContextImpl();
252+
securityContext.setAuthentication(new TestingAuthenticationToken("user", null));
253+
SecurityContextHolder.setContext(securityContext);
254+
255+
OAuth2DeviceAuthorizationConsentAuthenticationToken authentication =
256+
(OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request);
257+
assertThat(authentication).isNotNull();
258+
assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);
259+
assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);
260+
assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);
261+
assertThat(authentication.getUserCode()).isEqualTo(USER_CODE);
262+
assertThat(authentication.getScopes()).containsExactly("message.read", "message.write");
263+
assertThat(authentication.getAdditionalParameters())
264+
.containsExactly(entry("param-1", "value-1"), entry("param-2", "value-2"));
265+
}
266+
267+
@Test
268+
public void convertWhenNonNormalizedUserCodeThenReturnDeviceAuthorizationConsentAuthentication() {
269+
MockHttpServletRequest request = createRequest();
270+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID);
271+
request.addParameter(OAuth2ParameterNames.STATE, STATE);
272+
request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE.toLowerCase().replace("-", " . "));
273+
274+
SecurityContextImpl securityContext = new SecurityContextImpl();
275+
securityContext.setAuthentication(new TestingAuthenticationToken("user", null));
276+
SecurityContextHolder.setContext(securityContext);
277+
278+
OAuth2DeviceAuthorizationConsentAuthenticationToken authentication =
279+
(OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request);
280+
assertThat(authentication).isNotNull();
281+
assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI);
282+
assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID);
283+
assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class);
284+
assertThat(authentication.getUserCode()).isEqualTo(USER_CODE);
285+
assertThat(authentication.getScopes()).isEmpty();
286+
assertThat(authentication.getAdditionalParameters()).isEmpty();
287+
}
288+
289+
private static MockHttpServletRequest createRequest() {
290+
MockHttpServletRequest request = new MockHttpServletRequest();
291+
request.setMethod(HttpMethod.POST.name());
292+
request.setRequestURI(VERIFICATION_URI);
293+
return request;
294+
}
295+
}

0 commit comments

Comments
 (0)