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
params
property. 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 theauthToken
with the certificate for the user, then take the sha1 hash of that value. TheauthSignature
is the hex digest of that sha1 hash.
- Make an HTTP POST to https://secure.phabricator.com/api/conduit.connect
- Pass in the JSON formatted
params
object asparams
- Pass in
output
as “json” - Pass in
__conduit__
as true
- Pass in the JSON formatted
- Parse the result as a JSON object
- If the
error_code
property is not null, an error occurred. Details can be found in theerror_info
property. - If the call succeeded, you’ll want to save the values of the
sessionKey
property and theconnectionId
property. 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,sessionKey
andconnectionId
. 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 :).