Keycloak is an open-source identity and access management solution. Some of the features that it provides out-of-the-box are the easy deployment of application authentication, single-sign on, identity brokering from various sources like social media applications, user federation by creating a facade for LDAP, Active Directory, etc., and all of this using standard protocols like SAML, OpenID Connect (OIDC), etc.
CanDIG uses Keycloak (one deployment on each site in a project), to create a uniform interface for authentication and authorization infrastructure (AAI). This helps alleviate the issue of supporting a variety of authentication tools and protocols. This uniformity allows CanDIG instances in a project to ask for authentication and authorization for an incoming request, from the appropriate site much more easily.
CanDIG uses OIDC and its related tokens to achieve authentication and authorization in a distributed manner. This is a critical use case for CanDIG. CanDIG’s AAI implementation extends OIDC by using the Global Alliance for Genomic Health (GA4GH). GA4GH defines the claims carried within the OIDC token (renamed Passport in the GA4GH spec) that help with upstream authorization. Any claim in the token (which is a JSON Web Token) is merely a key-value pair within its payload section.
While the OIDC allows the creation of custom claims (the key-value pairs) in the tokens, it does not impose any limit on the size of the fields. To make the claims and their values self-sufficient, the GA4GH standard decided to add relevant details like cryptographic keys, etc., which make the claim values large. At the same time, the claim value can also be a list of items. This makes them even larger, depending on how many items are on the list.
As noted in the last section, for CanDIG to work as a distributed and federated system, it needs to have a facility to check for authorization. The core of that is checking for these Passports and claims within them. This Passport comes from an OIDC defined endpoint called “userinfo”.
In Keycloak, the said userinfo
endpoint was not working. When a user goes through logging in via their site in a CanDIG federation project to access a resource on another site, this userinfo
endpoint was not being called by the Keycloak on the foreign site to fetch the details of the user. As a result, the authorization information was missing and no access was given even though this user may have had valid access.
The screenshot above shows that even if the toggle to Disable User Info is off, the userinfo
endpoint was still never called.
The line that is responsible for this issue is as follows -
if (userInfoUrl != null && !userInfoUrl.isEmpty() && (id == null || name == null || preferredUsername == null || email == null)) {
and the fixed version is -
if (userInfoUrl != null && !userInfoUrl.isEmpty()) {
Deleting the unneeded conditional checks was the only thing required to fix this.
Setting up to a point where we can finally test the changes, add tests, confirm nothing else breaks, and contribute the PR, all of this was the cumbersome part. This fix needed to be manually checked first and that required setting up two Keycloak servers with the right configuration.
One detail in this entire story is that fetching userinfo
internally Keycloak has a feature called Mappers which helps in mapping or converting the incoming external claims to internal user attributes. This is important to pass the incoming claim to the user’s session so that the local application can use that user attributes to make the decisions.
When using Keycloak’s user interface, it is easy to check these mappers, change them, and set them. However, when you have to write unit tests, you need to find a way to do that programmatically and figure out which class to extend. In this case, the main two classes were -
AbstractRoleMapperTest
: for the test itselfKcOidcBrokerConfiguration
: for creating a broker to be used in the testOnce you figure out the correct classes to extend, your test writing is much more smooth. Writing tests gives you confidence and makes the maintainers’ lives easier. It is to be noted that you may not have to write a ton of tests. The original PR had ten (10) tests but the maintainers suggested only tests were sufficient because they likely cover the cases that need focus.
You can see the pull request - KEYCLOAK-14039 - UserInfo claims from external OIDC identity provider are not imported #7214.
Every open-source project has its peculiarities and it may seem daunting at first.