/ 2024-04-26-kanidm-and-mediawiki
back to the top

Setting up SSO for MediaWiki with OIDC and kanidm

As part of a larger project of setting up a single-sign-on solution, I have been experimenting with integrating kanidm as an identity provider with MediaWiki. In this post I’ll document a few of the hurdles I came across along the way, and how they can be overcome.

Gathering components

For my proof-of-concept, I’ve set up MediaWiki using the official docker image available on dockerhub. I used the OpenID Connect extension to outsource authentication to kandim using OIDC,. This extension builds on top of the PluggableAuth extension. I’ve previously installed kanidm and will not detail that here — for this post, assume the installation is hosted at idm.joostrijneveld.nl. The MediaWiki installation will be set up at wiki.joostrijneveld.nl.

Installing extensions

I’m following the installation instructions as specified on the extensions’ wiki pages. As I’m running MediaWiki in a docker container, I cannot simply git clone the extensions into the respective folders. Instead, I create a new Dockerfile to perform the appropriate git clone commands.

The installation instruction tells us to modify LocalSettings.php; we can preserve that file outside the container and mount it on startup. Using docker cp, we extract the default settings and go from there. We add the required wfLoadExtension commands.

The OpenID Connect extension requires a modification to the composer.json file to add in its own dependencies. As we’ll have to run composer update after modifying the file, we’ll do this as part of the Dockerfile. Again, we obtain the initial composer.json by copying it from the stock docker image. Composer depends on unzip or 7z, so we’ll also bundle that in.

The resulting Dockerfile looks as follows:

FROM mediawiki:1.39
RUN apt-get update
RUN apt-get install unzip zip
RUN git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/PluggableAuth /var/www/html/extensions/PluggableAuth
RUN git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/OpenIDConnect /var/www/html/extensions/OpenIDConnect
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
COPY composer.json /var/www/html/composer.json
RUN php composer.phar update

I’m using docker-compose to link it all together; it also contains an nginx configuration to handle TLS.

services:
  # ...
  mediawiki:
    build: ./mediawiki
    restart: always
    volumes:
      - ./mediawiki/LocalSettings.php:/var/www/html/LocalSettings.php
      - wikidatabase:/var/www/data
      - wikiimages:/var/www/html/images
# ...
volumes:
  wikidatabase:
  wikiimages:

Viewing verbose errors

Throughout the setup process I ran into several errors. Having those errors show up in the log file (or, in my case, docker logs) is crucial. We use the logging recipe as specified on the MediaWiki-Docker wiki, copying the following snippet to our LocalSettings.php:

if ( !defined( 'STDERR' ) ) {
	define( 'STDERR', fopen( 'php://stderr', 'w' ) );
}

if ( !isset( $maintClass ) || ( isset( $maintClass ) && $maintClass !== 'PHPUnitMaintClass' ) ) {
	$wgMWLoggerDefaultSpi = [
		'class' => \MediaWiki\Logger\ConsoleSpi::class,
	];
}

Language settings

The default MediaWiki setup specifies en-gb as the language code, but it appears that either the PluggableAuth or OIDC extension pull in a dependency that does not support that. This seems to trigger the following error:

[debug] [error-json] {"id":"b3dc4385d11ecd2d5269cbbb","type":"ErrorException","file":"/var/www/html/includes/language/dependency/FileDependency.php","line":76,"message":"PHP Warning: filemtime(): stat failed for /var/www/html/includes/Rest/i18n/en-gb.json","code":0,"url":"/opensearch_desc.php","caught_by":"mwe_handler","suppressed":true,"backtrace":[ ... ]]}

.. consider changing the line ..

$wgLanguageCode = "en-gb";

.. in LocalSettings.php to ..

$wgLanguageCode = "en";

Configuring kanidm

On the kanidm side, we create client integrations using the process described on the OAuth documentation page. We set up the client as follows:

