Keyring Core

Let’s dive right into /keyring.php, which is the “guts” of Keyring and see what we’ve got.

Configuration Constants

There are a few constants which you can use to alter the way that Keyring works. All of these constants may be defined in your wp-config.php file so that they won’t be affected by plugin upgrades. If you don’t define them then they will default to their own settings:

KEYRING__DEBUG_MODE: define this constant as true to enable very verbose output that will be included in your error_log. You can grep for “Keyring:” which is prepended to all lines output by Keyring. Default is false

KEYRING__TOKEN_STORE: The full PHP class name of the Token Store to use for handling Keyring Tokens. Default is Keyring_SingleStore

KEYRING__HEADLESS_MODE: Depending on your WordPress install, you may or may not want/need users to be able to access the default Keyring admin interface. Defining this constant as true will disable all built in user interface, and require you to implement your own. Default value is false (built in admin UI will be loaded).

Bootstrap + Request Handling

OK, once you’ve got things configured, it’s time to look at what the rest of this file provides. The best way to learn about most of it is to just walk through a typical Keyring-handled request and see what happens:

  • As WordPress is loaded, the plugins_loaded action is fired, which triggers Keyring::plugins_loaded(). Note that this is pretty early in the WordPress bootstrap.
  • /store.php is then loaded, which automatically loads all PHP files in /includes/store/
  • Once all the Store files are loaded, the keyring_load_token_stores action is fired and then KEYRING__TOKEN_STORE is checked to make sure it exists and is a valid Keyring_Store.
  • Now /token.php is loaded, and then /service.php is loaded (which triggers a load of all core + extended services)
  • With everytihng involved in the core of Keyring loaded, 2 things are hooked onto the init action — Keyring’s own Keyring::init() method, and a small closure that just fires the keyring_load_services action.
  • The keyring_load_services action is where all core Services hook in to load, and it’s where you should hook any custom Services in as well.
  • Since Keyring is intended to only operate within the admin area of WordPress, Keyring::request_handlers() are hooked into admin_init (and will conditionally handle Keyring-specific requests)

At this point, we’ve loaded Keyring and all core services, stores, etc. Now we get to actually handling requests.

  • Keyring::request_handlers(), which is hooked into admin_init handles all Keyring requests, provided a series of parameters exist.
  • If an admin request contains the following $_REQUEST parameters, then Keyring will intercept and handle it: page (and it matches the Keyring->admin_page var), action and service. Note that action and service are also restricted to known values.
  • Assuming Keyring is going to handle this request, it will fire a ‘pre’ action (pre_keyring_{$service}_{$action}), then it will verify the kr_nonce parameter to avoid replay and involuntary requests.
  • If the nonce is valid, Keyring will then fire the “main” action — keyring_{$service}_{$action}
  • If this is a delete request (action=delete), then an addition action, keyring_connection_deleted, will be fired for the specified service.

Auth Flow

The general idea of the auth flow in Keyring is modeled most closely on OAuth, where an initial request is made to the remove service, then the user is redirected to the service to grant permissions, then another request is made to get an authenicated token of some sort. Using this as a mental model, everything in Keyring works roughly like this:

  • The user indicates they would like to connect to a service (either directly, or by attempting to use a feature which required a connection)
  • They will be directed to the ‘request’ phase of the process (Phase 1)
  • If KEYRING__HEADLESS_MODE is set to false and something is hooked into the keyring_{$service}_request_ui action, that action will be fired and whatever is attached to it should render a UI to work with this first part of the flow (e.g. for HTTP Basic, this is where we ask the user for a username/password).
  • If a custom request UI doesn’t intercept, then the request_token() method will automatically be called (note that this method must be defined for all Services).
  • Depending on how this particular auth protocol works, the user may be redirected to an external site at this point, or automated requests may be made behind the scenes. Either way, the user will be directed to the next phase, called ‘verify’.
  • The ‘verify’ phase works the same as the ‘request’ phase, calling the keyring_{$service}_verify_ui action if defined, and then the verify_token() method if control has not been short-circuited already.
  • During verify_token(), external requests or internal checks may be made to ensure the token received is valid, has the correct permissions etc.
  • Once verify_token() is complete, it will call verified(), which if not intercepted will just redirect the user back to the Keyring admin with a message telling them that they created a connection.

What do you think?