added dynamic discovery to client
parent
33af3b1ad6
commit
f76f44b999
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue