added client_secret_jwt auth method support, closes #174

pull/576/head
Justin Richer 2014-03-04 23:45:36 +00:00
parent 485e17c893
commit b67121f0cd
3 changed files with 101 additions and 10 deletions

View File

@ -351,8 +351,14 @@ public class ClientDetailsEntity implements ClientDetails {
@Override @Override
@Transient @Transient
public boolean isSecretRequired() { public boolean isSecretRequired() {
// TODO: this should check the auth method field instead if (getTokenEndpointAuthMethod() != null &&
return getClientSecret() != null; getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC) ||
getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)) {
return true;
} else {
return false;
}
} }
/** /**

View File

@ -171,11 +171,10 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
JWSAlgorithm alg = signedJwt.getHeader().getAlgorithm(); JWSAlgorithm alg = signedJwt.getHeader().getAlgorithm();
if (client.getRequestObjectSigningAlg() != null) { if (client.getRequestObjectSigningAlg() == null ||
if (!client.getRequestObjectSigningAlg().equals(alg)) { !client.getRequestObjectSigningAlg().equals(alg)) {
throw new InvalidClientException("Client's registered request object signing algorithm (" + client.getRequestObjectSigningAlg() + ") does not match request object's actual algorithm (" + alg.getName() + ")"); throw new InvalidClientException("Client's registered request object signing algorithm (" + client.getRequestObjectSigningAlg() + ") does not match request object's actual algorithm (" + alg.getName() + ")");
} }
}
if (alg.equals(JWSAlgorithm.RS256) if (alg.equals(JWSAlgorithm.RS256)
|| alg.equals(JWSAlgorithm.RS384) || alg.equals(JWSAlgorithm.RS384)

View File

@ -19,12 +19,17 @@
*/ */
package org.mitre.openid.connect.assertion; package org.mitre.openid.connect.assertion;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Map;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.JWKSetCacheService; import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -37,6 +42,13 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.Use;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.ReadOnlyJWTClaimsSet; import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.SignedJWT;
@ -82,9 +94,45 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
// check the signature with nimbus // check the signature with nimbus
if (jwt instanceof SignedJWT) { if (jwt instanceof SignedJWT) {
SignedJWT jws = (SignedJWT)jwt; SignedJWT jws = (SignedJWT)jwt;
JWSAlgorithm alg = jws.getHeader().getAlgorithm();
if (client.getTokenEndpointAuthSigningAlg() == null ||
!client.getTokenEndpointAuthSigningAlg().equals(alg)) {
throw new InvalidClientException("Client's registered request object signing algorithm (" + client.getRequestObjectSigningAlg() + ") does not match request object's actual algorithm (" + alg.getName() + ")");
}
if (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) &&
(alg.equals(JWSAlgorithm.RS256)
|| alg.equals(JWSAlgorithm.RS384)
|| alg.equals(JWSAlgorithm.RS512))) {
JwtSigningAndValidationService validator = validators.getValidator(client.getJwksUri()); JwtSigningAndValidationService validator = validators.getValidator(client.getJwksUri());
if (validator == null || !validator.validateSignature(jws)) {
throw new AuthenticationServiceException("Invalid signature"); if (validator == null) {
throw new AuthenticationServiceException("Unable to create signature validator for client's JWKS URI: " + client.getJwksUri());
}
if (!validator.validateSignature(jws)) {
throw new AuthenticationServiceException("Signature did not validate for presented JWT authentication.");
}
} else if (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT) &&
(alg.equals(JWSAlgorithm.HS256)
|| alg.equals(JWSAlgorithm.HS384)
|| alg.equals(JWSAlgorithm.HS512))) {
// it's HMAC, we need to make a validator based on the client secret
JwtSigningAndValidationService validator = getSymmetricValidtor(client);
if (validator == null) {
throw new AuthenticationServiceException("Unable to create signature validator for client's secret: " + client.getClientSecret());
}
if (!validator.validateSignature(jws)) {
throw new AuthenticationServiceException("Signature did not validate for presented JWT authentication.");
}
} }
} }
@ -152,4 +200,42 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
} }
/**
* Create a symmetric signing and validation service for the given client
*
* @param client
* @return
*/
private JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
// TODO: cache
if (client == null) {
logger.error("Couldn't create symmetric validator for null client");
return null;
}
if (Strings.isNullOrEmpty(client.getClientSecret())) {
logger.error("Couldn't create symmetric validator for client " + client.getClientId() + " without a client secret");
return null;
}
try {
JWK jwk = new OctetSequenceKey(Base64URL.encode(client.getClientSecret()), Use.SIGNATURE, null, client.getClientId(), null, null, null);
Map<String, JWK> keys = ImmutableMap.of(client.getClientId(), jwk);
JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keys);
return service;
} catch (NoSuchAlgorithmException e) {
logger.error("Couldn't create symmetric validator for client " + client.getClientId(), e);
} catch (InvalidKeySpecException e) {
logger.error("Couldn't create symmetric validator for client " + client.getClientId(), e);
}
return null;
}
} }