org.springframework.security.oauth2.client.token.AccessTokenProviderChain.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.security.oauth2.client.token.AccessTokenProviderChain.java

Source

/*
 * 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<AccessTokenProvider> chain;

    private ClientTokenServices clientTokenServices;

    public AccessTokenProviderChain(List<? extends AccessTokenProvider> chain) {
        this.chain = chain == null ? Collections.<AccessTokenProvider>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);
    }

}