org.springframework.security.oauth2.client.token.AccessTokenProviderChain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-security-oauth2 Show documentation
Show all versions of spring-security-oauth2 Show documentation
Module for providing OAuth2 support to Spring Security
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.token;
import java.util.Collections;
import java.util.List;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
* A chain of OAuth2 access token providers. This implementation will iterate through its
* chain to find the first provider that supports the resource and use it to obtain the
* access token. Note that the order of the chain is relevant.
*
* @author Ryan Heaton
* @author Dave Syer
*/
public class AccessTokenProviderChain extends OAuth2AccessTokenSupport
implements AccessTokenProvider {
private final List chain;
private ClientTokenServices clientTokenServices;
public AccessTokenProviderChain(List chain) {
this.chain = chain == null ? Collections. emptyList()
: Collections.unmodifiableList(chain);
}
/**
* Token services for long-term persistence of access tokens.
*
* @param clientTokenServices the clientTokenServices to set
*/
public void setClientTokenServices(ClientTokenServices clientTokenServices) {
this.clientTokenServices = clientTokenServices;
}
public boolean supportsResource(OAuth2ProtectedResourceDetails resource) {
for (AccessTokenProvider tokenProvider : chain) {
if (tokenProvider.supportsResource(resource)) {
return true;
}
}
return false;
}
public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) {
for (AccessTokenProvider tokenProvider : chain) {
if (tokenProvider.supportsRefresh(resource)) {
return true;
}
}
return false;
}
public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails resource,
AccessTokenRequest request)
throws UserRedirectRequiredException, AccessDeniedException {
OAuth2AccessToken accessToken = null;
OAuth2AccessToken existingToken = null;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth instanceof AnonymousAuthenticationToken) {
if (!resource.isClientOnly()) {
throw new InsufficientAuthenticationException(
"Authentication is required to obtain an access token (anonymous not allowed)");
}
}
if (resource.isClientOnly() || (auth != null && auth.isAuthenticated())) {
existingToken = request.getExistingToken();
if (existingToken == null && clientTokenServices != null) {
existingToken = clientTokenServices.getAccessToken(resource, auth);
}
if (existingToken != null) {
if (existingToken.isExpired()) {
if (clientTokenServices != null) {
clientTokenServices.removeAccessToken(resource, auth);
}
OAuth2RefreshToken refreshToken = existingToken.getRefreshToken();
if (refreshToken != null && !resource.isClientOnly()) {
accessToken = refreshAccessToken(resource, refreshToken, request);
}
}
else {
accessToken = existingToken;
}
}
}
// Give unauthenticated users a chance to get a token and be redirected
if (accessToken == null) {
// looks like we need to try to obtain a new token.
accessToken = obtainNewAccessTokenInternal(resource, request);
if (accessToken == null) {
throw new IllegalStateException(
"An OAuth 2 access token must be obtained or an exception thrown.");
}
}
if (clientTokenServices != null
&& (resource.isClientOnly() || auth != null && auth.isAuthenticated())) {
clientTokenServices.saveAccessToken(resource, auth, accessToken);
}
return accessToken;
}
protected OAuth2AccessToken obtainNewAccessTokenInternal(
OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
throws UserRedirectRequiredException, AccessDeniedException {
if (request.isError()) {
// there was an oauth error...
throw OAuth2Exception.valueOf(request.toSingleValueMap());
}
for (AccessTokenProvider tokenProvider : chain) {
if (tokenProvider.supportsResource(details)) {
return tokenProvider.obtainAccessToken(details, request);
}
}
throw new OAuth2AccessDeniedException(
"Unable to obtain a new access token for resource '" + details.getId()
+ "'. The provider manager is not configured to support it.",
details);
}
/**
* Obtain a new access token for the specified resource using the refresh token.
*
* @param resource The resource.
* @param refreshToken The refresh token.
* @return The access token, or null if failed.
* @throws UserRedirectRequiredException
*/
public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails resource,
OAuth2RefreshToken refreshToken, AccessTokenRequest request)
throws UserRedirectRequiredException {
for (AccessTokenProvider tokenProvider : chain) {
if (tokenProvider.supportsRefresh(resource)) {
DefaultOAuth2AccessToken refreshedAccessToken = new DefaultOAuth2AccessToken(
tokenProvider.refreshAccessToken(resource, refreshToken,
request));
if (refreshedAccessToken.getRefreshToken() == null) {
// Fixes gh-712
refreshedAccessToken.setRefreshToken(refreshToken);
}
return refreshedAccessToken;
}
}
throw new OAuth2AccessDeniedException(
"Unable to obtain a new access token for resource '" + resource.getId()
+ "'. The provider manager is not configured to support it.",
resource);
}
}