added framework for processing assertions for client auth
parent
ad5e77f7ff
commit
abd64eccd6
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue