com.racquettrack.security.oauth.OAuth2AuthenticationProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-security-oauth2-client Show documentation
Show all versions of spring-security-oauth2-client Show documentation
An OAuth2 Client implementation for web applications using Spring Security
The newest version!
package com.racquettrack.security.oauth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.util.Assert;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.Map;
/**
* Processes an OAuth2 authentication request. The request will typically originate from a
* {@link OAuth2AuthenticationFilter} and will operate on a {@link OAuth2AuthenticationToken}.
*
* The OAuth2 processes falls somewhere in between Spring Security's Authenticated and PreAuthenticated models. The
* Authenticated model is used as we still need to exchange the OAuth code in order to get a OAuth token.
*
* For that reason the implementation bears similarities to
* {@link org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider} in
* addition to {@link org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider},
* particularly the implementation of the {@link #authenticate(org.springframework.security.core.Authentication)}
* method.
*
* Once the token is obtained, the
* AuthenticationUserDetailsService implementation may still throw a UsernameNotFoundException, for example.
*
* @author paul.wheeler
*/
public class OAuth2AuthenticationProvider implements AuthenticationProvider, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2AuthenticationProvider.class);
private AuthenticationUserDetailsService authenticatedUserDetailsService = null;
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
private boolean throwExceptionWhenTokenRejected = false;
private OAuth2ServiceProperties oAuth2ServiceProperties = null;
private Client client = null;
/**
* Check whether all required properties have been set.
*/
public void afterPropertiesSet() {
Assert.notNull(authenticatedUserDetailsService, "An AuthenticationUserDetailsService must be set");
Assert.notNull(oAuth2ServiceProperties, "An oAuth2ServiceProperties must be set");
}
/**
* Performs authentication with the same contract as {@link
* org.springframework.security.authentication.AuthenticationManager#authenticate(org.springframework.security.core.Authentication)}.
*
* @param authentication the authentication request object.
* @return a fully authenticated object including credentials. May return null if the
* AuthenticationProvider is unable to support authentication of the passed
* Authentication object. In such a case, the next AuthenticationProvider that
* supports the presented Authentication class will be tried.
* @throws org.springframework.security.core.AuthenticationException
* if authentication fails.
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
LOGGER.debug("OAuth2Authentication authentication request: " + authentication);
if (authentication.getCredentials() == null) {
LOGGER.debug("No credentials found in request.");
if (throwExceptionWhenTokenRejected) {
throw new BadCredentialsException("No pre-authenticated credentials found in request.");
}
return null;
}
String token = getAccessToken(authentication);
OAuth2AuthenticationToken tmpToken = new OAuth2AuthenticationToken(token);
UserDetails ud = authenticatedUserDetailsService.loadUserDetails(tmpToken);
userDetailsChecker.check(ud);
OAuth2AuthenticationToken result =
new OAuth2AuthenticationToken(ud, token, ud.getAuthorities());
result.setDetails(authentication.getDetails());
return result;
}
/**
* Indicate that this provider only supports {@link OAuth2AuthenticationToken} (sub)classes.
*
* @param authentication The authentication object presented.
* @return true if the implementation can more closely evaluate the Authentication class
* presented
*/
@Override
public boolean supports(Class> authentication) {
return OAuth2AuthenticationToken.class.isAssignableFrom(authentication);
}
/**
* Set the AuthenticatedUserDetailsService to be used to load the {@code UserDetails} for the authenticated user.
*
* @param uds The {@link AuthenticationUserDetailsService} to use.
*/
public void setAuthenticatedUserDetailsService(AuthenticationUserDetailsService uds) {
this.authenticatedUserDetailsService = uds;
}
/**
* If true, causes the provider to throw a BadCredentialsException if the presented authentication
* request is invalid (contains a null principal or credentials). Otherwise it will just return
* null. Defaults to false.
* @param throwExceptionWhenTokenRejected True to throw an exception if the token is rejected.
*/
public void setThrowExceptionWhenTokenRejected(boolean throwExceptionWhenTokenRejected) {
this.throwExceptionWhenTokenRejected = throwExceptionWhenTokenRejected;
}
/**
* Sets the strategy which will be used to validate the loaded UserDetails object
* for the user. Defaults to an {@link org.springframework.security.authentication.AccountStatusUserDetailsChecker}.
* @param userDetailsChecker The {@link UserDetailsChecker} to use.
*/
public void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {
Assert.notNull(userDetailsChecker, "userDetailsChacker cannot be null");
this.userDetailsChecker = userDetailsChecker;
}
/**
* Exchange the current {@link Authentication}, which should be an instance of {@link OAuth2AuthenticationToken}
* containing an OAuth2 code as the credential, for an OAuth2 token.
* @param authentication Expected to be an instance of a {@link OAuth2AuthenticationToken}.
* @return The OAuth2 token from the OAuth Provider.
*/
protected String getAccessToken(Authentication authentication) {
String accessToken;
try {
Response response = getResponseForAccessTokenRequestFrom(authentication);
if (!isOkay(response)) {
throw new AuthenticationServiceException("Got HTTP error code from OAuth2 provider: "
+ response.getStatus());
}
Map userData = getUserDataMapFrom(response);
// Check to see if there was an error or not
if (userData.containsKey("error")) {
String output = response.readEntity(String.class);
LOGGER.error("Got error response from the OAuth Provider, output={}",
output);
throw new AuthenticationServiceException("Credentials were rejected by the OAuth Provider, output=" + output);
}
accessToken = (String)userData.get(oAuth2ServiceProperties.getAccessTokenName());
} catch (WebApplicationException | ProcessingException e) {
LOGGER.error("Error thrown by Jersey client when exchanging code for token", e);
throw new AuthenticationServiceException("Error thrown by Jersey client when exchanging code for token", e);
}
return accessToken;
}
private Response getResponseForAccessTokenRequestFrom(Authentication authentication) {
Client client = getClient();
MultivaluedMap values = new MultivaluedHashMap<>();
values.add(oAuth2ServiceProperties.getGrantTypeParamName(), oAuth2ServiceProperties.getGrantType());
values.add(oAuth2ServiceProperties.getClientIdParamName(), oAuth2ServiceProperties.getClientId());
values.add(oAuth2ServiceProperties.getClientSecretParamName(), oAuth2ServiceProperties.getClientSecret());
values.add(oAuth2ServiceProperties.getCodeParamName(), (String) authentication.getCredentials());
URI redirectUri = redirectUriUsing(authentication);
values.add(oAuth2ServiceProperties.getRedirectUriParamName(), redirectUri.toString());
WebTarget webTarget = client.target(oAuth2ServiceProperties.getAccessTokenUri());
return webTarget
.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.form(values));
}
private boolean isOkay(Response response) {
return response != null && response.getStatusInfo() == Response.Status.OK;
}
private Map getUserDataMapFrom(Response response) throws AuthenticationServiceException {
return response.readEntity(new GenericType © 2015 - 2025 Weber Informatics LLC | Privacy Policy