1
1
/*
2
- * Copyright 2002-2017 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
package org .springframework .security .oauth2 .client .userinfo ;
17
17
18
18
import org .springframework .core .ParameterizedTypeReference ;
19
+ import org .springframework .core .convert .converter .Converter ;
20
+ import org .springframework .http .RequestEntity ;
21
+ import org .springframework .http .ResponseEntity ;
19
22
import org .springframework .security .core .GrantedAuthority ;
23
+ import org .springframework .security .oauth2 .client .http .OAuth2ErrorResponseErrorHandler ;
24
+ import org .springframework .security .oauth2 .client .registration .ClientRegistration ;
20
25
import org .springframework .security .oauth2 .core .OAuth2AuthenticationException ;
21
26
import org .springframework .security .oauth2 .core .OAuth2Error ;
22
27
import org .springframework .security .oauth2 .core .user .DefaultOAuth2User ;
23
28
import org .springframework .security .oauth2 .core .user .OAuth2User ;
24
29
import org .springframework .security .oauth2 .core .user .OAuth2UserAuthority ;
25
30
import org .springframework .util .Assert ;
26
31
import org .springframework .util .StringUtils ;
32
+ import org .springframework .web .client .ResponseErrorHandler ;
33
+ import org .springframework .web .client .RestClientException ;
34
+ import org .springframework .web .client .RestOperations ;
35
+ import org .springframework .web .client .RestTemplate ;
27
36
28
- import java .util .HashSet ;
37
+ import java .util .Collections ;
29
38
import java .util .Map ;
30
39
import java .util .Set ;
31
40
34
43
* <p>
35
44
* For standard OAuth 2.0 Provider's, the attribute name used to access the user's name
36
45
* from the UserInfo response is required and therefore must be available via
37
- * {@link org.springframework.security.oauth2.client.registration. ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName() UserInfoEndpoint.getUserNameAttributeName()}.
46
+ * {@link ClientRegistration.ProviderDetails.UserInfoEndpoint#getUserNameAttributeName() UserInfoEndpoint.getUserNameAttributeName()}.
38
47
* <p>
39
48
* <b>NOTE:</b> Attribute names are <b>not</b> standardized between providers and therefore will vary.
40
49
* Please consult the provider's API documentation for the set of supported user attribute names.
48
57
*/
49
58
public class DefaultOAuth2UserService implements OAuth2UserService <OAuth2UserRequest , OAuth2User > {
50
59
private static final String MISSING_USER_INFO_URI_ERROR_CODE = "missing_user_info_uri" ;
60
+
51
61
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute" ;
52
- private NimbusUserInfoResponseClient userInfoResponseClient = new NimbusUserInfoResponseClient ();
62
+
63
+ private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response" ;
64
+
65
+ private static final ParameterizedTypeReference <Map <String , Object >> PARAMETERIZED_RESPONSE_TYPE =
66
+ new ParameterizedTypeReference <Map <String , Object >>() {};
67
+
68
+ private Converter <OAuth2UserRequest , RequestEntity <?>> requestEntityConverter = new OAuth2UserRequestEntityConverter ();
69
+
70
+ private RestOperations restOperations ;
71
+
72
+ public DefaultOAuth2UserService () {
73
+ RestTemplate restTemplate = new RestTemplate ();
74
+ restTemplate .setErrorHandler (new OAuth2ErrorResponseErrorHandler ());
75
+ this .restOperations = restTemplate ;
76
+ }
53
77
54
78
@ Override
55
79
public OAuth2User loadUser (OAuth2UserRequest userRequest ) throws OAuth2AuthenticationException {
@@ -64,7 +88,8 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
64
88
);
65
89
throw new OAuth2AuthenticationException (oauth2Error , oauth2Error .toString ());
66
90
}
67
- String userNameAttributeName = userRequest .getClientRegistration ().getProviderDetails ().getUserInfoEndpoint ().getUserNameAttributeName ();
91
+ String userNameAttributeName = userRequest .getClientRegistration ().getProviderDetails ()
92
+ .getUserInfoEndpoint ().getUserNameAttributeName ();
68
93
if (!StringUtils .hasText (userNameAttributeName )) {
69
94
OAuth2Error oauth2Error = new OAuth2Error (
70
95
MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE ,
@@ -75,13 +100,63 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
75
100
throw new OAuth2AuthenticationException (oauth2Error , oauth2Error .toString ());
76
101
}
77
102
78
- ParameterizedTypeReference <Map <String , Object >> typeReference =
79
- new ParameterizedTypeReference <Map <String , Object >>() {};
80
- Map <String , Object > userAttributes = this .userInfoResponseClient .getUserInfoResponse (userRequest , typeReference );
81
- GrantedAuthority authority = new OAuth2UserAuthority (userAttributes );
82
- Set <GrantedAuthority > authorities = new HashSet <>();
83
- authorities .add (authority );
103
+ RequestEntity <?> request = this .requestEntityConverter .convert (userRequest );
104
+
105
+ ResponseEntity <Map <String , Object >> response ;
106
+ try {
107
+ response = this .restOperations .exchange (request , PARAMETERIZED_RESPONSE_TYPE );
108
+ } catch (OAuth2AuthenticationException ex ) {
109
+ OAuth2Error oauth2Error = ex .getError ();
110
+ StringBuilder errorDetails = new StringBuilder ();
111
+ errorDetails .append ("Error details: [" );
112
+ errorDetails .append ("UserInfo Uri: " ).append (
113
+ userRequest .getClientRegistration ().getProviderDetails ().getUserInfoEndpoint ().getUri ());
114
+ errorDetails .append (", Error Code: " ).append (oauth2Error .getErrorCode ());
115
+ if (oauth2Error .getDescription () != null ) {
116
+ errorDetails .append (", Error Description: " ).append (oauth2Error .getDescription ());
117
+ }
118
+ errorDetails .append ("]" );
119
+ oauth2Error = new OAuth2Error (INVALID_USER_INFO_RESPONSE_ERROR_CODE ,
120
+ "An error occurred while attempting to retrieve the UserInfo Resource: " + errorDetails .toString (), null );
121
+ throw new OAuth2AuthenticationException (oauth2Error , oauth2Error .toString (), ex );
122
+ } catch (RestClientException ex ) {
123
+ OAuth2Error oauth2Error = new OAuth2Error (INVALID_USER_INFO_RESPONSE_ERROR_CODE ,
124
+ "An error occurred while attempting to retrieve the UserInfo Resource: " + ex .getMessage (), null );
125
+ throw new OAuth2AuthenticationException (oauth2Error , oauth2Error .toString (), ex );
126
+ }
127
+
128
+ Map <String , Object > userAttributes = response .getBody ();
129
+ Set <GrantedAuthority > authorities = Collections .singleton (new OAuth2UserAuthority (userAttributes ));
84
130
85
131
return new DefaultOAuth2User (authorities , userAttributes , userNameAttributeName );
86
132
}
133
+
134
+ /**
135
+ * Sets the {@link Converter} used for converting the {@link OAuth2UserRequest}
136
+ * to a {@link RequestEntity} representation of the UserInfo Request.
137
+ *
138
+ * @since 5.1
139
+ * @param requestEntityConverter the {@link Converter} used for converting to a {@link RequestEntity} representation of the UserInfo Request
140
+ */
141
+ public final void setRequestEntityConverter (Converter <OAuth2UserRequest , RequestEntity <?>> requestEntityConverter ) {
142
+ Assert .notNull (requestEntityConverter , "requestEntityConverter cannot be null" );
143
+ this .requestEntityConverter = requestEntityConverter ;
144
+ }
145
+
146
+ /**
147
+ * Sets the {@link RestOperations} used when requesting the UserInfo resource.
148
+ *
149
+ * <p>
150
+ * <b>NOTE:</b> At a minimum, the supplied {@code restOperations} must be configured with the following:
151
+ * <ol>
152
+ * <li>{@link ResponseErrorHandler} - {@link OAuth2ErrorResponseErrorHandler}</li>
153
+ * </ol>
154
+ *
155
+ * @since 5.1
156
+ * @param restOperations the {@link RestOperations} used when requesting the UserInfo resource
157
+ */
158
+ public final void setRestOperations (RestOperations restOperations ) {
159
+ Assert .notNull (restOperations , "restOperations cannot be null" );
160
+ this .restOperations = restOperations ;
161
+ }
87
162
}
0 commit comments