MODX+OAuth2+Zapier: How to Make an Extra with CMP

This is the first in a multi-post series on how to make a MODX Extra, with Custom Manager Page (CMP), based on the making of the OAuth2Server and Zapier Extras.

By YJ Tso
December 14, 2015
MODX+OAuth2+Zapier: How to Make an Extra with CMP

Recently—with the support of the MODX Core Team—I released two MODX Extras: OAuth2Server and Zapier.

The use cases, features, roadmap and documentation for these Extras are in their respective Github repos:

The process of making these Extras was, for me, a crash course in several areas of MODX development, in which I had limited experience and understanding. No one person can know everything about MODX (as even Jason Coward, Chief Architect, will attest). While I'm confident doing many varied things with MODX—to scale—making a CMP is not one of them.

This multi-post series is an attempt to document my learning, both for my own future reference and for anyone else who wishes to learn.

Without these tools, development may have been impossible, slower, or at least less enjoyable:

And, of course MODX and xPDO, as well as my code editor, Coda (although I think a more robust and appropriate IDE like phpStorm would be beneficial).

The primary goal of this whole exercise was to allow us, at MODX, to integrate our contact forms with our tools of choice for CRM, support, analytics, communication, and project management.

I could've developed integrations for each and every tool we use, but our Chief Shiny Objectifier Ryan Thrash has always expounded the virtues of Zapier, leading me to daydream—for the better part of two years—about the day that MODX could connect with it. That day hadn't come, so true to Internet culture, I chose to attempt it myself.

These are pretty big topics—here's a 50,000ft overview.

Your MODX site needs a way to "know", for certain, that a Zapier account (or any other requester) has the permission to access whatever resource it is requesting.

It's the equivalent of you logging into your MODX Manager, except it's some machine in The Cloud doing it. So MODX needs a way to separate "good" machines (that have permission) from "bad" machines (bots, hackers, and whomever else happens across your resource URLs).

Zapier needs to know that you have the permission to grant Zapier permission to your MODX site (Inception, slightly).

Zapier supports multiple ways to do this, but for more reasons than I can list here, OAuth2 has become the "gold standard"—specifically, "3-legged oauth with Authorization Code".

NOTE: for simplicity and brevity, for the rest of this post, "MODX" will refer to your MODX site, to which you want to connect Zapier, and "Zapier" will refer to your Zapier account.

The way it works is:

  1. In MODX, you create a Client ID and Client Secret (think username and password) with which to identify Zapier. Remember this means your Zapier account. You can connect multiple Zapier accounts using multiple Client IDs, but that's not what we're talking about here.

  2. In Zapier, you add a connection to MODX and provide the Client ID and Client Secret so it can "be" that "user". Zapier is "Leg 1", the Consumer.

  3. Zapier forwards you to an "Authorization URL" at MODX. You must be logged into MODX, and your MODX User must have the permission to authorize access. You are "Leg 2", the owner of the resources in MODX.

  4. When you grant permission, MODX sends Zapier an "Authorization Code" which expires in a short time. MODX is "Leg 3", the API or Provider.

  5. Zapier makes a request to MODX, with the Authorization Code, Client ID, and Client Secret, and if all are valid, MODX grants an "Access Token" in the response.

  6. All future requests made by Zapier to MODX will include this Access Token, which will be the sole identifier of Zapier, at the level of access you granted it.

With this, you have fine-grained control over the access to your site. If you remove an Access Token, none of Zapier's requests will be allowed. If you delete or modify the Client ID or Client Secret, Zapier will need to be authorized again, by you, in order to gain a new Access Token. MODX "knows" it's your Zapier account because of the combination of Client ID, Client Secret, and your manual authorization. Zapier "knows" you own the site because you were able to make MODX send an Authorization Code, and it "knows" you allow it continued access as long as MODX accepts the provided Client ID and Secret as valid.

It seems daunting at first, but distilled to its core principles, it makes a lot of sense.

The downside was that MODX didn't support this...

At the bare minimum, 4 things need to persist in MODX for this to work:

  • Client ID
  • Client Secret
  • Authorization Code
  • Access Token

I often make crappy, un-optimized versions of things on the first go-round. My prototype stored Client credentials in MODX System Settings, and the latter two in User Settings. This worked, but you could only have one set of Client credentials, the Authorization Code didn't expire, and Access Tokens couldn't be refreshed automatically, but the idea was valid.

Jason, being prudent and careful in all things, suggested I use a well-established library for the next iteration: oauth2-server-php

Following the cookbook, I manually executed the SQL statements to create the database tables, and included the necessary classes from the library in my Snippets.

It worked, with only a reasonable amount of fussing about. The Postman Chrome extension was invaluable for testing the POST requests.

Interfaces are like run-of-the-mill PHP classes in that they can be extended, and they have member functions, also called "methods". However, interface methods don't do anything. Their internals are defined in the class that "implements" the interface. The interface defines "what" functionality is required, but is completely agnostic to "how" it's accomplished.

From the PHP docs:

The class implementing the interface must use the exact same method signatures as are defined in the interface. Not doing so will result in a fatal error.

It's basically a list of (strictly) required functions. The author of the oauth2-server-php library needed a way to support various storage options, but maintain full control over the functionality that any storage option would provide. The interface design pattern perfectly suits this use case.

At this point I was toying with the idea of writing a class using xPDO methods to implement the library's storage interface. Jason advised me against this—turns out it wasn't needed, but I'm considering it for a future release, just to satisfy my own interest.

Understanding this design pattern was very helpful, for wielding the library effectively. It features a PDO storage class, which plays nicely with MODX. (You just have to pass in an array of table names, which the OAuth2Server "wrapper class" in the Extra will do for you.)

With that working, the next step was to build a package that could be installed via the MODX Extras Installer. This would require another first for me.