Skip to content

Commit 9e68345

Browse files
jgrandjathomasdarimont
authored andcommitted
Add tests to oauth2-client
Fixes spring-projectsgh-4299
1 parent c46837f commit 9e68345

File tree

32 files changed

+3328
-264
lines changed

32 files changed

+3328
-264
lines changed

oauth2/oauth2-client/spring-security-oauth2-client.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ dependencies {
99

1010
optional project(':spring-security-oauth2-jose')
1111

12+
testCompile powerMock2Dependencies
13+
testCompile 'com.squareup.okhttp3:mockwebserver'
14+
testCompile 'com.fasterxml.jackson.core:jackson-databind'
15+
1216
provided 'javax.servlet:javax.servlet-api'
1317
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ public OAuth2AccessTokenResponse getTokenResponse(OAuth2AuthorizationCodeGrantRe
100100
httpRequest.setReadTimeout(30000);
101101
tokenResponse = com.nimbusds.oauth2.sdk.TokenResponse.parse(httpRequest.send());
102102
} catch (ParseException pe) {
103-
throw new OAuth2AuthenticationException(new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE), pe);
103+
OAuth2Error oauth2Error = new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE,
104+
"An error occurred parsing the Access Token response: " + pe.getMessage(), null);
105+
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), pe);
104106
} catch (IOException ioe) {
105107
throw new AuthenticationServiceException("An error occurred while sending the Access Token Request: " +
106108
ioe.getMessage(), ioe);

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcAuthorizationCodeAuthenticationProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ private void validateIdToken(OidcIdToken idToken, ClientRegistration clientRegis
262262
// 10. The iat Claim can be used to reject tokens that were issued too far away from the current time,
263263
// limiting the amount of time that nonces need to be stored to prevent attacks.
264264
// The acceptable range is Client specific.
265-
Instant maxIssuedAt = now.plusSeconds(30);
265+
Instant maxIssuedAt = Instant.now().plusSeconds(30);
266266
if (issuedAt.isAfter(maxIssuedAt)) {
267267
this.throwInvalidIdTokenException();
268268
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/NimbusUserInfoResponseClient.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.http.client.AbstractClientHttpResponse;
2828
import org.springframework.http.client.ClientHttpResponse;
2929
import org.springframework.http.converter.GenericHttpMessageConverter;
30+
import org.springframework.http.converter.HttpMessageNotReadableException;
3031
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
3132
import org.springframework.security.authentication.AuthenticationServiceException;
3233
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -57,7 +58,7 @@ <T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, Class<T> returnType
5758
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken());
5859
try {
5960
return (T) this.genericHttpMessageConverter.read(returnType, userInfoResponse);
60-
} catch (IOException ex) {
61+
} catch (IOException | HttpMessageNotReadableException ex) {
6162
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
6263
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null);
6364
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
@@ -69,7 +70,7 @@ <T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, ParameterizedTypeRe
6970
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken());
7071
try {
7172
return (T) this.genericHttpMessageConverter.read(typeReference.getType(), null, userInfoResponse);
72-
} catch (IOException ex) {
73+
} catch (IOException | HttpMessageNotReadableException ex) {
7374
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
7475
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null);
7576
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
2727
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
2828
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
29+
import org.springframework.util.Assert;
2930
import org.springframework.util.StringUtils;
3031

3132
import java.util.Arrays;
@@ -52,6 +53,7 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
5253

5354
@Override
5455
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
56+
Assert.notNull(userRequest, "userRequest cannot be null");
5557
OidcUserInfo userInfo = null;
5658
if (this.shouldRetrieveUserInfo(userRequest)) {
5759
ParameterizedTypeReference<Map<String, Object>> typeReference =

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/CustomUserTypesOAuth2UserService.java

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public CustomUserTypesOAuth2UserService(Map<String, Class<? extends OAuth2User>>
4949

5050
@Override
5151
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
52+
Assert.notNull(userRequest, "userRequest cannot be null");
5253
String registrationId = userRequest.getClientRegistration().getRegistrationId();
5354
Class<? extends OAuth2User> customUserType;
5455
if ((customUserType = this.customUserTypes.get(registrationId)) == null) {

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
2424
import org.springframework.security.oauth2.core.user.OAuth2User;
2525
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
26+
import org.springframework.util.Assert;
2627
import org.springframework.util.StringUtils;
2728

2829
import java.util.HashSet;
@@ -52,6 +53,7 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
5253

5354
@Override
5455
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
56+
Assert.notNull(userRequest, "userRequest cannot be null");
5557
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
5658
if (!StringUtils.hasText(userNameAttributeName)) {
5759
OAuth2Error oauth2Error = new OAuth2Error(

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DelegatingOAuth2UserService.java

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public DelegatingOAuth2UserService(List<OAuth2UserService<R, U>> userServices) {
5151

5252
@Override
5353
public U loadUser(R userRequest) throws OAuth2AuthenticationException {
54+
Assert.notNull(userRequest, "userRequest cannot be null");
5455
return this.userServices.stream()
5556
.map(userService -> userService.loadUser(userRequest))
5657
.filter(Objects::nonNull)

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/NimbusUserInfoResponseClient.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.http.client.AbstractClientHttpResponse;
2828
import org.springframework.http.client.ClientHttpResponse;
2929
import org.springframework.http.converter.GenericHttpMessageConverter;
30+
import org.springframework.http.converter.HttpMessageNotReadableException;
3031
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
3132
import org.springframework.security.authentication.AuthenticationServiceException;
3233
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -54,7 +55,7 @@ <T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, Class<T> returnType
5455
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken());
5556
try {
5657
return (T) this.genericHttpMessageConverter.read(returnType, userInfoResponse);
57-
} catch (IOException ex) {
58+
} catch (IOException | HttpMessageNotReadableException ex) {
5859
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
5960
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null);
6061
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
@@ -66,7 +67,7 @@ <T> T getUserInfoResponse(OAuth2UserRequest userInfoRequest, ParameterizedTypeRe
6667
userInfoRequest.getClientRegistration(), userInfoRequest.getAccessToken());
6768
try {
6869
return (T) this.genericHttpMessageConverter.read(typeReference.getType(), null, userInfoResponse);
69-
} catch (IOException ex) {
70+
} catch (IOException | HttpMessageNotReadableException ex) {
7071
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE,
7172
"An error occurred reading the UserInfo Success response: " + ex.getMessage(), null);
7273
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/HttpSessionOAuth2AuthorizationRequestRepository.java

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.security.oauth2.client.web;
1717

1818
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
19+
import org.springframework.util.Assert;
1920

2021
import javax.servlet.http.HttpServletRequest;
2122
import javax.servlet.http.HttpServletResponse;
@@ -36,6 +37,7 @@ public final class HttpSessionOAuth2AuthorizationRequestRepository implements Au
3637

3738
@Override
3839
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
40+
Assert.notNull(request, "request cannot be null");
3941
HttpSession session = request.getSession(false);
4042
if (session != null) {
4143
return (OAuth2AuthorizationRequest) session.getAttribute(this.sessionAttributeName);
@@ -46,6 +48,8 @@ public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest re
4648
@Override
4749
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
4850
HttpServletResponse response) {
51+
Assert.notNull(request, "request cannot be null");
52+
Assert.notNull(response, "response cannot be null");
4953
if (authorizationRequest == null) {
5054
this.removeAuthorizationRequest(request);
5155
return;
@@ -55,6 +59,7 @@ public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationReq
5559

5660
@Override
5761
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
62+
Assert.notNull(request, "request cannot be null");
5863
OAuth2AuthorizationRequest authorizationRequest = this.loadAuthorizationRequest(request);
5964
if (authorizationRequest != null) {
6065
request.getSession().removeAttribute(this.sessionAttributeName);

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationRequestUriBuilder.java

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
1919
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
20+
import org.springframework.util.Assert;
2021
import org.springframework.util.StringUtils;
2122
import org.springframework.web.util.UriComponentsBuilder;
2223

@@ -35,6 +36,7 @@
3536
class OAuth2AuthorizationRequestUriBuilder {
3637

3738
URI build(OAuth2AuthorizationRequest authorizationRequest) {
39+
Assert.notNull(authorizationRequest, "authorizationRequest cannot be null");
3840
Set<String> scopes = authorizationRequest.getScopes();
3941
UriComponentsBuilder uriBuilder = UriComponentsBuilder
4042
.fromUriString(authorizationRequest.getAuthorizationUri())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright 2002-2017 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+
* http://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.client;
17+
18+
import org.junit.Test;
19+
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
21+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
22+
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
23+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
24+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
25+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.mockito.Mockito.mock;
29+
import static org.mockito.Mockito.when;
30+
31+
/**
32+
* Tests for {@link InMemoryOAuth2AuthorizedClientService}.
33+
*
34+
* @author Joe Grandja
35+
*/
36+
public class InMemoryOAuth2AuthorizedClientServiceTests {
37+
private String registrationId1 = "registration-1";
38+
private String registrationId2 = "registration-2";
39+
private String registrationId3 = "registration-3";
40+
private String principalName1 = "principal-1";
41+
private String principalName2 = "principal-2";
42+
43+
private ClientRegistration registration1 = ClientRegistration.withRegistrationId(this.registrationId1)
44+
.clientId("client-1")
45+
.clientSecret("secret")
46+
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
47+
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
48+
.redirectUri("{scheme}://{serverName}:{serverPort}{contextPath}/login/oauth2/code/{registrationId}")
49+
.scope("user")
50+
.authorizationUri("https://provider.com/oauth2/authorize")
51+
.tokenUri("https://provider.com/oauth2/token")
52+
.userInfoUri("https://provider.com/oauth2/user")
53+
.userNameAttributeName("id")
54+
.clientName("client-1")
55+
.build();
56+
57+
private ClientRegistration registration2 = ClientRegistration.withRegistrationId(this.registrationId2)
58+
.clientId("client-2")
59+
.clientSecret("secret")
60+
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
61+
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
62+
.redirectUri("{scheme}://{serverName}:{serverPort}{contextPath}/login/oauth2/code/{registrationId}")
63+
.scope("openid", "profile", "email")
64+
.authorizationUri("https://provider.com/oauth2/authorize")
65+
.tokenUri("https://provider.com/oauth2/token")
66+
.userInfoUri("https://provider.com/oauth2/userinfo")
67+
.jwkSetUri("https://provider.com/oauth2/keys")
68+
.clientName("client-2")
69+
.build();
70+
71+
private ClientRegistration registration3 = ClientRegistration.withRegistrationId(this.registrationId3)
72+
.clientId("client-3")
73+
.clientSecret("secret")
74+
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
75+
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
76+
.redirectUri("{scheme}://{serverName}:{serverPort}{contextPath}/login/oauth2/code/{registrationId}")
77+
.scope("openid", "profile")
78+
.authorizationUri("https://provider.com/oauth2/authorize")
79+
.tokenUri("https://provider.com/oauth2/token")
80+
.userInfoUri("https://provider.com/oauth2/userinfo")
81+
.jwkSetUri("https://provider.com/oauth2/keys")
82+
.clientName("client-3")
83+
.build();
84+
85+
private ClientRegistrationRepository clientRegistrationRepository =
86+
new InMemoryClientRegistrationRepository(this.registration1, this.registration2, this.registration3);
87+
88+
private InMemoryOAuth2AuthorizedClientService authorizedClientService =
89+
new InMemoryOAuth2AuthorizedClientService(this.clientRegistrationRepository);
90+
91+
92+
@Test(expected = IllegalArgumentException.class)
93+
public void constructorWhenClientRegistrationRepositoryIsNullThenThrowIllegalArgumentException() {
94+
new InMemoryOAuth2AuthorizedClientService(null);
95+
}
96+
97+
@Test(expected = IllegalArgumentException.class)
98+
public void loadAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {
99+
this.authorizedClientService.loadAuthorizedClient(null, this.principalName1);
100+
}
101+
102+
@Test(expected = IllegalArgumentException.class)
103+
public void loadAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {
104+
this.authorizedClientService.loadAuthorizedClient(this.registrationId1, null);
105+
}
106+
107+
@Test
108+
public void loadAuthorizedClientWhenClientRegistrationNotFoundThenReturnNull() {
109+
OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
110+
"registration-not-found", this.principalName1);
111+
assertThat(authorizedClient).isNull();
112+
}
113+
114+
@Test
115+
public void loadAuthorizedClientWhenClientRegistrationFoundButNotAssociatedToPrincipalThenReturnNull() {
116+
OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
117+
this.registrationId1, "principal-not-found");
118+
assertThat(authorizedClient).isNull();
119+
}
120+
121+
@Test
122+
public void loadAuthorizedClientWhenClientRegistrationFoundAndAssociatedToPrincipalThenReturnAuthorizedClient() {
123+
Authentication authentication = mock(Authentication.class);
124+
when(authentication.getName()).thenReturn(this.principalName1);
125+
126+
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
127+
this.registration1, this.principalName1, mock(OAuth2AccessToken.class));
128+
this.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);
129+
130+
OAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService.loadAuthorizedClient(
131+
this.registrationId1, this.principalName1);
132+
assertThat(loadedAuthorizedClient).isEqualTo(authorizedClient);
133+
}
134+
135+
@Test(expected = IllegalArgumentException.class)
136+
public void saveAuthorizedClientWhenAuthorizedClientIsNullThenThrowIllegalArgumentException() {
137+
this.authorizedClientService.saveAuthorizedClient(null, mock(Authentication.class));
138+
}
139+
140+
@Test(expected = IllegalArgumentException.class)
141+
public void saveAuthorizedClientWhenPrincipalIsNullThenThrowIllegalArgumentException() {
142+
this.authorizedClientService.saveAuthorizedClient(mock(OAuth2AuthorizedClient.class), null);
143+
}
144+
145+
@Test
146+
public void saveAuthorizedClientWhenSavedThenCanLoad() {
147+
Authentication authentication = mock(Authentication.class);
148+
when(authentication.getName()).thenReturn(this.principalName2);
149+
150+
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
151+
this.registration3, this.principalName2, mock(OAuth2AccessToken.class));
152+
this.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);
153+
154+
OAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService.loadAuthorizedClient(
155+
this.registrationId3, this.principalName2);
156+
assertThat(loadedAuthorizedClient).isEqualTo(authorizedClient);
157+
}
158+
159+
@Test(expected = IllegalArgumentException.class)
160+
public void removeAuthorizedClientWhenClientRegistrationIdIsNullThenThrowIllegalArgumentException() {
161+
this.authorizedClientService.removeAuthorizedClient(null, this.principalName2);
162+
}
163+
164+
@Test(expected = IllegalArgumentException.class)
165+
public void removeAuthorizedClientWhenPrincipalNameIsNullThenThrowIllegalArgumentException() {
166+
this.authorizedClientService.removeAuthorizedClient(this.registrationId2, null);
167+
}
168+
169+
@Test
170+
public void removeAuthorizedClientWhenSavedThenRemoved() {
171+
Authentication authentication = mock(Authentication.class);
172+
when(authentication.getName()).thenReturn(this.principalName2);
173+
174+
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
175+
this.registration2, this.principalName2, mock(OAuth2AccessToken.class));
176+
this.authorizedClientService.saveAuthorizedClient(authorizedClient, authentication);
177+
178+
OAuth2AuthorizedClient loadedAuthorizedClient = this.authorizedClientService.loadAuthorizedClient(
179+
this.registrationId2, this.principalName2);
180+
assertThat(loadedAuthorizedClient).isNotNull();
181+
182+
this.authorizedClientService.removeAuthorizedClient(this.registrationId2, this.principalName2);
183+
184+
loadedAuthorizedClient = this.authorizedClientService.loadAuthorizedClient(
185+
this.registrationId2, this.principalName2);
186+
assertThat(loadedAuthorizedClient).isNull();
187+
}
188+
}

0 commit comments

Comments
 (0)