added system wide cache for all symmetric validators, closes # 557
parent
ca333d256b
commit
e4d5f4a540
|
@ -16,19 +16,18 @@
|
|||
******************************************************************************/
|
||||
package org.mitre.openid.connect.client;
|
||||
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.*;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.PRIVATE_KEY;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_JWT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -37,9 +36,8 @@ import javax.servlet.http.HttpSession;
|
|||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.SystemDefaultHttpClient;
|
||||
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.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.jwt.signer.service.impl.SymmetricCacheService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
|
||||
|
@ -65,18 +63,13 @@ import org.springframework.web.client.HttpClientErrorException;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.Use;
|
||||
import com.nimbusds.jose.util.Base64;
|
||||
import com.nimbusds.jose.util.Base64URL;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
@ -104,6 +97,9 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
@Autowired
|
||||
private JWKSetCacheService validationServices;
|
||||
|
||||
@Autowired(required=false)
|
||||
private SymmetricCacheService symmetricCacheService;
|
||||
|
||||
@Autowired(required=false)
|
||||
private JwtSigningAndValidationService authenticationSignerService;
|
||||
|
||||
|
@ -131,6 +127,17 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// if our JOSE validators don't get wired in, drop defaults into place
|
||||
|
||||
if (validationServices == null) {
|
||||
validationServices = new JWKSetCacheService();
|
||||
}
|
||||
|
||||
if (symmetricCacheService == null) {
|
||||
symmetricCacheService = new SymmetricCacheService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -324,7 +331,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
|| alg.equals(JWSAlgorithm.HS512))) {
|
||||
|
||||
// generate one based on client secret
|
||||
signer = getSymmetricValidtor(clientConfig.getClient());
|
||||
signer = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient());
|
||||
|
||||
} else if (PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
|
||||
|
||||
|
@ -744,41 +751,4 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
this.authOptions = authOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package org.mitre.jwt.signer.service.impl;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.Use;
|
||||
import com.nimbusds.jose.util.Base64URL;
|
||||
|
||||
/**
|
||||
* Creates and caches symmetrical validators for clients based on client secrets.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class SymmetricCacheService {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SymmetricCacheService.class);
|
||||
|
||||
private LoadingCache<String, JwtSigningAndValidationService> validators;
|
||||
|
||||
|
||||
public SymmetricCacheService() {
|
||||
validators = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(24, TimeUnit.HOURS)
|
||||
.maximumSize(100)
|
||||
.build(new SymmetricValidatorBuilder());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a symmetric signing and validation service for the given client
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
public JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
|
||||
|
||||
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 {
|
||||
return validators.get(client.getClientSecret());
|
||||
} catch (UncheckedExecutionException ue) {
|
||||
logger.error("Problem loading client validator", ue);
|
||||
return null;
|
||||
} catch (ExecutionException e) {
|
||||
logger.error("Problem loading client validator", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SymmetricValidatorBuilder extends CacheLoader<String, JwtSigningAndValidationService> {
|
||||
@Override
|
||||
public JwtSigningAndValidationService load(String key) throws Exception {
|
||||
try {
|
||||
|
||||
String id = "SYMMETRIC-KEY";
|
||||
|
||||
JWK jwk = new OctetSequenceKey(Base64URL.encode(key), Use.SIGNATURE, null, id, null, null, null);
|
||||
Map<String, JWK> keys = ImmutableMap.of(id, jwk);
|
||||
JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keys);
|
||||
|
||||
return service;
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("Couldn't create symmetric validator for client", e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
logger.error("Couldn't create symmetric validator for client", e);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Couldn't create symmetric validator for client");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -16,8 +16,6 @@
|
|||
******************************************************************************/
|
||||
package org.mitre.openid.connect;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
@ -26,8 +24,8 @@ import java.util.UUID;
|
|||
|
||||
import org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService;
|
||||
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.SymmetricCacheService;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||
import org.mitre.oauth2.service.SystemScopeService;
|
||||
|
@ -35,23 +33,18 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
||||
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.provider.DefaultOAuth2RequestFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.Algorithm;
|
||||
import com.nimbusds.jose.JWEObject.State;
|
||||
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.EncryptedJWT;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
@ -68,6 +61,9 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
|
|||
|
||||
@Autowired
|
||||
private JWKSetCacheService validators;
|
||||
|
||||
@Autowired
|
||||
private SymmetricCacheService symmetricCacheService;
|
||||
|
||||
@Autowired
|
||||
private SystemScopeService systemScopes;
|
||||
|
@ -126,15 +122,19 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
|
|||
}
|
||||
|
||||
if (request.getClientId() != null) {
|
||||
ClientDetailsEntity client = clientDetailsService.loadClientByClientId(request.getClientId());
|
||||
|
||||
if ((request.getScope() == null || request.getScope().isEmpty())) {
|
||||
Set<String> clientScopes = client.getScope();
|
||||
request.setScope(clientScopes);
|
||||
}
|
||||
|
||||
if (request.getExtensions().get("max_age") == null && client.getDefaultMaxAge() != null) {
|
||||
request.getExtensions().put("max_age", client.getDefaultMaxAge().toString());
|
||||
try {
|
||||
ClientDetailsEntity client = clientDetailsService.loadClientByClientId(request.getClientId());
|
||||
|
||||
if ((request.getScope() == null || request.getScope().isEmpty())) {
|
||||
Set<String> clientScopes = client.getScope();
|
||||
request.setScope(clientScopes);
|
||||
}
|
||||
|
||||
if (request.getExtensions().get("max_age") == null && client.getDefaultMaxAge() != null) {
|
||||
request.getExtensions().put("max_age", client.getDefaultMaxAge().toString());
|
||||
}
|
||||
} catch (OAuth2Exception e) {
|
||||
logger.error("Caught OAuth2 exception trying to test client scopes and max age:", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
|
|||
|
||||
// it's HMAC, we need to make a validator based on the client secret
|
||||
|
||||
JwtSigningAndValidationService validator = getSymmetricValidtor(client);
|
||||
JwtSigningAndValidationService validator = symmetricCacheService.getSymmetricValidtor(client);
|
||||
|
||||
if (validator == null) {
|
||||
throw new InvalidClientException("Unable to create signature validator for client's secret: " + client.getClientSecret());
|
||||
|
@ -366,40 +366,4 @@ public class ConnectOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a symmetric signing and validation service for the given client
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
private JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,15 +19,12 @@
|
|||
*/
|
||||
package org.mitre.openid.connect.assertion;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
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.SymmetricCacheService;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod;
|
||||
import org.mitre.oauth2.service.ClientDetailsEntityService;
|
||||
|
@ -42,13 +39,7 @@ import org.springframework.security.core.AuthenticationException;
|
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
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.ReadOnlyJWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
@ -64,6 +55,10 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
|
|||
// map of verifiers, load keys for clients
|
||||
@Autowired
|
||||
private JWKSetCacheService validators;
|
||||
|
||||
// map of symmetric verifiers for client secrets
|
||||
@Autowired
|
||||
private SymmetricCacheService symmetricCacheService;
|
||||
|
||||
// Allow for time sync issues by having a window of X seconds.
|
||||
private int timeSkewAllowance = 300;
|
||||
|
@ -123,7 +118,7 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
|
|||
|
||||
// it's HMAC, we need to make a validator based on the client secret
|
||||
|
||||
JwtSigningAndValidationService validator = getSymmetricValidtor(client);
|
||||
JwtSigningAndValidationService validator = symmetricCacheService.getSymmetricValidtor(client);
|
||||
|
||||
if (validator == null) {
|
||||
throw new AuthenticationServiceException("Unable to create signature validator for client's secret: " + client.getClientSecret());
|
||||
|
@ -199,43 +194,4 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
|
|||
return (JwtBearerAssertionAuthenticationToken.class.isAssignableFrom(authentication));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue