Claims source
1. OpenID Connect claims
OpenID Connect enables client applications to retrieve claims (attributes), about the end-user after successful login and consent. The application has a choice of two methods to receive the requested claims:
- From the protected UserInfo endpoint (the default method, requires an access token);
- Included in the issued ID token.
Example claims about a logged-in user, typically delivered in JSON format:
{
"sub" : "248289761001",
"name" : "Jane Doe",
"given_name" : "Jane",
"family_name" : "Doe",
"email" : "janedoe@example.com",
"email_verified" : true,
"picture" : "https://example.com/janedoe/me.jpg"
}
2. Claims source SPI
The Connect2id server comes with a plugin interface (SPI) for collecting claims from one or more data sources, such as
- LDAP / Active Directory (connector available)
- HTTP endpoint (connector available)
- SQL and NoSQL databases
- SCIM web service
- HR system
- External identity and claims providers
Features of the claims source SPI:
-
Provides an initialisation method, to configure the source or establish a database connection.
-
Method to let the Connect2id server query the names of the claims supported by the connector.
-
Can handle language tags (RFC 5646).
-
Supports all OpenID claim types:
-
normal – Claims directly asserted by the provider.
-
aggregated – Claims sourced from an external provider, made available as a signed JWT.
-
distributed – Claims which can be obtained from the endpoint of an external provider using a supplied bearer access token.
-
verified – Claims for eKYC / Identity Assurance.
-
-
Optional
claims_data
set during consent or by an OAuth 2.0 grant handler to assist construction of the claims set. -
Provides access to the subject session where the claims sourcing was authorised.
-
Enables implementations to release resources on Connect2id server shutdown.
-
Is executed synchronously, in a Java Servlet.
3. Available claims source connectors
Connect2id provides two ready connectors for sourcing claims. Their code is open (Apache 2.0 license), so you can use them as a starting point to devise your own claims sourcing.
3.1 LDAP
An LDAP / Active Directory connector is included in Connect2id server package. Check its configuration manual for details.
Git repo | https://bitbucket.org/connect2id/openid-connect-ldap-claims-source |
---|
3.2 HTTP endpoint
This connector retrieves claims from a protected HTTP endpoint. Check its configuration manual for details.
Git repo | https://bitbucket.org/connect2id/openid-connect-http-claims-source |
---|
4. How to develop your own claims source connector
First, read our general guide for developing, annotating and packaging an SPI-based plugin.
The connector must implement the ClaimsSource or AdvancedClaimsSource SPI defined in the Connect2id server SDK:
Git repo | https://bitbucket.org/connect2id/server-sdk |
---|
The AdvancedClaimsSource
interface exposes additional features, such as
access to optional claims_data
set during the consent.
Explanation of the methods:
-
init(InitContext) – This method is called by the Connect2id server at startup to initialise the connector. The connector can use this method to read a configuration file with database details, create a connection pool, etc. If there is a need to implement a claims caching strategy the context object provides access to the Infinispan instance embedded in the Connect2id server. If there is no need to initialise the connector leave it at the default.
-
isEnabled – For the Connect2id server to check whether the connector is enabled and can be used.
-
supportedClaims – Returns the names of the claims provided by the connector. The Connect2id server uses this information to find out which connector to call when multiple connectors are present.
Support for a pattern of claims can be indicated with the
*
wildcard character, for example ashttps://idp.example.com/*
for a set of claims having a common URI prefix. Returning a single claim set to*
indicates support for all claims supported by the OpenID provider without explicitly listing them. -
getClaims(Subject, Set<String>, List<LangTag>) – Performs the actual claims retrieval for a subject (end-user). Inside the method the connector is supposed to make a database request (SQL query, RESTful call, etc) to retrieve the requested claims. Return
null
if the end-user wasn’t found. If some claims are not available or withheld for some reason they need not be returned. -
shutdown – Called by the Connect2id server on shutdown. Intended to clean up resources, such as database connection pools created by the connector. If there is no need to clean up anything leave it at the default method.
Consult the Connect2id server SDK
JavaDocs for more
information, in particular the
com.nimbusds.openid.connect.provider.spi.claims
package.
If the Connect2id server detects an SPI implementation at startup it will log
its loading under OP7101
or OP7104
:
5. Tips
5.1 Aggregated claims
How to return aggregated claims in a ClaimsSource implementation:
import java.net.*;
import java.util.*;
import com.nimbusds.jwt.*;
import com.nimbusds.oauth2.sdk.id.*;
import com.nimbusds.oauth2.sdk.token.*;
import com.nimbusds.openid.connect.provider.spi.*;
import com.nimbusds.openid.connect.provider.spi.claims.*;
import com.nimbusds.openid.connect.sdk.claims*;
UserInfo userInfo = new UserInfo(subject);
AggregatedClaims ac = new AggregatedClaims(
// the claim names
Set.of("credit_score", "credit_plan"),
// the claim JWT, signed by the claim provider
SignedJWT.parse(jwt)
);
userInfo.addAggregatedClaims(ac);
5.2 Distributed claims
How to return distributed claims in a ClaimsSource implementation:
import java.net.*;
import java.util.*;
import com.nimbusds.oauth2.sdk.id.*;
import com.nimbusds.oauth2.sdk.token.*;
import com.nimbusds.openid.connect.provider.spi.*;
import com.nimbusds.openid.connect.provider.spi.claims.*;
import com.nimbusds.openid.connect.sdk.claims*;
UserInfo userInfo = new UserInfo(subject);
DistributedClaims dc = new DistributedClaims(
// the claim names
Set.of("credit_score", "credit_plan"),
// the claim endpoint
URI.create("https://credit-score-agency.com/claims"),
// the token to retrieve the claims
new BearerAccessToken("Eethahb8ci6Eidii")
);
userInfo.addDistributedClaims(dc);
5.3 Passing the UserInfo access token to upstream claims providers
The claims source may reuse the UserInfo access token to retrieve aggregated and distributed claims from upstream claims providers, if there is an agreement in place to recognise the token upstream. Requires implementation of AdvancedClaimsSource to receive the ClaimsSourceRequestContext:
import java.util.List;
import java.util.Set;
import com.nimbusds.oauth2.sdk.id.Subject;
import com.nimbusds.openid.connect.provider.spi.claims.AdvancedClaimsSource;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimsSourceRequestContext;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
public class MyClaimsSource implements AdvancedClaimsSource {
@Override
public UserInfo getClaims(final Subject subject,
final Set<String> claims,
final List<LangTag> claimsLocales,
final ClaimsSourceRequestContext requestContext)
throws Exception {
// If the claims request was triggered by a UserInfo request the
// submitted and successfully validated token can be retrieved
AccessToken userInfoToken = requestContext.getUserInfoAccessToken();
// Pass the token to upstream providers...
}
}
5.4 Verified claims for eKYC / Identity Assurance
Check the guide for implementing verified claims provision.
5.5 Handling claims requests with language tags
Client applications can request the claims to be returned in a given language or script. This is facilitated by means of language tags (RFC 5646).
For a getClaims
request:
-
Check if the
Set<String> claims
argument contains tagged claim names, e.g.name#de-DE
. Try to fulfill those claims in the requested language / script and return them with the same language tag set.UserInfo userinfo = new UserInfo(sub); userinfo.setName("Alice Adams", langTag);
-
Check if the
List<LangTag> claimsLocales
argument is set and contains language tags. For each language tag in the list try to fulfill the plain (untagged) claims inSet<String> claim
in this language / script. Again, return the claims with the appropriate language tag set.
The Nimbus LangTag library included in the Connect2id server runtime contains helpful methods for dealing with language tags. In particular, check the LangTagUtils. In Connect2id server v10.0+ it adds a split method that splits an optionally language tagged string into a string and language tag pair.
5.6 Accessing optional claims request data
A claims_data
JSON object set during consent can be accessed like this:
import java.util.List;
import java.util.Set;
import org.minidev.json.JSONObject;
import com.nimbusds.oauth2.sdk.id.Subject;
import com.nimbusds.openid.connect.provider.spi.claims.AdvancedClaimsSource;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimsSourceRequestContext;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
public class MyClaimsSource implements AdvancedClaimsSource {
@Override
public UserInfo getClaims(final Subject subject,
final Set<String> claims,
final List<LangTag> claimsLocales,
final ClaimsSourceRequestContext requestContext)
throws Exception {
// The claims data can be obtained from the context object.
JSONObject claimsData = requestContext.getClaimsData();
}
}
5.7 Accessing the subject session
Starting with Connect2id server 14.0 the AdvancedClaimsSource
provides lazy
access to the subject session where the claims
sourcing was authorised.
The session can be used to source selected claims, such as:
-
Claims related to or derived from the user authentication;
-
Claims saved into the
claims
session field during the user authentication / session creation.
The session is supplied in the following cases:
-
Claims sourcing for the UserInfo endpoint where the subject session where the claims consent occurred is still present (not expired or closed);
-
Claims sourcing for ID token issue for an OAuth 2.0 authorisation code, implicit (including OpenID Connect hybrid response type) and refresh token grants;
-
Claims sourcing for a direct authorisation request where a valid subject session ID was supplied, or a new subject session was created.
The claims source can access the session via the ClaimsSourceRequestContext.getSubjectSession method.
import java.util.List;
import java.util.Set;
import com.nimbusds.oauth2.sdk.id.Subject;
import com.nimbusds.openid.connect.provider.spi.claims.AdvancedClaimsSource;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimsSourceRequestContext;
import com.nimbusds.openid.connect.provider.spi.internal.sessionstore.SubjectSession;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
public class MyClaimsSource implements AdvancedClaimsSource {
@Override
public UserInfo getClaims(final Subject subject,
final Set<String> claims,
final List<LangTag> claimsLocales,
final ClaimsSourceRequestContext requestContext)
throws Exception {
// Returns the associated subject (end-user) session where the claims
// sourcing was authorised.
SubjectSession session = requestContext.setSubjectSession();
if (session != null) {
// The sessions is available / present, do something with it
// ...
}
}
}
5.8 How to return an HTTP error status when processing a claims request at the UserInfo endpoint
An exception thrown in the getClaims
method will cause the UserInfo endpoint
to return a 500 Internal Server Error HTTP status.
To cause the Connect2id server to return a different HTTP status code throw a com.nimbusds.oauth2.sdk.GeneralException from the OAuth 2.0 SDK, with an ErrorObject having the desired status code.
Example:
throw new GeneralException(new ErrorObject(
"my_error_code",
"My error message",
444));
This will result in a HTTP response like this:
HTTP/1.1 444
Content-Type: application/json;charset=UTF-8
{
"error" : "my_error_code",
"error_description" : "My error message"
}
5.9 Logging
At startup the Connect2id server logs each claims source
that it detects and then loads. Search for lines with the OP7101
, OP7102
and OP7104
codes:
2017-08-07T13:39:44,634 INFO localhost-startStop-1 MAIN - [OP7101] Loaded claims source [1]: class com.nimbusds.openid.connect.provider.spi.claims.ldap.LDAPClaimsSource
2017-08-07T13:39:44,635 INFO localhost-startStop-1 MAIN - [OP7102] Claims supported by source [1]: sub email_verified address x_employee_number name nickname phone_number_verified phone_number preferred_username given_name family_name email
To aid debugging and monitoring consider adding logging to your claims source, using the Log4j2 library which is included in the Connect2id server WAR package.
5.10 The Connect2id server crashes when loading my claims source connector
If the Connect2id server fails to initialise the claims source for some reason it will abort startup with an exception. If you use Tomcat check the following log files for a record of the exception stack trace:
- /tomcat/logs/c2id-server.log
- /tomcat/logs/catalina.out
- /tomcat/logs/[host]_access_log.YYYY-MM-DD.txt
Does your claims source connector have all dependency requirements satisfied?
There are two approaches to that: merge the dependency classes into the
connector JAR, or adding their JARs to the /WEB-INF/lib
of the c2id
web
application. If a dependency is missing Tomcat will throw a
java.lang.NoClassDefFoundError
.
It’s also good practice to test your connector before deployment, using a framework for automated tests.
5.11 How to monitor your connectors
The aggregate performance of the claims source connectors can be
monitored via the claimsSource.retrievalTimer
metric. This can help you identify latency and other problems that may in turn
affect the overall performance of the IdP service.
Example claimsSource.retrievalTimer
metric:
{
"count" : 6,
"max" : 0.01028953,
"mean" : 6.647419832286938E-4,
"min" : 6.56503E-4,
"p50" : 6.647430000000001E-4,
"p75" : 6.647430000000001E-4,
"p95" : 6.647430000000001E-4,
"p98" : 6.647430000000001E-4,
"p99" : 6.647430000000001E-4,
"p999" : 6.647430000000001E-4,
"stddev" : 9.152683616619665E-8,
"m15_rate" : 0.0016652947966130432,
"m1_rate" : 1.9309212342695557E-4,
"m5_rate" : 0.0015804743529140488,
"mean_rate" : 0.0015484305097827385,
"duration_units" : "seconds",
"rate_units" : "calls/second"
}
Claims retrieval should be relatively swift, particularly if ID tokens are used to transport claims to the client apps (serving claims at the UserInfo endpoint with longer delays is less of an issue). If your claims sources incur long latencies you should consider caching their data. Let us know if you need assistance with that.
5.12 How to disable the LDAP connector
There are two possible ways to do that.
By removing the LDAP connector:
-
Stop the Connect2id server.
-
Remove
oidc-claims-source-ldap-[version].jar
from theWEB-INF/lib/
directory. -
Start the Connect2id server.
By disabling the LDAP connector from its settings:
-
Open
WEB-INF/ldapClaimsSource.properties
-
Set
op.ldapClaimsSource.enable = false
-
Restart the Connect2id server.
-
The Connect2id server will still load the LDAP claims source, but it will not be used.
See the LDAP connector config docs for details.
5.13 When does the claims source get invoked?
The claims source gets called in the following cases:
-
When the UserInfo endpoint processes a request.
-
To feed OpenID claims into an ID token:
- For OpenID authentication requests
with
response_type=id_token
when OpenID claims are requested via thescope
parameter, for example withscope=email profile
. - For OpenID authentication requests with the
claims
parameter set and in it having one or more OpenID claims requested to be included in the issued ID token.
- For OpenID authentication requests
with
-
To feed OpenID claims into an access token. This is triggered by prefixing the name of the consented claim with
access_token:
in consent. -
By SPI plugins which for some internal reason need to call the claims source. Custom token codecs and custom token introspection are able to do this.
The above listed cases apply to Connect2id server v9.0. Newer server versions may invoke the claims source in additional situations. To receive an update for the most recent Connect2id server version contact support.