kanidm system oauth2 create mediawiki "Mediawiki" https://wiki.joostrijneveld.nl

We then add the appropriate scopemaps:

kanidm system oauth2 update-scope-map mediawiki idm_all_persons openid email profile

And we view the client secret, so that we can include it in the PluggableAuth configuration:

kanidm system oauth2 show-basic-secret mediawiki

Degrading security

You may run into the following error in the kanidm logs:

ERROR       │  ┕━ 🚨 [error]: No PKCE code challenge was provided with client in enforced PKCE mode. | event_tag_id: 12 | o2rs.name: "mediawiki"
ERROR       ┝━ 🚨 [error]: Unable to authorise - Error ID: 959b3246-4c82-4c4f-b02d-ef2f1bf33754 error: invalid_request | event_tag_id: 1

As the library underlying the OIDC plugin does not support PKCE, we disable this as follows. Assuming we can keep the client secret a secret, this should not be an urgent problem.

kanidm system oauth2 warning-insecure-client-disable-pkce mediawiki

Similarly, you may run into the following error on the MediaWiki side:

[debug] [OpenIDConnect] Jumbojett\OpenIDConnectClientException: No support for signature type: ES256 in /var/www/html/vendor/jumbojett/openid-connect-php/src/OpenIDConnectClient.php:1257

As the error specifies, the OIDC extension does not support ES256, which makes use of elliptic curve signatures. Instead, we can rely on RSA signatures as part of RS256. Kanidm requires us to explicitly allow the use of legacy crypto, which can be done as follows:

kanidm system oauth2 warning-enable-legacy-crypto mediawiki

Refer for more details to the kanidm documentation on options for legacy clients.

‘Authorization endpoint could not be fetched’

If you have not specified the authorization endpoint correctly, you will run into the following error.

- [debug] [OpenIDConnect] Jumbojett\OpenIDConnectClientException: The provider authorization_endpoint could not be fetched. Make sure your provider has a well known configuration available. in /var/www/html/vendor/jumbojett/openid-connect-php/src/OpenIDConnectClient.php:675

In my case (with a client named ‘mediawiki’), the appropriate provider URL is:

    'providerURL' => 'https://idm.joostrijneveld.nl/oauth2/openid/mediawiki',

Setting up account creation rights

The installation instructions for the PluggableAuth extension specify that ‘createaccount’ or ‘autocreateaccount’ rights must be granted to all users. Indeed, failing to set this configuration will lead to the following error:

Auto-creation of a local account failed: Automatic account creation is not allowed.

To configure these rights, we add the following line to the LocalSettings.php file.

$wgGroupPermissions['*']['autocreateaccount'] = true;

Usernames include IDM URL

At this point you should be able to log into the MediaWiki installation using accounts that can be authenticated using kanidm. Upon logging in, a MediaWiki account is created. The URL of kanidm is included in the username, creating usernames such as joost@idm.joostrijneveld.nl.

While that’s conveniently unique, it’s not very pretty. As all our users are authenticating through kanidm using hte OpenIDConnect extension, we won’t need to disambiguate. Instead, we can inherit the username from kanidm by specifying the preffered_username option in the PluggableAuth config.

'preferred_username' => 'name'

Note that kanidm allows users to change their usernames. While this will not break the sign-in process, these changes are not reflected in the MediaWiki accounts.

Configuration overview

After taking the above into account, the relevant configuration changes are:

$wgGroupPermissions['*']['autocreateaccount'] = true;
wfLoadExtension( 'PluggableAuth' );
wfLoadExtension( 'OpenIDConnect' );

$wgPluggableAuth_Config = [
        [
                'plugin' => 'OpenIDConnect',
                'data' => [
                        'providerURL' => 'https://idm.joostrijneveld.nl/oauth2/openid/mediawiki/',
                        'clientID' => 'mediawiki',
                        'clientsecret' => '...',
                        'preferred_username' => 'name'
                ]
        ]
];