Using WordPress: Keyring

WordPress: Keyring is actually a fine piece of software.

What does it do? As the documentation says, Keyring is

… a generalized framework for WordPress which handles authentication with, and authenticated requests to remote services.

The way it works is very convenient: you install the plugin, configure it in the admin panel, and finally issue requests to services without worrying about authentication, be it HTTP basic auth, OAuth, or whatever.

Unfortunately, documentation is a bit missing, and tells just the bare minimum to get started, requiring then a bit of tinkering to get further on.

So, here I am putting what I have learned using it, using GMail as an example for a service.

Setup

The very first step is to setup the thing.

  1. After installing the plugin, there should be a new menu in the admin dashboard, Tools | Keyring.
  2. From the list of services to configure, select “Google Mail”, following the steps from the plugin (but beware the url – see sidebar).
  3. Then, create a new connection. This will open up the classical OAuth authentication mechanism, and from there select the account that should be used for the integration.

Usage

The usage is very simple:

  1. Instantiate a service object
  2. Get a token for that service, and set it to the service object
  3. Use the service object to make authenticated requests

As an example, we can see:

$gmail = Keyring::get_service_by_name( 'google-mail' );
assert( $gmail->is_connected() );
$token = current( $gmail->get_tokens() );
$gmail->set_token( $token );
$all_messages = $gmail->request( "https://www.googleapis.com/gmail/v1/users/me/messages", [] );

Why current( $gmail->get_tokens() )? In this case, there is only one token; hence I am feeding it to the service.
However, if there would have been more than one, the situation would have been more complex, requiring to choose the one to use; I have not investigated that use case, so I am not describing it here.

The request mimics the interface of wp_remote_request, and accepts two parameters: the url, and an array of arguments.
Since the function eventually delegate the call to WP_Http::request(), most of the parameters in that array can be found in the documentation for that function.
The most common one, I believe, is method, to specify anything different from the default GET.
It is in particular very important for sending json payload to a POST request:

$payload = ["key" => "value"];
$args = [
  'method'  => 'POST',
  'body'    => json_encode($payload),
  'headers' => ['Content-Type' => 'application/json'],
  'data_format' => 'body',
];

The last item (data_format) is not mandatory, as it should be already correctly set in that way when the request is not GET or HEAD; but it is very important to set the Content-Type header and to ensure that the body is a well-formed json string.
Note: this is true in general for wp_remote_request.

What to do in case you need another service

Sometimes, the existing services are not enough. For instance, the gmail service has a readonly scope, which is not suitable for operations like update or sending mails.
Fortunately, it’s quite easy to extend services:

  • Create a new service class, extending the existing one
  • Tweak the parameters (in this case, the scope), and update name and label
  • Remember to call parent::__construct();
  • Register the new service class by calling its ::init() static method from the keyring_load_services hook.

Since the plugin class might not have been loaded yet when loading the file for our plugin, it might be useful to define the new service class inside a callback, like in this example:

function load_personalized_google_mail() {
    class Personalized_Service_GoogleMail extends Keyring_Service_GoogleMail {
        const NAME        = 'google-mail-personalized';
        const LABEL       = 'Google Mail (with modify scope)';
        const SCOPE       = 'https://www.googleapis.com/auth/gmail.modify';

        function __construct() {
            parent::__construct();
        }
    }

    Personalized_Service_GoogleMail::init();
}
add_action( 'keyring_load_services', 'load_personalized_google_mail' );

The plugin will tell to use the “external” address of the machine as authorized origin and redirect URIs.
This is correct, when working on a production environment, but on a development environment, like a laptop, it is way an overkill.
The reason is that Google (like most if not all OAuth providers) require an https schema for those locations, and a public name. This means playing with SSL in WP – which might be daunting on a local installation – and then setting up ngrok or even or forwarding ports.

However, there is a much easier approach: all the authentication process is driven by the client, and especially the redirection. This means that it is possible to use localhost, without having to get a public name in a DNS; furthermore and Google (and most OAuth providers) will also exceptionally accept an http schema when the location is localhost, removing the need to setup https in WP.

Note that it is always possible to setup multiple origin and redirect URIs; in this way, it is possible to set both development and production environment configured at the same time.