/* * Copyright 2012-2017 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 * * http://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.authentication; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.oauth2.client.user.OAuth2UserService; import org.springframework.security.oauth2.core.AccessToken; import org.springframework.security.oauth2.core.endpoint.TokenResponseAttributes; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.util.Assert; import java.util.Collection; /** * An implementation of an {@link AuthenticationProvider} that is responsible for authenticating * an <i>authorization code</i> credential with the authorization server's <i>Token Endpoint</i> * and if valid, exchanging it for an <i>access token</i> credential. * Additionally, it will also obtain the end-user's (resource owner) attributes from the <i>UserInfo Endpoint</i> * (using the <i>access token</i>) and create a <code>Principal</code> in the form of an {@link OAuth2User} * associating it with the returned {@link OAuth2AuthenticationToken}. * * <p> * The {@link AuthorizationCodeAuthenticationProvider} uses an {@link AuthorizationGrantTokenExchanger} * to make a request to the authorization server's <i>Token Endpoint</i> * to verify the {@link AuthorizationCodeAuthenticationToken#getAuthorizationCode()}. * If the request is valid, the authorization server will respond back with a {@link TokenResponseAttributes}. * * <p> * It will then create a {@link OAuth2AuthenticationToken} associating the {@link AccessToken} * from the {@link TokenResponseAttributes} and pass it to {@link OAuth2UserService#loadUser(OAuth2AuthenticationToken)} * to obtain the end-user's (resource owner) attributes in the form of an {@link OAuth2User}. * * <p> * Finally, it will create another {@link OAuth2AuthenticationToken}, this time associating * the {@link AccessToken} and {@link OAuth2User} and return it to the {@link AuthenticationManager}, * at which point the {@link OAuth2AuthenticationToken} is considered <i>"authenticated"</i>. * * @author Joe Grandja * @since 5.0 * @see AuthorizationCodeAuthenticationToken * @see AuthorizationGrantTokenExchanger * @see TokenResponseAttributes * @see AccessToken * @see OAuth2UserService * @see OAuth2User * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a> * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a> * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response</a> */ public class AuthorizationCodeAuthenticationProvider implements AuthenticationProvider { private final AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger; private final OAuth2UserService userInfoService; private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); public AuthorizationCodeAuthenticationProvider( AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger, OAuth2UserService userInfoService) { Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null"); Assert.notNull(userInfoService, "userInfoService cannot be null"); this.authorizationCodeTokenExchanger = authorizationCodeTokenExchanger; this.userInfoService = userInfoService; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (AuthorizationCodeAuthenticationToken) authentication; TokenResponseAttributes tokenResponse = this.authorizationCodeTokenExchanger.exchange(authorizationCodeAuthentication); AccessToken accessToken = new AccessToken(tokenResponse.getTokenType(), tokenResponse.getTokenValue(), tokenResponse.getIssuedAt(), tokenResponse.getExpiresAt(), tokenResponse.getScopes()); OAuth2AuthenticationToken accessTokenAuthentication = new OAuth2AuthenticationToken( authorizationCodeAuthentication.getClientRegistration(), accessToken); accessTokenAuthentication.setDetails(authorizationCodeAuthentication.getDetails()); OAuth2User user = this.userInfoService.loadUser(accessTokenAuthentication); Collection<? extends GrantedAuthority> authorities = this.authoritiesMapper.mapAuthorities(user.getAuthorities()); OAuth2AuthenticationToken authenticationResult = new OAuth2AuthenticationToken(user, authorities, accessTokenAuthentication.getClientRegistration(), accessTokenAuthentication.getAccessToken()); authenticationResult.setDetails(accessTokenAuthentication.getDetails()); return authenticationResult; } @Override public boolean supports(Class<?> authentication) { return AuthorizationCodeAuthenticationToken.class.isAssignableFrom(authentication); } public final void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); this.authoritiesMapper = authoritiesMapper; } }