added framework for processing assertions for client auth

pull/263/head
Justin Richer 2013-01-15 11:41:25 -05:00
parent ad5e77f7ff
commit abd64eccd6
3 changed files with 299 additions and 0 deletions

View File

@ -0,0 +1,90 @@
/**
*
*/
package org.mitre.openid.connect.assertion;
import java.util.Collection;
import org.mitre.jwt.model.Jwt;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* @author jricher
*
*/
public class JwtBearerAssertionAuthenticationToken extends AbstractAuthenticationToken {
private String clientId;
private Jwt jwt;
public JwtBearerAssertionAuthenticationToken(String clientId, Jwt jwt) {
super(null);
this.clientId = clientId;
this.jwt = jwt;
setAuthenticated(false);
}
public JwtBearerAssertionAuthenticationToken(String clientId, Jwt jwt, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.clientId = clientId;
this.jwt = jwt;
setAuthenticated(true);
}
/* (non-Javadoc)
* @see org.springframework.security.core.Authentication#getCredentials()
*/
@Override
public Object getCredentials() {
return jwt;
}
/* (non-Javadoc)
* @see org.springframework.security.core.Authentication#getPrincipal()
*/
@Override
public Object getPrincipal() {
return clientId;
}
/**
* @return the clientId
*/
public String getClientId() {
return clientId;
}
/**
* @param clientId the clientId to set
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* @return the jwt
*/
public Jwt getJwt() {
return jwt;
}
/**
* @param jwt the jwt to set
*/
public void setJwt(Jwt jwt) {
this.jwt = jwt;
}
/**
* Clear out the JWT that this token holds.
*/
@Override
public void eraseCredentials() {
super.eraseCredentials();
setJwt(null);
}
}

View File

@ -0,0 +1,123 @@
/**
*
*/
package org.mitre.openid.connect.assertion;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.mitre.jwt.signer.JwtSigner;
import org.mitre.jwt.signer.impl.RsaSigner;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
import org.mitre.key.fetch.KeyFetcher;
import org.mitre.oauth2.exception.ClientNotFoundException;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.openid.connect.config.OIDCServerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* @author jricher
*
*/
public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtBearerAuthenticationProvider.class);
private Map<ClientDetailsEntity, JwtSigningAndValidationService> validators = new HashMap<ClientDetailsEntity, JwtSigningAndValidationService>();
@Autowired
private ClientDetailsEntityService clientService;
/**
* Try to validate the client credentials by parsing and validating the JWT.
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JwtBearerAssertionAuthenticationToken jwtAuth = (JwtBearerAssertionAuthenticationToken)authentication;
try {
ClientDetailsEntity client = clientService.loadClientByClientId(jwtAuth.getClientId());
JwtSigningAndValidationService validator = getValidatorForClient(client);
} catch (ClientNotFoundException e) {
throw new UsernameNotFoundException("Could not find client: " + jwtAuth.getClientId());
}
}
/**
* We support {@link JwtBearerAssertionAuthenticationToken}s only.
*/
@Override
public boolean supports(Class<?> authentication) {
return (JwtBearerAssertionAuthenticationToken.class.isAssignableFrom(authentication));
}
protected JwtSigningAndValidationService getValidatorForClient(ClientDetailsEntity client) {
if(validators.containsKey(client)){
return validators.get(client);
} else {
KeyFetcher keyFetch = new KeyFetcher();
PublicKey signingKey = null;
if (client.getJwkUrl() != null) {
// prefer the JWK
signingKey = keyFetch.retrieveJwkKey(client);
} else if (client.getX509Url() != null) {
// use the x509 only if JWK isn't configured
signingKey = keyFetch.retrieveX509Key(client);
} else {
// no keys configured
logger.warn("No server key URLs configured for " + client.getIssuer());
}
if (signingKey != null) {
Map<String, JwtSigner> signers = new HashMap<String, JwtSigner>();
if (signingKey instanceof RSAPublicKey) {
RSAPublicKey rsaKey = (RSAPublicKey)signingKey;
// build an RSA signer
RsaSigner signer256 = new RsaSigner(JwsAlgorithm.RS256.getJwaName(), rsaKey, null);
RsaSigner signer384 = new RsaSigner(JwsAlgorithm.RS384.getJwaName(), rsaKey, null);
RsaSigner signer512 = new RsaSigner(JwsAlgorithm.RS512.getJwaName(), rsaKey, null);
signers.put(client.getIssuer() + JwsAlgorithm.RS256.getJwaName(), signer256);
signers.put(client.getIssuer() + JwsAlgorithm.RS384.getJwaName(), signer384);
signers.put(client.getIssuer() + JwsAlgorithm.RS512.getJwaName(), signer512);
}
JwtSigningAndValidationService signingAndValidationService = new DefaultJwtSigningAndValidationService(signers);
validationServices.put(client, signingAndValidationService);
return signingAndValidationService;
} else {
// there were either no keys returned or no URLs configured to fetch them, assume no checking on key signatures
return null;
}
}
}

View File

@ -0,0 +1,86 @@
/**
*
*/
package org.mitre.openid.connect.assertion;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mitre.jwt.model.Jwt;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import com.google.common.base.Strings;
/**
* Filter to check client authentication via JWT Bearer assertions.
*
* @author jricher
*
*/
public class JwtBearerClientAssertionTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
public JwtBearerClientAssertionTokenEndpointFilter() {
super();
// TODO Auto-generated constructor stub
}
public JwtBearerClientAssertionTokenEndpointFilter(String path) {
super(path);
// TODO Auto-generated constructor stub
}
/**
* Pull the assertion out of the request and send it up to the auth manager for processing.
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
// check for appropriate parameters
String assertionType = request.getParameter("client_assertion_type");
String assertion = request.getParameter("client_assertion");
try {
Jwt jwt = Jwt.parse(assertion);
String clientId = jwt.getClaims().getPrincipal();
Authentication authRequest = new JwtBearerAssertionAuthenticationToken(clientId, jwt);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IllegalArgumentException e) {
throw new BadCredentialsException("Invalid JWT credential: " + assertion);
}
}
/**
* Check to see if the "client_assertion_type" and "client_assertion" parameters are present and contain the right values.
*/
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
// check for appropriate parameters
String assertionType = request.getParameter("client_assertion_type");
String assertion = request.getParameter("client_assertion");
if (Strings.isNullOrEmpty(assertionType) || Strings.isNullOrEmpty(assertion)) {
return false;
} else if (!assertionType.equals("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) {
return false;
}
return super.requiresAuthentication(request, response);
}
}