
com.nimbusds.openid.connect.provider.spi.claims.http.HTTPClaimsSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oidc-claims-source-http Show documentation
Show all versions of oidc-claims-source-http Show documentation
OpenID Connect HTTP claims source
The newest version!
package com.nimbusds.openid.connect.provider.spi.claims.http;
import com.nimbusds.common.contenttype.ContentType;
import com.nimbusds.langtag.LangTag;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.id.Subject;
import com.nimbusds.oauth2.sdk.util.MapUtils;
import com.nimbusds.openid.connect.provider.spi.InitContext;
import com.nimbusds.openid.connect.provider.spi.claims.AdvancedClaimsSource;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimUtils;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimsSourceRequestContext;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
import net.jcip.annotations.ThreadSafe;
import net.minidev.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Connector for retrieving OpenID Connect claims from an HTTP endpoint.
*/
@ThreadSafe
public class HTTPClaimsSource implements AdvancedClaimsSource {
/**
* The configuration file path.
*/
public static final String CONFIG_FILE_PATH = "/WEB-INF/httpClaimsSource.properties";
/**
* The HTTP connector configuration.
*/
private Configuration config;
/**
* Logs the overriding system properties.
*/
static void logOverridingSystemProperties() {
Set overriddenPropNames = new HashSet<>();
for (String key: System.getProperties().stringPropertyNames()) {
if (key.startsWith(Configuration.DEFAULT_PREFIX))
overriddenPropNames.add(key);
}
Loggers.MAIN_LOG.info("[CSHTTP0010] Overriding system properties: {}", overriddenPropNames);
}
/**
* Loads the configuration.
*
* @param initContext The initialisation context. Must not be
* {@code null}.
*
* @return The configuration.
*
* @throws IOException If loading failed.
*/
private static Configuration loadConfiguration(final InitContext initContext)
throws IOException {
var props = new Properties();
InputStream inputStream = initContext.getResourceAsStream(CONFIG_FILE_PATH);
if (inputStream != null) {
props.load(inputStream);
} else {
Loggers.MAIN_LOG.warn("[CSHTTP0015] HTTP claims source configuration file {} not found, trying system properties", CONFIG_FILE_PATH);
}
// Override with any system properties
logOverridingSystemProperties();
props.putAll(System.getProperties());
return new Configuration(props);
}
@Override
public void init(final InitContext initContext)
throws Exception {
Loggers.MAIN_LOG.info("[CSHTTP0011] Initializing HTTP claims source...");
config = loadConfiguration(initContext);
config.log();
}
/**
* Returns the HTTP claims source configuration.
*
* @return The HTTP claims source configuration, {@code null} if not
* configured yet.
*/
public Configuration getConfiguration() {
return config;
}
@Override
public boolean isEnabled() {
return config.enable;
}
@Override
public Set supportedClaims() {
if (! config.enable) {
// Empty set
return Collections.emptySet();
}
return config.supportedClaims;
}
@Override
public UserInfo getClaims(final Subject subject,
final Set claims,
final List claimsLocales,
final ClaimsSourceRequestContext ctx)
throws Exception {
if (! config.enable) {
return null;
}
// Apply the preferred language tags if any
Set individualClaims = ClaimUtils.applyLangTags(claims, claimsLocales);
// Construct the request JSON object
var requestJSONObject = new JSONObject();
requestJSONObject.put("iss", ctx.getIssuer().getValue());
requestJSONObject.put("sub", subject.getValue());
requestJSONObject.put("claims", new LinkedList<>(individualClaims));
if (ctx.getClaimsTransport() != null) {
requestJSONObject.put("claims_transport", ctx.getClaimsTransport().name().toLowerCase());
}
if (MapUtils.isNotEmpty(ctx.getClaimsData())) {
requestJSONObject.put("claims_data", ctx.getClaimsData());
}
if (config.includeSubjectSessionID && ctx.getSubjectSessionID() != null) {
requestJSONObject.put("sub_sid", ctx.getSubjectSessionID().getValue());
}
if (config.includeSubjectSession && ctx.getSubjectSession() != null) {
requestJSONObject.put("sub_session", JSONUtils.toJSONObject(ctx.getSubjectSession()));
}
if (config.includeScope && ctx.getScope() != null) {
requestJSONObject.put("scope", ctx.getScope().toStringList());
}
JSONObject claimsJSONObject;
try {
var httpRequest = new HTTPRequest(HTTPRequest.Method.POST, config.url.toURL());
httpRequest.setEntityContentType(ContentType.APPLICATION_JSON);
httpRequest.setAccept(ContentType.APPLICATION_JSON.getType());
httpRequest.setAuthorization(config.apiAccessToken.toAuthorizationHeader());
httpRequest.setConnectTimeout(config.connectTimeout);
httpRequest.setReadTimeout(config.readTimeout);
httpRequest.setBody(requestJSONObject.toJSONString());
HTTPResponse httpResponse = httpRequest.send();
httpResponse.ensureStatusCode(200);
claimsJSONObject = httpResponse.getBodyAsJSONObject();
} catch (Exception e) {
Loggers.USERINFO_LOG.error("[CSHTTP0014] UserInfo retrieval error: {} {}", e.getMessage(), e);
throw new Exception("Couldn't get UserInfo for subject \"" + subject + "\": " + e.getMessage(), e);
}
if (claimsJSONObject.isEmpty()) {
// Invalid subject or not found
return null;
}
if (! claimsJSONObject.containsKey("sub")) {
// Make sure we have the subject in the JSON object
// before we create a UserInfo object from it
claimsJSONObject.put("sub", subject.getValue());
}
try {
return new UserInfo(claimsJSONObject);
} catch (Exception e) {
Loggers.USERINFO_LOG.error("[CSHTTP0013] UserInfo construction error: {} {}", e.getMessage(), e);
throw new Exception("Couldn't create UserInfo object: " + e.getMessage(), e);
}
}
@Override
public void shutdown() {
Loggers.MAIN_LOG.info("[CSHTTP0012] Shutting down HTTP claims source...");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy