Is there any reason the existing remote_key and remote_data fields on the modUser class would not suffice for storing tokens and state codes between service requests on a per-user basis?
There may be several OAuth Clients for each user (twitter,facebook,github etc etc) active at one time, so can't use the modUser fields. Also, there needs to be a 'master' connection available to all user (for site-wide connections, twitter feeds etc).
Or modRegistry perhaps?
I don't like the idea of storing auth credentials (tokens/secrets etc) on the filesystem
To clarify my terminology in this situation:
oauthService - A web-based 3rd party service consisting of both
authorization server and
resource server [RFC 6749 section 1.1]
modxService - A php class that can be initialized using modX::getService to provide extended core functionality
As it stands at the moment, I have a functional modxService class to provide connectivity to one or more oauthServices. The schema for data storage is as follows (some fields omitted for brevity):
<?xml version="1.0" encoding="UTF-8"?>
<model package="oauth" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
<object class="OAuthServiceType" table="oauth_clients" extends="xPDOSimpleObject">
<field key="name" dbtype="varchar" precision="100" phptype="string" null="false" />
<field key="path" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="client_id" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="client_secret" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<composite alias="Tokens" class="OAuthToken" local="id" foreign="service" cardinality="many" owner="local" />
</object>
<object class="OAuthToken" table="oauth_tokens" extends="xPDOSimpleObject">
<field key="service" dbtype="int" precision="11" attributes="unsigned" phptype="integer" null="false" />
<field key="user" dbtype="int" precision="11" attributes="unsigned" phptype="integer" null="false" />
<field key="oauth_state" dbtype="varchar" precision="255" phptype="string" null="false" />
<field key="token_granted" dbtype="datetime" phptype="string" null="false" />
<field key="oauth_token" dbtype="varchar" precision="255" phptype="string" null="false" />
<composite alias="User" class="modUser" local="user" foreign="id" cardinality="one" owner="foreign" />
<composite alias="Service" class="OAuthServiceType" local="service" foreign="id" cardinality="one" owner="foreign" />
</object>
</model>
To get an interface to an oauthService, a brief outline of the following code is used:
<?php
$modx->getService('OAuth',$path_to_class);
/** @var string Name of xpdo/OAuthServiceType object to load */
$serviceName = 'GitHib';
/** @var int ID of user to initialize the connection for. 0 is master account. */
$modxUserId = 0;
/**
* Load an instance of a class capable of authorizing & communicating with the oauthService
* @var TheClassTypeInQuestion
*/
$github = $modx->OAuth->load('GitHub');
if($github->authorized){
// We have an auth token, go ahead and query the API
$apiResponse = $github->GET('/path/to/endpoint');
} else {
// No auth token, need to do authorization flow
$authUrl = $github->getAuthorizationUrl();
echo '<a href="'.$authUrl.'">Click here to authorize github</a>';
}
This all works fine, my question more related to the inheritance path for the api classes (dubbed TheClassTypeInQuestion above). It's only really a case of coding style i guess, but was just wondering what others thought. Each instance of the class uses an xpdo OAuthToken object for persisting it's data. This object is stored as a property of the class for access, which means that read/write on the OAuthToken needs to be abstracted in the main class.
The other direction I was considering was to extend TheClassTypeInQuestion from XPDOObject/OAuthToken, putting all the custom functionality directly into the class. This means that an instance can be grabbed directly with modX::getObject. The downside is it requires more work to add a new oauthService interface.
I have a feeling i'm starting to ramble on a bit, so i'll stop typing now. Hopefully someone can distill some sense of what i'm asking... If not i'll wait for the coffee to wear off and try again.