All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.security.oauth2.client.token.AccessTokenProviderChain Maven / Gradle / Ivy

There is a newer version: 2.5.2.RELEASE
Show newest version
/*
 * 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);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy