SSO, Let’s Talk About Single Sign-On (for WordPress and GitLab using Keycloak/RedHat SSO featuring Special Guest Duo Security for 2FA)

This post has 2175 words. Reading it will take approximately 11 minutes.

A couple of years ago, I was doing a survey of single sign-on solutions.  I had outlined this great post about it, modeled after Homer’s Odyssey, where I’d compare some of the SSO solutions on the market to various parts of the Odyssey and it would be quite humorous: Aeolus would be the low-level Spring Security/Spring Social, Circe would be the pretty but deadly WSO2 Identity Server, Scylla would be the many-headed monster OpenAM, Charybdis would be CAS because gosh-darn-it, I’ve picked an analogy and I’m gonna stick with it, until we finally reach the Phaeacians where we finally meet the helpful Keycloak (at the time also known as PicketLink, but now commercialized as RedHat SSO).

I’m sure it would have been quite hilarious with little stabs at how miserable it is to try and configure WSO2 or OpenAM, or how open source and undocumented CAS and Spring Security are.  So hilarious indeed, that the post shall remain forever unwritten because penning it would just ruin the Platonic ideal of the idea.  This post, therefore, is not about that.  Instead, it’s about how I have set up RedHat SSO not only for some of my own projects but also for some of the infrastructure I am using, in particular, GitLab and WordPress.  After getting that running, I’ll also show how I integrated this all with Duo Security for 2FA using their excellent platform

Keycloak

Keycloak is a really well-written piece of software.  It follows RedHat’s philosophy of developing software as open source and periodically make snapshots to sell as commercial products together with support and security fixes; the commercial counter-part to Keycloak is RedHat SSO.  RedHat uses the same model for Wildfly and JBoss EAP and other products.

Keycloak supports many features you would expect from an SSO solution if you had done enough investigation into what an SSO solution would normally provide.  Most importantly, it supports multiple realms each with their own set of clients (such as my GitLab and WordPress installations), identity providers (such as Facebook or StackExchange), optional user federation, and authentication flows.  It is possible to configure users and groups for each realm.  Keycloak supports all the standard identity provider protocols, such as SAML and OpenID Connect (you might know this as OAuth 2.0).

Keycloak also adds standard abilities like viewing (and terminating) active sessions centrally and impersonating users, which is useful for applications that don’t support that out of the box.  It is very friendly with allowing multiple logins on different realms.

What this all means is that I can keep identities in Keycloak and allow users to log in using Facebook, other social services, or e-mail, and my applications do not have to care about any of this.  If a user is logged in to one of my applications, it is trivial to use other applications without logging in again, a bit how you just log in to Google once and are then free to provision a cloud cluster or post a racist comment on Youtube without having to identify you towards Google.

For this, I am using the latest Keycloak 4 beta (beta 2) in anticipation of a final version getting released as soon as Wildfly 13 is.

GitLab

Getting SSO working with GitLab caused me a surprising amount of problems.  It’s just not very well documented, I suppose.  If you Google GitLab and Keycloak, you are likely to end up in discussions about making this work with SAML.  Don’t listen to that garbage.  It is a time sink that can only end with sadness.  Making GitLab work with Keycloak using SAML never worked out for me.

Instead, I ended up configuring my GitLab to use Keycloak in OpenID Connect mode.  I found this forum post after quite a bit of searching.  Other posts contain partial or outdated truths.  Based on this, my Keycloak configuration for GitLab looks like this:

That’s all pretty standard, though I have switched to using confidential access type.  My redirection URLs are:

The only non-standard this is that I have two “official” server names for my GitLab server (most of my services have just a single canonical name with all other names redirecting to the canonical one).  The final minor peculiarity is that I expose all user information to GitLab:

On the other end of the connection, I am using GitLAb in the omnibus configuration, so configuration happens in /etc/ gitlab/ gitlab.rb and requires a reconfigure and restart after changing.

It may be very useful that Keycloak exposes a JSON service which provides information about endpoints and available services if you go to /auth/realms/REALM_NAME/.well-known/ openid-configuration; in my example, this is available at https://sso.brinformatics.eu/auth/realms/brinformatics/.well-known/openid-configuration.  This output provides information about endpoints for the relevant OpenID Connect services, available scopes and exposed information.

My final configuration is:

# OmniAuth
gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_allow_single_sign_on'] = ['oauth2_generic']
gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'oauth2_generic'

gitlab_rails['omniauth_providers'] = [
{
        'name' => 'oauth2_generic',
        'app_id' => 'gitlab',
        'app_secret' => 'KEYCLOAK SECRET GOES HERE',
        'args' => {
        client_options: {
                'site' => 'https://sso.brinformatics.eu', # including port if necessary
                'user_info_url' => '/auth/realms/brinformatics/protocol/openid-connect/userinfo',
                'authorize_url' => '/auth/realms/brinformatics/protocol/openid-connect/auth',
                'token_url' => '/auth/realms/brinformatics/protocol/openid-connect/token',
        },
        user_response_structure: {
        #root_path: ['user'], # i.e. if attributes are returned in JsonAPI format (in a 'user' node nested under a 'data' node)
        attributes: { email:'email', first_name:'given_name', last_name:'family_name', name:'name', nickname:'preferred_username' }, # if the nickname attribute of a user is called 'username'
        id_path: 'preferred_username'
        },
        }
}
]

The app_id (l. 9) and app_secret (l. 10) should match with what you have configured in Keycloak (app_id = Client Id) or Keycloak has generated, steal it from the Installation page:

The configuration of the URLs (ll. 14-17) to connect to is important; the default will not work.  You need to make sure the site_url matches yours and that the realm name in the other URLs is correct.

The response structure (ll. 19-23) is also important but can be copied verbatim. Note that the root_path (l. 20) is commented out; here we use the default.  You can use another attribute for the id_path (l. 22) if you want.

In order to use this, we need to switch on omniauth (l. 2) and configure omniauth to use our newly configured connector (l. 4).  In my case, I want users set up in SSO to have direct access to GitLab; if you don’t want that you need to change line 3 to instead block them and manually take action.

Users in GitLab and connect their account to an SSO provider at will:

I used that to log in to my previously created account and link it to my SSO account during the transition.  After the transition, I then added line 5 from the configuration to have GitLab automatically redirect to the SSO page instead of offering its own sign-in page.  Without that line, users can log in using their accounts without SSO which is useful in a transition phase or if we allow multiple sign-ins and let GitLab perform the user federation.

You can see the results on my GitLab server at https://gitlab.westergaard.eu/; it will just redirect you to the SSO page, where you can create an account should you so desire (either using the Register link or any of the social media identity providers).

My biggest problem with the integration is that you have to pay for the GitLab enterprise edition to be able to configure credentials from the SSO; as it is, I have to configure permissions inside GitLab which to at least some extent defeats the purpose of a central identity service.

WordPress

Getting WordPress to play nice with Keycloak was at the same time harder and easier than getting GitLab working.  We again have a choice between either SAML or OpenID Connect, and have a choice of 4 plug-ins that in principle can make things work for you: MiniOrange’s SAML SSO plug-in, OneLogin’s SAML SSO plug-in, MiniOrange’s OpenID Connect plug-in, and the OpenID Connect Generic Client plug-in.  The MiniOrange offerings genuinely look great – they even have a guide specifically for connecting to Keycloak.

The MiniOrange plug-ins are a bit on the expensive side, though; the SAML plug-in clocks in at $250-500 and the OpenID Connect one at $100-250.  They also have a free version, but it does not have true SSO (it will add a button to the regular login screen instead of redirecting directly to the SSO server).  I would probably have shelled out the money, but I have a couple of sites I want to be protected in the long run, and I’m not going to pay several hundreds of dollars for a site I’m mostly setting up for a short-lived idea.

The OneLogin plug-in relies on a third-party identity service.  Which is probably fine, but I’m not running Keycloak just to have all logins filtered thru another third-party service as well.

The remaining option is the generic OpenID Connect plug-in.  It’s open source and in general, works fine, with one caveat which we’ll get back to.

For WordPress, my Keycloak configuration looks like this:

This is set up in WordPress like this:

We just need to set up the endpoints and double check a few of the formattings (I had to change the Display Name).

What is really important in my case was the Login Type at the top.  Like the GitLab integration, this plug-in can add a button to the existing login form or it can work in full SSO mode (i.e., redirecting users to the SSO page instead of displaying the regular login page).  The user I have used for most of the time while using WordPress has a different user name than my regular user set up in SSO, and the plug-in has not manual link functionality (That I could find), so I have to create a new user by logging out, then logging in using SSO, logging back out and logging in using my regular (administrator) account to assign my new SSO account administrator privileges and assign ownership of all posts to the new account.  Only after this, did I switch my blog to full SSO mode.

After switching to SSO mode, I discovered that my blog has become incompatible with my 2FA plug-in of choice, Duo Security.  I disabled the plug-in and moved on to the next section.

2FA using Duo Security

Keycloak comes with its own support for 2FA.  It is probably fine – I have never actually used it – but I assume it relies on the same 2FA standard everybody else uses, which would force me to scan a QR code using an OTP generator and generate and enter an OTP every time I wanted to log in.  Like some sort of caveman.

I have relied on the 2FA solution from Duo for a long time.  It’s truly great.  I have used it for my blog and for protecting ssh logins to my servers.  What is so special about Duo is that in addition to the regular 2FA options (OTP using SMS, call or generator), they also support just sending a push notification to my phone or watch; I just click approve and I’m immediately logged in.  So much less effort than having to find the right generator and typing in the password or waiting for a text message to arrive like we all still lived in the 90s and logged on to Geocities page suspiciously still under construction.

In addition, the 2FA of GitLab is atrocious.  It allows you to use a standard OTP generator, but that means you can no longer authenticate http checkouts using your password, but instead have to generate application-specific passwords for each place you want to check out, which is no fun at all.

All in all, I wanted a simple 2FA based on Duo in my SSO.  It turned out, that somebody recently actually made just that.  The installation instructions are spot-on and the only small aberdabei is that you have to build the DuoWeb integration yourself because it’s apparently not available in any Maven repositories.  Having done that, I just compiled and copied the artifacts in according to the instructions.  I then modified my Browser flow as instructed:

I switched the default browser flow for my realm to the new one:

Like something straight out an Apple keynote, that was it: it just worked.  Now, I can log in to my sites using my preferred 2FA solution.  I switched off 2FA in WordPress and GitLab which means that SSO works in WordPress and I don’t have to bother with application-specific passwords in GitLab if I don’t want to.

Conclusion and Wish List

In conclusion, using Keycloak worked really well.  I have used it for some of my hobby development projects as well, and it is very easy to work with there.

I am missing a bit more centralized configuration of user roles; while Keycloak has support for it, GitLab has hidden it behind a pay-wall and my free WordPress plug-in doesn’t seem to support this (the MiniOrange ones seem to, though).

I would also like to protect some of my other services using the SSO, but Artifactory hides SSO behind a paywall as well, and I will have to look into how to make SSH authenticate using an SSO solution.  I have already migrated some of my hobby projects to Keycloak (that’s why I set it up in the first place), but my mobile site is still using my own old home-brew PHP solution from more than 10 years ago. 

Time person of the year 2006, Nobel Peace Prize winner 2012.

One thought on “SSO, Let’s Talk About Single Sign-On (for WordPress and GitLab using Keycloak/RedHat SSO featuring Special Guest Duo Security for 2FA)

  1. Hello

    We want to do SSO for the application deployed on Wildfly Instance and we want to use the SAML standard and keycloak .

    We don’t have website name yet . We have the host number and port and application we deployed is jenkins.war file on our wildfky instance . For more information please email me.

    Our app url is like this
    http://host no:port no/jenkins

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.