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

org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter Maven / Gradle / Ivy

There is a newer version: 6.3.1
Show newest version
/*
 * Copyright 2002-2020 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.web;

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * A {@code Filter} for the OAuth 2.0 Authorization Code Grant,
 * which handles the processing of the OAuth 2.0 Authorization Response.
 *
 * 

* The OAuth 2.0 Authorization Response is processed as follows: * *

    *
  • * Assuming the End-User (Resource Owner) has granted access to the Client, the Authorization Server will append the * {@link OAuth2ParameterNames#CODE code} and {@link OAuth2ParameterNames#STATE state} parameters * to the {@link OAuth2ParameterNames#REDIRECT_URI redirect_uri} (provided in the Authorization Request) * and redirect the End-User's user-agent back to this {@code Filter} (the Client). *
  • *
  • * This {@code Filter} will then create an {@link OAuth2AuthorizationCodeAuthenticationToken} with * the {@link OAuth2ParameterNames#CODE code} received and * delegate it to the {@link AuthenticationManager} to authenticate. *
  • *
  • * Upon a successful authentication, an {@link OAuth2AuthorizedClient Authorized Client} is created by associating the * {@link OAuth2AuthorizationCodeAuthenticationToken#getClientRegistration() client} to the * {@link OAuth2AuthorizationCodeAuthenticationToken#getAccessToken() access token} and current {@code Principal} * and saving it via the {@link OAuth2AuthorizedClientRepository}. *
  • *
* * @author Joe Grandja * @since 5.1 * @see OAuth2AuthorizationCodeAuthenticationToken * @see OAuth2AuthorizationCodeAuthenticationProvider * @see OAuth2AuthorizationRequest * @see OAuth2AuthorizationResponse * @see AuthorizationRequestRepository * @see OAuth2AuthorizationRequestRedirectFilter * @see ClientRegistrationRepository * @see OAuth2AuthorizedClient * @see OAuth2AuthorizedClientRepository * @see Section 4.1 Authorization Code Grant * @see Section 4.1.2 Authorization Response */ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter { private final ClientRegistrationRepository clientRegistrationRepository; private final OAuth2AuthorizedClientRepository authorizedClientRepository; private final AuthenticationManager authenticationManager; private AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository(); private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); private final RequestCache requestCache = new HttpSessionRequestCache(); /** * Constructs an {@code OAuth2AuthorizationCodeGrantFilter} using the provided parameters. * * @param clientRegistrationRepository the repository of client registrations * @param authorizedClientRepository the authorized client repository * @param authenticationManager the authentication manager */ public OAuth2AuthorizationCodeGrantFilter(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository, AuthenticationManager authenticationManager) { Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null"); Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null"); Assert.notNull(authenticationManager, "authenticationManager cannot be null"); this.clientRegistrationRepository = clientRegistrationRepository; this.authorizedClientRepository = authorizedClientRepository; this.authenticationManager = authenticationManager; } /** * Sets the repository for stored {@link OAuth2AuthorizationRequest}'s. * * @param authorizationRequestRepository the repository for stored {@link OAuth2AuthorizationRequest}'s */ public final void setAuthorizationRequestRepository(AuthorizationRequestRepository authorizationRequestRepository) { Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null"); this.authorizationRequestRepository = authorizationRequestRepository; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (matchesAuthorizationResponse(request)) { processAuthorizationResponse(request, response); return; } filterChain.doFilter(request, response); } private boolean matchesAuthorizationResponse(HttpServletRequest request) { MultiValueMap params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { return false; } OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.loadAuthorizationRequest(request); if (authorizationRequest == null) { return false; } // Compare redirect_uri UriComponents requestUri = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request)).build(); UriComponents redirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()).build(); Set>> requestUriParameters = new LinkedHashSet<>(requestUri.getQueryParams().entrySet()); Set>> redirectUriParameters = new LinkedHashSet<>(redirectUri.getQueryParams().entrySet()); // Remove the additional request parameters (if any) from the authorization response (request) // before doing an exact comparison with the authorizationRequest.getRedirectUri() parameters (if any) requestUriParameters.retainAll(redirectUriParameters); if (Objects.equals(requestUri.getScheme(), redirectUri.getScheme()) && Objects.equals(requestUri.getUserInfo(), redirectUri.getUserInfo()) && Objects.equals(requestUri.getHost(), redirectUri.getHost()) && Objects.equals(requestUri.getPort(), redirectUri.getPort()) && Objects.equals(requestUri.getPath(), redirectUri.getPath()) && Objects.equals(requestUriParameters.toString(), redirectUriParameters.toString())) { return true; } return false; } private void processAuthorizationResponse(HttpServletRequest request, HttpServletResponse response) throws IOException { OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response); String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID); ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); MultiValueMap params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); String redirectUri = UrlUtils.buildFullRequestUrl(request); OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri); OAuth2AuthorizationCodeAuthenticationToken authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken( clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse)); authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); OAuth2AuthorizationCodeAuthenticationToken authenticationResult; try { authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) this.authenticationManager.authenticate(authenticationRequest); } catch (OAuth2AuthorizationException ex) { OAuth2Error error = ex.getError(); UriComponentsBuilder uriBuilder = UriComponentsBuilder .fromUriString(authorizationRequest.getRedirectUri()) .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode()); if (!StringUtils.isEmpty(error.getDescription())) { uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription()); } if (!StringUtils.isEmpty(error.getUri())) { uriBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, error.getUri()); } this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString()); return; } Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication(); String principalName = currentAuthentication != null ? currentAuthentication.getName() : "anonymousUser"; OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient( authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(), authenticationResult.getRefreshToken()); this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request, response); String redirectUrl = authorizationRequest.getRedirectUri(); SavedRequest savedRequest = this.requestCache.getRequest(request, response); if (savedRequest != null) { redirectUrl = savedRequest.getRedirectUrl(); this.requestCache.removeRequest(request, response); } this.redirectStrategy.sendRedirect(request, response, redirectUrl); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy