I spent way more time than I should have yesterday trying to figure out how to authenticate with Phabricator’s conduit service. The goal was pretty simple: turn a Phabricator object key (ie. D1337) into a link with the name as the text. Since I was trying to integrate with hubot running on a heroku instance, I didn’t want to use Arcanist to accomplish this.
As it turns out, how to authenticate with conduit given a user and a certificate is not documented anywhere, so I’m writing it down here for all you fine folks.
Getting a Session Key
The first thing you need to do to be able to make calls to conduit is to connect to conduit. I’ll be using https://secure.phabricator.com/ as the canonical Phabricator installation, but you should replace that url with the url to your Phabricator install wherever you see it.
Prerequisites
- An arc user
- A certificate (can be found at /settings/panel/conduit/)
Making the call
The interesting and undocumented parts of this are the authToken and
authSignature bits.
- Make a JSON formatted
paramsproperty. It should contain:client- A string naming the client. I used “hubot”.clientVersion- Version information about your clientclientDescription- An optional description of your client.user- The user you have the certificate forhost- In this example, https://secure.phabricator.com. This must be the exact HTTP address of your phabricator install. It’s<scheme>://<host>with no path.authToken- The UTC time in seconds. I got stuck on this part as JavaScript’sDate.now()returns the time in milliseconds, and that took a while to realize.authSignature- Concatenate theauthTokenwith the certificate for the user, then take the sha1 hash of that value. TheauthSignatureis the hex digest of that sha1 hash.
- Make an HTTP POST to https://secure.phabricator.com/api/conduit.connect
- Pass in the JSON formatted
paramsobject asparams - Pass in
outputas “json” - Pass in
__conduit__as true
- Pass in the JSON formatted
- Parse the result as a JSON object
- If the
error_codeproperty is not null, an error occurred. Details can be found in theerror_infoproperty. - If the call succeeded, you’ll want to save the values of the
sessionKeyproperty and theconnectionIdproperty. You’ll need these in all subsequent calls.
- If the
Making a Call to Conduit
After you have a session key, it’s pretty straightforward to make a call.
- Look up the call you want to make at https://secure.phabricator.com/conduit/
- In each of your JSON formatted params objects, you need to add a property
called
__conduit__. This property is an object with two properties,sessionKeyandconnectionId. Their values are the values from the conduit.connect call described above.
Example
Here’s an example in Python, using the outstanding requests library.
import hashlib
import json
import requests
import time
# Constants to be filled in by the reader
CERT = 'Your cert here'
USER = 'Your user here'
PHAB = 'Your phabricator URL here'
# Format parameters for conduit.connect
token = int(time.time())
signature = hashlib.sha1(str(token) + CERT).hexdigest()
connect_params = {
'client': 'Python demo',
'clientVersion': 0,
'clientDescription': 'A script for demonstrating conduit',
'user': USER,
'host': PHAB,
'authToken': token,
'authSignature': signature,
}
# Make the request to conduit.connect
req = requests.post('%s/api/conduit.connect' % PHAB, data={
'params': json.dumps(connect_params),
'output': 'json',
'__conduit__': True,
})
# Parse out the response (error handling ommitted)
result = json.loads(req.content)['result']
conduit = {
'sessionKey': result['sessionKey'],
'connectionID': result['connectionID'],
}
# Make the call to phid.lookup
req = requests.post('%s/api/phid.lookup' % PHAB, data={
'params': json.dumps({
'names': ['D1337'],
'__conduit__': conduit,
}),
'output': 'json',
})
result = json.loads(req.content)['result']
print result['D1337']['fullName']
Pretty simple once it’s documented :).