I recently wrote a Grok application that implements a REST API for Atom Publishing so that I can connect a website I have via my new Nokia phone has LifeBlog which uses the Atom API to talk to the server.
Anyway, the authentication on Atom is WSSE (good introduction article) which basically works like this:
PasswordDigest = Base64 \ (SHA1 (Nonce + CreationTimestamp + Password))
This is one of the pieces in a request header called Authorization
which can look something like this:
Authorization: WSSE profile="UsernameToken"
X-WSSE: UsernameToken Username="bob", PasswordDigest="quR/EWLAV4xLf9Zqyw4pDmfV9OY=",
Nonce="d36e316282959a9ed4c89851497a717f", Created="2003-12-15T14:43:07Z"
What I did was I wrote a simple Python script to mimic what the Nokia does but from a script. The script creates a password digest using these python modules: sha
, binascii
and base64
and then fires off a POST request. Here's thing, if you generate this header with base64.encodestring(ascii_string)
you get something like this:
quR/EWLAV4xLf9Zqyw4pDmfV9OY=\n
Notice the extra newline character at the end of the base64 encoded string. This is perfectly valid and is decoded easily with base64.decodestring(base64_string)
by the Grok app. Everything was working fine when I tried posting to http://localhost:8080/++rest++atompub/snapatom
and my application successfully authenticated the dummy user. I was happy.
Then I set this up properly on atom.someotherdomain.com
which was managed by Apache who internally rewrote the URL to a Grok on localhost:8080. The problem now was that the Authentication
header value was broken into two lines because of the newline character and then the whole request was rejected by Apache because some header values came without a :
semi-colon.
The solution was to not use base64.encodestring()
and base64.decodestring()
but to instead use base64.urlsafe_b64encode()
and base64.urlsafe_b64decode()
. Let me show you:
>>> import base64
>>> x = 'Peter'
>>> base64.encodestring(x)
'UGV0ZXI=\n'
>>> base64.urlsafe_b64encode(x)
'UGV0ZXI='
>>> base64.decodestring(base64.urlsafe_b64encode(x))
'Peter'
If you're still reading, then hopefully you won't make the same mistake as I did and wasting time on trying to debug Apache. The lesson learned from this is to use the URL safe base64 header values and not the usual ones.