* Update: Modified and slightly revised version of this post is available on the LabKey Blog in two parts. See Part1 and Part2

As you may or may not know, the LabKey Server has a whole suite of client APIs that can be used to programmatically access data inside your LabKey Server. Like any other open source project, some of these APIs have seen a lot more development than others (see the functionality available in the Javascript API, which is one of the first we developed; compared to our newest API, Python which was written last month by Elizabeth in her spare time).

Periodically, I will need to access data in a LabKey Server in an unsupported language(for example NODE.js or BASH). In these cases, one of the first hurdles that you need to get over is simply authenticating to the server. In this entry, I am going to provide some sample code for how to authenticate to the LabKey Server in a variety of languages (shell script, python and node.js).

How Does Authentication in the LabKey Server work?

LabKey Server uses form-based authentication, by default, for all user-agents (ie browsers). However, it will correctly accept HTTP basic authentication headers if presented. I recommend using Basic Auth when programmatically accessing the server.

Please note that when using either form-based authentication or basic authentication, your credentials are passed in clear text, thus I strongly recommend implementing SSL on your LabKey Server.

Here is a very simplistic example of how Basic Auth works:

  1. You attempt to connect to a web page which requires authentication
  2. The server will respond with an Authorization Required error message (HTTP response code = 401).
  3. In addition, the server will add a new HTTP header to the response. It will be similar toWWW-Authenticate: Basic realm="Default"
  4. If you are using a browser, the browser will then pop-up a dialog box asking you to enter a username and password to gain access.

This is how the LabKey Server works for all the APIs, which are designed to be accessed programmatically.

For all other pages(where the LabKey Server assumes you will accessing via a browser) the LabKey Server will not behave in the way described above but will behave completely different. Here is an example:

  1. You attempt to connect to a web page which requires authentication
  2. The server will respond with an Moved Temporarily error message (HTTP response code = 302).
    • The server does not include the WWW-Authenticate:... header in the response headers
  3. If you are using a browser, the browser be redirected to the LabKey Server login page.
  4. If you are not using a browser, but accessing the server programmatically you simply get this error message

In general, the actions which respond with the WWW-Authenticate:... HTTP Header are

  • All API requests (any page whose names ends with .api)
    • Both POST and GET

All other pages do not respond with the WWW-Authenticate:... HTTP Header, but redirect you to the LabKey Server login page. This includes actions such as

  • Most Admin actions (creating new folders, viewing the Audit Log)
  • Submitting MS2 searches to the server
  • Viewing and managing Wikis
  • Viewing or posting messages to a Message board or Issue tracker.

This is an important point. When accessing the LabKey Server programmatically tools (wget, curl, etc) or programming libraries which handle Basic Auth authentication will work properly when accessing an API, but will not work when accessing any other page on the server.

A very easy way to determine the authentication behavior of page is to make an unauthenticated connection to the web page and review the response HTTP Headers. You can do this using python

{% highlight python %} import urllib2 try: f = urllib2.urlopen(‘http://my.labkey.server/labkey/your/page’) print f.info() except urllib2.HTTPError, e: print e.info()\

{% endhighlight %}

or using wget

wget --server-response http://my.labkey.server/labkey/your/page

Let me use an example to illustrate the behavior I explained above. Lets use wget to view a secured wiki page on the server

wget --server-response --max-redirects=0 https://www.labkey.org/wiki/Home/page.view?name=secureWiki

the response is

HTTP request sent, awaiting response... 
  HTTP/1.1 302 Moved Temporarily
  Server: Apache-Coyote/1.1
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  Cache-Control: no-cache
  Location: /login/Internal/login.view?returnUrl=%wiki%2FHome%page.view%3Fname=secureWiki
  Content-Length: 156
  Date: Wed, 24 Aug 2011 01:01:19 GMT
  Connection: keep-alive
Location: /login/Internal/login.view?returnUrl=%wiki%2FHome%page.view%3Fname=secureWiki [following]

You can see that server:

  1. Does not respond with a WWW-Authenticate:... HTTP Header, but
  2. Redirects the browser to the login page

Now lets look at an attempt to use the getQuery API (which you can use to query data in the LabKey Server).

wget --server-response --max-redirects=0 https://www.labkey.org/query/Internal/getQuery.api

the response is

HTTP request sent, awaiting response... 
  HTTP/1.1 401 Unauthorized
  Server: Apache-Coyote/1.1
  WWW-Authenticate: Basic realm="LabKey Server and CPAS Distribution and Support"
  Content-Type: text/html
  Content-Length: 2871
  Vary: Accept-Encoding
  Date: Wed, 24 Aug 2011 01:05:35 GMT
  Connection: keep-alive

In this example, you can see that the server

  1. Responded with a HTTP response code of 401
  2. Added the WWW-Authenticate: Basic realm="LabKey Server and CPAS Distribution and Support" header which you can then programmatically use to authenticate using Basic Auth.

Now that I have shown you how the LabKey Server will respond to authentication requests, I can provide some examples of how to authenticate to the LabKey Server using various programming languages. Before we get to the examples, here are few links to documentation for programmatically accessing a LabKey Server and using the LabKey APIs

In the examples below, you will see two URLs used; one is meant to represent viewing a secured wiki page and the other is for reading the contents of a list. The URLs are:


Authenticating to LabKey Server using Python

LabKey currently ships a client PYTHON client API. The PYTHON client api currently supports (as of 01/01/2012):

  • reading data inside LabKey Server (selectRows)
  • inserting new data inside LabKey Server (insertRows)
  • deleting existing data inside LabKey Server (deleteRows)
  • updating data inside LabKey Server (updateRows)
  • executing SQL statements (executeSql)
  • updating a wiki page
  • posting a message to a message board

The client API will handle the authentication for you. If there are other tasks you need to perform, you can use the example code below to authenticate to the server

Authenticate to an API page

In this example, I will use the urllib2 library. I will load my creadentials into the a password manager and then let the OpenerDirector object handle passing the Basic Auth headers to the server when requested.

{% highlight python %} import urllib2 myemail = ‘email’ mypassword = ‘password’

Create a password manager

passmanager = urllib2.HTTPPasswordMgrWithDefaultRealm()

Add login info to the password manager

passmanager.add_password(None, mymachine, myemail, mypassword)

Create the AuthHandler

authhandler = urllib2.HTTPBasicAuthHandler(passmanager)

Create opener

opener = urllib2.build_opener(authhandler)

Build the URL for querying LabKey Server (Replace SCHEMA, QUERY with the schema name and query name of your choice)

myurl = ‘ https://your.labkey.server/labkey/query/Home/getQuery.api?schemaName=lists&query.queryName=listName’
# Get authenticated to send URL requests opener =_create_opener()

Use the opener to fetch a URL request

myrequest = urllib2.Request(myurl) try: response = opener.open(myrequest) print response.read() except urllib2.HTTPError, e: print e.code print e.read() {% endhighlight %}

Authenticate to an non API page

In this example, I will load my creadentials into the a password manager and in addition add the Basic Auth Header to all requests using the OpenerDirector object.

{% highlight python %} import urllib2 myemail = ‘email’ mypassword = ‘password’

Create a password manager

passmanager = urllib2.HTTPPasswordMgrWithDefaultRealm()

Add login info to the password manager

passmanager.add_password(None, mymachine, myemail, mypassword)

Create the AuthHandler

authhandler = urllib2.HTTPBasicAuthHandler(passmanager)

Create the Basic Authentication Header

authHeader = base64.encodestring("%s:%s" % (myemail, mypassword))[:-1] authHeader = "Basic %s" % authHeader

Create opener

opener = urllib2.build_opener(authhandler)

Build the URL for viewing a secured wiki on LabKey Server

myurl = ‘https://your.labkey.server/labkey/wiki/Home/page.view?name=securedWiki’
# Get authenticated to send URL requests opener =_create_opener()

Use the opener to fetch a URL request

myrequest = urllib2.Request(myurl) try: response = opener.open(myrequest) print response.read() except urllib2.HTTPError, e: print e.code print e.read()

{% endhighlight %}

NOTE: If you want to use only one opener for both API and non-API pages, you simply can use the 2nd example for accessing all pages.


Authenicating to LabKey Server with Node.js

There is currently not a client API for the Node.js language yet. (If you are using Node.js to work with data in your LabKey Server and want to assist in writing an client API, please post a message to the Developer Community Forum as we would love to work with you.)

As we saw above, with Python, there are two ways to authenticate. For API pages, the OpenerDirector object handles chaining of requests together and will properly resubmit a request if it receives a 401 HTTP response code with theWWW Authenicate: header. There is not something similar for NODE.js and thus there is only one way to authenticate to the server.

NOTE: Full disclosure, I am a Node.js novice, there are probably many better ways to do this. If you know a much better, simpler way, please post it in the comments and I will update this example.

{% highlight javascript linenos %} https = require("https");

var email = ‘myemail’; var password = ‘mypassword’; var auth = ‘Basic ‘ + new Buffer(email + ‘:’ + password).toString(‘base64’);

var authHeaders = { Authorization: auth };

var addUserURL = { host: ‘your.labkey.server’, port: 443, path: ‘ /labkey/wiki/Home/page.view?name=securedWiki’, method: ‘GET’, headers: authHeaders
};

var request = https.request(addUserURL, function(response) { response.setEncoding(‘utf8’); responseData = ‘’; response.on(‘data’, function (chunk) {
responseData += chunk; }); response.on(‘error’, function(e){ console.log("An error occurred during the request.") console.log(‘HTTP Response Code: ‘ + response.statusCode); console.log(‘HTTP Response Headers: ‘ +r esponse.headers); return; }); response.on(‘end’, function(){ console.log("Response Output: " + responseData) console.log(‘HTTP Response Code: ‘ + response.statusCode); console.log(‘HTTP Response Headers: ‘ + response.headers); }); });

request.end(); {% endhighlight %}

Authenticating to the LabKey Server using wget

Now, if you do not want to bother with these programming languages and just want to grab a page quickly or download some data and parse it with sed, you can just use wget. (To be completely honest, I use wget all the time for testing and/or grabbing a single page).

Authenticating to an API page.

This case is pretty simple one liner

wget --server-response --http-user=myEmail --http-password=myPassword \
 "https://your.labkey.server/labkey/query/Home/getQuery.api?schemaName=lists&query.queryName=listName"

Authenticating to a non-API page.

For this case, you will need to make two wget requests. The first to login and the 2nd to access the secured page.

wget --server-response --save-cookies cookies.cpas --keep-session-cookies \
https://your.labkey.server/Login/login.post --post-data "email=myEmail&password=myPassword"

wget --load-cookies cookies.cpas https://your.labkey.server/labkey/wiki/Home/page.view?name=securedWiki

Now that you have gotten past the authentication, you can start doing all sorts of fun things with your data.

There are a few other languages that I have used in the past, namely perl, that I should have included in these examples. I will try and get to those in a later post(s). (If you are using perl, see the Perl Client API for an example)

I hope this is helpful and understandable. If you have any questions, please post a comment or post a message to the Developer Community Forum