Implementing Keyring in Your Plugin/Theme

The reason for Keyring’s existence is to make it easier for developers to work with services which require authentication of some sort. Here’s a quick primer on how to do that.

Core Methods

There are a few core methods (contained in the Keyring class, in /keyring.php) which you are most likely to interact with:

  • Keyring::init() should be used if you’d like to access the global instance of Keyring, assign it to a variable, or whatever. You should never call new Keyring();
  • Keyring::get_registered_services() will give you an array containing all of Keyring’s currently registered Services. The array is keyed off the slug/name of the service, and the elements are usable instances of the class for a Service.
  • Keyring::get_service_by_name( $name ) returns a single Service, based on its slug/name.
  • Keyring::get_token_store() will return a reference to the Token Store in use by Keyring. If required, it will initiate the Token Store first.

Keyring Utils

In addition to the core methods above, there are some utility methods provided, within the Keyring_Util class.

  • Keyring_Util::debug( $string, $level ) will output a string to the debug log. The default $level is KEYRING__DEBUG_NOTICE which will just output into the error log. If you pass KEYRING__DEBUG_WARN then it will also echo the message to the user, and KEYRING__DEBUG_ERROR will force a wp_die() and output the message to the user.
  • Keyring_Util::is_service( $object ) checks an object to see if it appears to be a Keyring_Service.
  • Keyring_Util::has_custom_ui( $service, $action ) just checks to see if a Service has registered a custom UI for a specific action, based on the specific Keyring action hook.
  • Keyring_Util::admin_url( $service, $params ) generates a URL for use within Keyring, based on the admin UI being enabled. Note that this does not automatically add the kr_nonce parameter required for most internal links, but that can be passed in via the $params array.
  • Keyring_Util::connect_to( $service, $for ) starts the connection process for the specified $service and flags the connection so that once complete, you can grab it based on the $for value, which can be retrieved from the meta of $keyring_request_token after a Token is verified.
  • Keyring_Util::token_select_box( $tokens, $name, $create ) creates a simple HTML select box which allows a user to pick from a list of existing Tokens. It uses the get_display() method of each Token to render something for the user to see, and the unique id of the Token as the value. If you set $create to true then an additional option will be added to the top of the list, allowing the user to create a new connection (which will pass the string new as the value when submitted).

Interfacing with Keyring/Getting a Connection

If you’re just using an existing Service, and just need a connection, then you should be able to interact with Keyring pretty easily. You can do things like:

Check if a user is connected to a service already
$service->is_connected();

Load an existing Token when you know its unique id
Keyring::get_token_store()->get_token( array( 'service' => $service_name, 'id' => $unique_id ) );

Use a Token to make authenticated requests against a Service

$service = Keyring::get_service_by_name( $service );
$service->set_token( $token );
print_r( $service->request( $url, $params ) );

Extending Keyring Services

One way to build a plugin which uses Keyring is to extend the Keyring Service classes directly. This can make things easier to work with (less objects to juggle), you just need to make sure you don’t overwrite/redefine any of the core methods and properties in your plugin.

To do this, you’d want to make sure that your plugin loads after Keyring is already loaded, and then you’d do something like class MyPlugin_Facebook extends Keyring_Service_Facebook. At this point, your inheritance tree would be Keyring_Service > Keyring_Service_OAuth1 > Keyring_Service_OAuth2 > Keyring_Service_Facebook > MyPlugin_Facebook. Depending on the architecture and complexity of your plugin, this might be a good option.

Customizing Tokens

Mainly through the use of filters, you can manipulate what is stored for Tokens, and especially what meta data is stored along with them. This can be very handy in cases where you need some global information stored with Tokens, or where you want to pass data through the auth flow to maintain some sort of state. You can also use metadata in Tokens to hint to your custom Token Store what to do with a Token, how to store it, etc.

The filters you want to look at for modifying Tokens are below, see the Actions, Filters and Constants Index for more information about them.

keyring_request_token_meta
keyring_request_token
keyring_{$service}_request_token_params
keyring_{$service}_verify_token_params
keyring_access_token
keyring_access_token_meta

What do you think?

  1. Could you provide a one page example with a connection to say Facebook ( assuming the user has authorized Facebook via the admin already )

    The examples above are somewhat confusing if one isn’t sure where to get $service or $unique_id from.

      • Hmm again, that code doesn’t really illuminate much,


        $service = \Keyring_Service_Facebook::init(); // good

        $service->get_tokens() // cool, gives me an array

        $service->get_token() // no dice, and jezz that example code doesn’t show a clear path here.

        • So this seems to work, but the call to set token seems a bit clunky.

          $service = Keyring_Service_Facebook::init();

          echo $service->is_connected() ? ‘true’ : ‘false’;

          $token = current($service->get_tokens());

          $service->set_token( $token );

          $res = $service->request(‘https://graph.facebook.com/me?fields=id,name’);

          print_r($res);

          • Beau said:

            That’s expected/correct; setting the token like that is definitely something you’ll need to do before using it to make authenticated calls.

  2. konamiman said:

    Here’s a quick reference on how to have Keyring register a service defined in another plugin, since it’s not immediate from the documentation:

    1. Define your service in a keyring-service-foobar.php file in your plugin directory as follows:

    class Keyring_Service_FooBar extends Keyring_Service {

    }

    add_action(‘keyring_load_services’, array(‘Keyring_Service_FooBar’, ‘init’));

    2. Add the following to your plugin file:

    function register_foobar_service($keyring_services) {
    array_push($keyring_services, __DIR__ . ‘/keyring-service-foobar.php’);
    return $keyring_services;
    }
    add_filter(‘keyring_services’, ‘register_foobar_service’);

    I hope that’s useful.