added dynamic discovery to client

pull/324/merge
Justin Richer 2013-04-16 15:01:08 -04:00
parent 33af3b1ad6
commit f76f44b999
2 changed files with 111 additions and 6 deletions

View File

@ -0,0 +1,100 @@
/**
*
*/
package org.mitre.openid.connect.client.service.impl;
import java.util.concurrent.ExecutionException;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.mitre.openid.connect.client.service.ServerConfigurationService;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
*
* Dynamically fetches OpenID Connect server configurations based on the issuer. Caches the server configurations.
*
* @author jricher
*
*/
public class DynamicServerConfigurationService implements ServerConfigurationService {
private static Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
// map of issuer -> server configuration, loaded dynamically from service discovery
private LoadingCache<String, ServerConfiguration> servers;
public DynamicServerConfigurationService() {
// initialize the cache
servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher());
}
@Override
public ServerConfiguration getServerConfiguration(String issuer) {
try {
return servers.get(issuer);
} catch (ExecutionException e) {
logger.warn("Couldn't load configuration for " + issuer, e);
return null;
}
}
/**
* @author jricher
*
*/
private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader<String, ServerConfiguration> {
private HttpClient httpClient = new DefaultHttpClient();
private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
private RestTemplate restTemplate = new RestTemplate(httpFactory);
@Override
public ServerConfiguration load(String issuer) throws Exception {
// data holder
ServerConfiguration conf = new ServerConfiguration();
// construct the well-known URI
String url = issuer + "/.well-known/openid-configuration";
// fetch the value
String jsonString = restTemplate.getForObject(url, String.class);
JsonElement parsed = new JsonParser().parse(jsonString);
if (parsed.isJsonObject()) {
JsonObject o = parsed.getAsJsonObject();
// sanity checks
if (!issuer.equals(o.get("issuer").getAsString())) {
throw new IllegalStateException("Discovered issuers didn't match, expected " + issuer + " got " + o.get("issuer").getAsString());
}
conf.setIssuer(o.get("issuer").getAsString());
conf.setAuthorizationEndpointUri(o.get("authorization_endpoint").getAsString());
conf.setTokenEndpointUri(o.get("token_endpoint").getAsString());
conf.setJwksUri(o.get("jwks_uri").getAsString());
conf.setUserInfoUri(o.get("userinfo_endpoint").getAsString());
return conf;
} else {
throw new IllegalStateException("Couldn't parse server discovery results for " + url);
}
}
}
}

View File

@ -15,6 +15,8 @@ import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultHttpClient;
import org.mitre.jose.keystore.JWKSetKeyStore; import org.mitre.jose.keystore.JWKSetKeyStore;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -31,7 +33,8 @@ import com.nimbusds.jose.jwk.RSAKey;
/** /**
* *
* Creates a * Creates a caching map of JOSE signers and validators keyed on the JWK Set URI.
* Dynamically loads JWK Sets to create the signing and validation services.
* *
* @author jricher * @author jricher
* *
@ -39,6 +42,9 @@ import com.nimbusds.jose.jwk.RSAKey;
@Service @Service
public class JWKSetSigningAndValidationServiceCacheService { public class JWKSetSigningAndValidationServiceCacheService {
private static Logger logger = LoggerFactory.getLogger(JWKSetSigningAndValidationServiceCacheService.class);
// map of jwk set uri -> signing/validation service built on the keys found in that jwk set
private LoadingCache<String, JwtSigningAndValidationService> cache; private LoadingCache<String, JwtSigningAndValidationService> cache;
public JWKSetSigningAndValidationServiceCacheService() { public JWKSetSigningAndValidationServiceCacheService() {
@ -48,17 +54,16 @@ public class JWKSetSigningAndValidationServiceCacheService {
} }
/** /**
* @param key * @param jwksUri
* @return * @return
* @throws ExecutionException * @throws ExecutionException
* @see com.google.common.cache.Cache#get(java.lang.Object) * @see com.google.common.cache.Cache#get(java.lang.Object)
*/ */
public JwtSigningAndValidationService get(String key) { public JwtSigningAndValidationService get(String jwksUri) {
try { try {
return cache.get(key); return cache.get(jwksUri);
} catch (ExecutionException e) { } catch (ExecutionException e) {
// TODO Auto-generated catch block logger.warn("Couldn't load JWK Set from " + jwksUri, e);
e.printStackTrace();
return null; return null;
} }
} }