Skip to content

Commit f7d0385

Browse files
committed
OAuth2AuthorizedClientManager implementation works outside of request
Fixes gh-6780
1 parent a604468 commit f7d0385

File tree

2 files changed

+422
-0
lines changed

2 files changed

+422
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2002-2019 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.client;
17+
18+
import org.springframework.lang.Nullable;
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.core.endpoint.OAuth2ParameterNames;
23+
import org.springframework.util.Assert;
24+
import org.springframework.util.StringUtils;
25+
26+
import java.util.Collections;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import java.util.function.Function;
30+
31+
/**
32+
* An implementation of an {@link OAuth2AuthorizedClientManager}
33+
* that is capable of operating outside of a {@code HttpServletRequest} context,
34+
* e.g. in a scheduled/background thread and/or in the service-tier.
35+
*
36+
* @author Joe Grandja
37+
* @since 5.2
38+
* @see OAuth2AuthorizedClientManager
39+
* @see OAuth2AuthorizedClientProvider
40+
* @see OAuth2AuthorizedClientService
41+
*/
42+
public final class AuthorizedClientServiceOAuth2AuthorizedClientManager implements OAuth2AuthorizedClientManager {
43+
private final ClientRegistrationRepository clientRegistrationRepository;
44+
private final OAuth2AuthorizedClientService authorizedClientService;
45+
private OAuth2AuthorizedClientProvider authorizedClientProvider = context -> null;
46+
private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper = new DefaultContextAttributesMapper();
47+
48+
/**
49+
* Constructs an {@code AuthorizedClientServiceOAuth2AuthorizedClientManager} using the provided parameters.
50+
*
51+
* @param clientRegistrationRepository the repository of client registrations
52+
* @param authorizedClientService the authorized client service
53+
*/
54+
public AuthorizedClientServiceOAuth2AuthorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
55+
OAuth2AuthorizedClientService authorizedClientService) {
56+
Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
57+
Assert.notNull(authorizedClientService, "authorizedClientService cannot be null");
58+
this.clientRegistrationRepository = clientRegistrationRepository;
59+
this.authorizedClientService = authorizedClientService;
60+
}
61+
62+
@Nullable
63+
@Override
64+
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
65+
Assert.notNull(authorizeRequest, "authorizeRequest cannot be null");
66+
67+
String clientRegistrationId = authorizeRequest.getClientRegistrationId();
68+
OAuth2AuthorizedClient authorizedClient = authorizeRequest.getAuthorizedClient();
69+
Authentication principal = authorizeRequest.getPrincipal();
70+
71+
OAuth2AuthorizationContext.Builder contextBuilder;
72+
if (authorizedClient != null) {
73+
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
74+
} else {
75+
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
76+
Assert.notNull(clientRegistration, "Could not find ClientRegistration with id '" + clientRegistrationId + "'");
77+
authorizedClient = this.authorizedClientService.loadAuthorizedClient(clientRegistrationId, principal.getName());
78+
if (authorizedClient != null) {
79+
contextBuilder = OAuth2AuthorizationContext.withAuthorizedClient(authorizedClient);
80+
} else {
81+
contextBuilder = OAuth2AuthorizationContext.withClientRegistration(clientRegistration);
82+
}
83+
}
84+
OAuth2AuthorizationContext authorizationContext = contextBuilder
85+
.principal(principal)
86+
.attributes(this.contextAttributesMapper.apply(authorizeRequest))
87+
.build();
88+
89+
authorizedClient = this.authorizedClientProvider.authorize(authorizationContext);
90+
if (authorizedClient != null) {
91+
this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
92+
} else {
93+
// In the case of re-authorization, the returned `authorizedClient` may be null if re-authorization is not supported.
94+
// For these cases, return the provided `authorizationContext.authorizedClient`.
95+
if (authorizationContext.getAuthorizedClient() != null) {
96+
return authorizationContext.getAuthorizedClient();
97+
}
98+
}
99+
100+
return authorizedClient;
101+
}
102+
103+
/**
104+
* Sets the {@link OAuth2AuthorizedClientProvider} used for authorizing (or re-authorizing) an OAuth 2.0 Client.
105+
*
106+
* @param authorizedClientProvider the {@link OAuth2AuthorizedClientProvider} used for authorizing (or re-authorizing) an OAuth 2.0 Client
107+
*/
108+
public void setAuthorizedClientProvider(OAuth2AuthorizedClientProvider authorizedClientProvider) {
109+
Assert.notNull(authorizedClientProvider, "authorizedClientProvider cannot be null");
110+
this.authorizedClientProvider = authorizedClientProvider;
111+
}
112+
113+
/**
114+
* Sets the {@code Function} used for mapping attribute(s) from the {@link OAuth2AuthorizeRequest} to a {@code Map} of attributes
115+
* to be associated to the {@link OAuth2AuthorizationContext#getAttributes() authorization context}.
116+
*
117+
* @param contextAttributesMapper the {@code Function} used for supplying the {@code Map} of attributes
118+
* to the {@link OAuth2AuthorizationContext#getAttributes() authorization context}
119+
*/
120+
public void setContextAttributesMapper(Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper) {
121+
Assert.notNull(contextAttributesMapper, "contextAttributesMapper cannot be null");
122+
this.contextAttributesMapper = contextAttributesMapper;
123+
}
124+
125+
/**
126+
* The default implementation of the {@link #setContextAttributesMapper(Function) contextAttributesMapper}.
127+
*/
128+
public static class DefaultContextAttributesMapper implements Function<OAuth2AuthorizeRequest, Map<String, Object>> {
129+
130+
@Override
131+
public Map<String, Object> apply(OAuth2AuthorizeRequest authorizeRequest) {
132+
Map<String, Object> contextAttributes = Collections.emptyMap();
133+
String scope = authorizeRequest.getAttribute(OAuth2ParameterNames.SCOPE);
134+
if (StringUtils.hasText(scope)) {
135+
contextAttributes = new HashMap<>();
136+
contextAttributes.put(OAuth2AuthorizationContext.REQUEST_SCOPE_ATTRIBUTE_NAME,
137+
StringUtils.delimitedListToStringArray(scope, " "));
138+
}
139+
return contextAttributes;
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)