To provide a structured and consistent way of talking with external services, Keyring expects them to be expressed as Services. This means that they all extend the same class (Keyring_Service, found in /service.php). While Services may implement any additional methods and operations they wish, they must implement a base set of functionality to operate with Keyring core. We’ll talk more about that in Writing Your Own Service.

Core Services

Located in the /includes/services/core/ directory, these base classes are actually libraries which should be used as the foundation of more specific Service definitions. They are always loaded before other Services, so that they may be extended safely. Core Services for OAuth1, OAuth2 and HTTP Basic are all provided. Examples of using each may be seen in the Twitter, Facebook and Delicious Extended Services, respectively.

Extended Services

User-facing Services are all referred to as Extended Services since they extend at least Keyring_Service, if not one of the Core Services above (which all extend Keyring_Service themselves). By extending the Core Services, the bulk of the complexity of protocols like OAuth can be avoided, and left up to pre-written code to handle. These Extended Services just define the differences or specific details required for a particular implementation (including labels, URLs/endpoints, verification steps, etc).

Writing Your Own Service

If the service you need to connect to is not included in /includes/services/extended/ then you may need to write your own. Before you start, identify what the authentication protocol is that you need (OAuth etc), if there is a Core Service for that protocol then use it! If the Service you need to connect to uses some custom or exotic protocol which is not covered by the Core Services, then see the section on Writing a Service From Scratch for some additional pointers.

Extending a Core Service

  • Always use an existing Extended Service as a template (hint: there is at least one Extended Service using each of the Core Services bundled with Keyring)
  • If you’re loading your Service as a separate plugin, there’s a load-order problem you need to be aware of. WordPress loads plugin files alphabetically. This means that if your plugin file comes before Keyring’s, then the Keyring classes you need to extend are not loaded yet and you’ll get a fatal PHP error. There are 2 ways around this: name your files something that will ensure they load later (e.g. zz-my-keyring-service.php), or wrap your entire class definition and keyring_load_services hook inside a function definition, and then attach a call to that function on the init action, which should define the class, and at the same time hook into the keyring_load_services action to load and register your Service when appropriate.
  • In the constructor for your custom Service, always call parent::__construct(); as the first thing, to ensure everything is set up correctly, all the way up the inheritance chain.
  • If you’re making a Service intended for distribution, then you should hook into the core-provided admin UI and other components. See existing Extended Services for details how to do that. All of the bundled Services also provide the ability to configure those Services using constants, which you might choose to implement in addition to UI-driven configuration.
  • If your Service requires UI of some sort (to allow the user to enter a username or some other information for example), then you can hook into either the request or verify phases using the keyring_{$service}_request_ui or keyring_{$service}_verify_ui hooks. If you just need a simple username/password combination then have a look at Keyring_Service_HTTP_Basic::request_ui() for an example.

Writing a Service From Scratch

In addition to all of the considerations for Extending a Core Service, here are some tips if you’re starting from scratch:

  • Try to follow one of the other Core Services, along with an implementation of it (e.g. OAuth1 + Twitter) to make sure you’re doing things in an interoperable way.
  • Even if your Service doesn’t have the true concepts of a ‘request’ and ‘verify’ process, you should implement it as if it does. Have a look at HTTP Basic, which doesn’t really work like this, but acts as if it does.
  • Make sure you implement both Request and Access Tokens to ensure compatibility with all Keyring implementations.
  • Always use provided Keyring APIs/methods for interacting with Token Stores and Tokens.
  • Make sure you actually store the Token once it’s verified.
  • In verify_token() be sure to globalize $keyring_request_token to provide for standardized handling for plugins in verified().
  • Along those lines, make sure you call $this->verified( $id, $keyring_request_token ); at the end of verify_token() (once you’ve actually verified the token!)
  • If you need to throw an error, call Keyring::error() with a string, and then return false;
  • request() should use parse_response to parse the response received when the raw_response argument is set to true (which it should default to).
  • Provide a test_connection() method, which might be used to verify that a Token provides a valid connection to a service.

What do you think?