Mixed HTTP / WordPress authentication with nginx

Our WordPress blog / site network at wp.sanbi.ac.za uses Daniel Westermann-Clark’s HTTP Authentication plugin. Despite the scary warning on the plugin’s page over at wordpress.org, the setup has worked well for us over the years. The only problem has been allowing external (non-SANBI) users to edit pages. This is something we really need for the annual bioinformatics course website.

After quite a few false starts I realised that the solution is to use passive authentication. I.e. do not force the user to use HTTP authentication but rather make it an optional extra. We use PAM for nginx authentication (via nginx_auth_pam, included in the nginx build in Ubuntu’s nginx-full module) and the crucial bit of the nginx config looks like this:

        location /sanbi-login.html {
            auth_pam  "SANBI authentication";
            auth_pam_service_name "nginx";
        # Process only the requests to wp-login and wp-admin
        location ~ /wp-(admin|login|includes|content) {
          try_files $uri $uri/ \1/index.php?args;

          location ~ \.php$ {
             try_files $uri =404;
             include fastcgi_params;
             fastcgi_param REMOTE_USER $remote_user;
             fastcgi_index index.php;
             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
             fastcgi_pass  php;
             fastcgi_intercept_errors on;

(Full nginx config is here). The nginx server forwards the REMOTE_USER variable to the php5-fpm backend but doesn’t actually force HTTP authentication for any of the WordPress content. Instead only a single page is protected with HTTP authentication, and that page simply redirects to the WordPress admin interface. Here’s the source of sanbi-login.html:

<title>SANBI login</title>
<meta http-equiv="refresh" content="0; url=/wp-admin" />

This works well because ours is a sub-domain based WordPress network. In the HTTP Authentication plugin settings, we point to the address of the sanbi-login.html page as the login page, so the login page ends up looking like this:

WP login screen.

The Log in with HTTP Authentication takes you to the sanbi-login.html page that requests HTTP Authentication and immediately redirects back to /wp-admin. The login form uses conventional WordPress authentication.

One remaining challenge is how to set this option for all sites in the network. Currently it needs to be configured for each site individually. The setting is stored in the wordpress database in the wp_BLOGNUMBER_options table (where BLOGNUMBER is the number of the blog in the wordpress network) with option_name = ‘http_authentication_options’, as part of a serialised array. This code suggests how to set an option for all sites in a network but as yet there is no way to do this from the wordpress web interface, and I’m a bit loath to work on the database directly.