How to Oauth authenticate using python for RM REST api?
I have tried doing this using oauth2 library and rolling my own oauth dance, but I seem to be missing a header. When I post credentials to the server, a new cookie is returned, and the process goes back to starting point it seems.
Also, I have compared my code with java example in OSLC tutorial, and seems consistent, except that in python I have to handle cookies myself. But looking at headers from http trace on browser login, it seems that I'm doing that consistently too. A working python example to study would be very helpful!
Thanks
4 answers
[code]
# http://stackoverflow.com/questions/301924/python-urllib-urllib2-httplib-confusion
import urllib
import urllib2
class HttpBot:
"""an HttpBot represents one browser session, with cookies."""
def __init__(self):
cookie_handler= urllib2.HTTPCookieProcessor()
redirect_handler= urllib2.HTTPRedirectHandler()
self._opener = urllib2.build_opener(redirect_handler, cookie_handler)
def GET(self, url):
return self._opener.open(url).read()
def POST(self, url, parameters):
return self._opener.open(url, urllib.urlencode(parameters)).read()
if __name__ == "__main__":
bot = HttpBot()
ignored_html = bot.POST('https://myserver.com:9443/jts//authenticated/identity', {})
ignored_html = bot.POST('https://myserver.com:9443/jts/authenticated/j_security_check?j_username=jdoe&j_password=s3cret', {})
print bot.GET('https://myserver.com:9443/rm/discovery/RMCatalog')
[code]
You need to
1.) RetrieveRequestToken
2.) FormLogin
3.) AuthenticateRequestToken (use the cookies from FormLogin)
4.) RetrieveAccessToken
5.) Do some work (use the cookies from 2&3)
edit: Bleeping useless post editor.
Comments
Step 4 in the log above actually does a form login successfully; after doing this, I can access protected resources from jts with no problem. But RM (Oauth) authentication still fails. Also, if I corrupt the credentials the value for response header x-com-ibm-team-repository-web-auth-msg is "authfailed";
But, I have had some success using oauth2 library; I will post my test code shortly.
1. needs to know the consumer_key & _secret. So need to handle this if I want the same code to work on multiple jts environments.
2. handling the form login independently of oauth2 module seems wrong, even though it's working. This should be something that is handled by the library, but I've not seen how to do that (but will continue researching it):
Working example:
def main():
#from jts/admin Consumers page; any valid key/secret will work
#would like to implement something that does not require knowing consumer key and secret, like shown in java example
consumer_key = 'key'
consumer_secret = 'secret'
user = 'user@ibm.com'
pw = 'secret'
REQUEST_TOKEN_URL = 'https://myserver.ibm.com/jts/oauth-request-token'
AUTHORIZE_URL = 'https://myserver.ibm.com/jts/oauth-authorize'
ACCESS_TOKEN_URL = 'https://myserver.ibm.com/jts/oauth-access-token'
PROTECTED_RESOURCE_URL = 'https://myserver.ibm.com/rm/discovery/RMCatalog'
RM_RESOURCE_URL = 'https://myserver.ibm.com/rm/publish/resources?projectName=Test+Configuration+Requirements'
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
#step 1: get token
resp, content = client.request(REQUEST_TOKEN_URL, "POST")
if resp['status'] != '200':
raise Exception("Invalid response %s." % resp['status'])
request_token = dict(urlparse.parse_qsl(content))
auth_url_with_token='%s?oauth_token=%s' % (AUTHORIZE_URL, request_token['oauth_token'])
#step 2: authenticate to jts server
cookie = formLoginToServer('jts', user, pw)
#debug: prove form login was successful by getting some jts protected resource
page, _ = getResourceFromServer(cookie, 'https://myserver.ibm.com/jts/users?query=foaf:nick=%22billowen@us.ibm.com%22')
print page
#step 3: get authorize url using token from step 1
page, _ = getResourceFromServer(cookie, auth_url_with_token )
#create a new client object that contains our request token and secret
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
client = oauth.Client(consumer, token)
#step 4: post the access token url
resp, content = client.request(ACCESS_TOKEN_URL, "POST")
access_token = dict(urlparse.parse_qsl(content))
print "Access Token:"
print " - oauth_token = %s" % access_token['oauth_token']
print " - oauth_token_secret = %s" % access_token['oauth_token_secret']
print "You may now access protected resources using the access tokens above."
#now create a new client using access_token & access_token_secret
token = oauth.Token(access_token['oauth_token'],access_token['oauth_token_secret'])
client = oauth.Client(consumer, token)
#with that we can get protected resources like RMCatalog
print 'now get RMCatalog at ', PROTECTED_RESOURCE_URL
response = client.request(PROTECTED_RESOURCE_URL)
#output the RMCatalog rdf
print 'response is '
g = Graph()
g.parse(data=response[1], format='xml')
g.serialize()
#prove that we can also get the set of requirements defined for a project
print 'now get some requirements data'
response = client.request(RM_RESOURCE_URL)
rDom = ET.fromstring( response[1] )
f = open('/tmp/rm_data.xml', 'w')
f.write(ET.tostring( rDom ) )
f.close()
print 'Data saved into /tmp/rm_data.xml'
if __name__ == '__main__':
main()
Helpful Links:
http://developer.linkedin.com/documents/getting-oauth-token-python
http://parand.com/say/index.php/2010/06/13/using-python-oauth2-to-access-oauth-protected-resources/
I am using RRC v4.0. I was using RRC 3.0.1.1 previously, and was able to authenticate using slightly simplified steps, but seemed like RRC was giving access tokens at the wrong spot. That changed in releases after 3.0.1.1 and I am not able to authenticate since.
Here is the log from a test program:
start...
--------------------------------------------------------------------------------
OLOGIN STEP 1: get protected url: https://myserver.ibm.com/rm/discovery/RMCatalog
Request headers:
Response headers:
content-length: 352
set-cookie: jfs-oauth-access-token0=; expires=Thu, 01-Jan-70 00:00:00 GMT; path=/rm, jfs-oauth-access_token-secret0=; expires=Thu, 01-Jan-70 00:00:00 GMT; path=/rm, jfs-request-token-26923537973f4b09a74bf518a25b0620="JQYlfePc8ISyOB8D+ByJKOJma9W7LmrjcfWb0A1VSec="; Version=1; expires=Thu, 26-Jul-12 19:34:25 GMT; path=/rm
server: Apache-Coyote/1.1
x-jazz-web-oauth-url: https://myserver.ibm.com/jts/oauth-authorize?oauth_token=26923537973f4b09a74bf518a25b0620
connection: close
date: Thu, 26 Jul 2012 19:29:25 GMT
content-type: text/html
www-authenticate: OAuth realm=https://myserver.ibm.com/jts/oauth-authorize
HTTP Response Code: 401
OLOGIN STEP 1. next url: https://myserver.ibm.com/jts/oauth-authorize?oauth_token=26923537973f4b09a74bf518a25b0620
--------------------------------------------------------------------------------
OLOGIN Step 2: get redirect url: https://myserver.ibm.com/jts/oauth-authorize?oauth_token=26923537973f4b09a74bf518a25b0620
Request headers:
Host: myserver.ibm.com
Response headers:
x-com-ibm-team-repository-web-auth-msg: authrequired
content-length: 0
set-cookie: JSESSIONID=D773DF4B0FDF018FDABD54E34100DC5C; Path=/jts/; Secure; HttpOnly
expires: Wed, 31 Dec 1969 17:00:00 MST
server: Apache-Coyote/1.1
connection: close
location: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
cache-control: private
date: Thu, 26 Jul 2012 19:29:25 GMT
HTTP Response Code: 302
OLOGIN STEP 2. next url: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
--------------------------------------------------------------------------------
OLOGIN Step 3: get redirect url: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
Request headers:
Cookie: JSESSIONID=D773DF4B0FDF018FDABD54E34100DC5C; Path=/jts/; Secure; HttpOnly
Response headers:
x-com-ibm-team-repository-web-auth-msg: authrequired
content-length: 2870
set-cookie: JazzFormAuth=Form; Path=/jts
expires: Wed, 31 Dec 1969 17:00:00 MST
server: Apache-Coyote/1.1
connection: close
cache-control: private
date: Thu, 26 Jul 2012 19:29:25 GMT
content-type: text/html;charset=UTF-8
HTTP Response Code: 200
--------------------------------------------------------------------------------
OLOGIN Step 4: post authdata url: https://myserver.ibm.com/jts/j_security_check
Request headers:
Content-type: application/x-www-form-urlencoded;charset=UTF-8
Cookie: JSESSIONID=D773DF4B0FDF018FDABD54E34100DC5C; Path=/jts/; Secure; HttpOnly JazzFormAuth=Form;
Response headers:
x-com-ibm-team-repository-web-auth-msg: authrequired
content-length: 0
set-cookie: JSESSIONID=7E49C694DF6A68192F613CFC75F38F6D; Path=/jts/; Secure; HttpOnly
NOTE: step 4 should not return a new jesessionid at this point - trouble ahead
expires: Wed, 31 Dec 1969 17:00:00 MST
server: Apache-Coyote/1.1
connection: close
location: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
cache-control: private
date: Thu, 26 Jul 2012 19:29:25 GMT
HTTP Response Code: 302
OLOGIN STEP 4. next url: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
--------------------------------------------------------------------------------
OLOGIN Step 5: get redirect url: https://myserver.ibm.com/jts/authenticated/identity?redirectPath=%2Fjts%2Foauth-authorize%3Foauth_token%3D26923537973f4b09a74bf518a25b0620
Request headers:
Cookie: JSESSIONID=D773DF4B0FDF018FDABD54E34100DC5C; Path=/jts/; Secure; HttpOnly JazzFormAuth=Form;
Response headers:
x-com-ibm-team-repository-web-auth-msg: authrequired
content-length: 2870
set-cookie: JSESSIONID=5F1F40880A2E9D079A0D46E15C47122D; Path=/jts/; Secure; HttpOnly, JazzFormAuth=Form; Path=/jts
expires: Wed, 31 Dec 1969 17:00:00 MST
server: Apache-Coyote/1.1
connection: close
cache-control: private
date: Thu, 26 Jul 2012 19:29:26 GMT
content-type: text/html;charset=UTF-8
NOTE: step 5 should return with cookie jsessionid & jsessionidsso - it does not so stop - we are not authenticated
stopping...
Comments
"Response headers: x-com-ibm-team-repository-web-auth-msg: authrequired"
You're getting authrequired which means your Form Based Login is failing.
https://jazz.net/wiki/bin/view/Main/JFSCoreSecurity#Implementation Therefore the client should always check for the presence of the x-com-ibm-team-repository-web-auth-msg HTTP response header, if the value of this is authrequired then the client must use the form-based login process described below ... The details of the Jazz Foundation user authentication follows the J2EE form-based authentication specification, as shown below. At this time the URL for the form is not present in the rot services document, this may change in future versions of the Foundation.
URL: https://{server-name-and-port}/jazz/j_security_check
User name: j_username
Password: j_password
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Comments
Bas Bekker
JAZZ DEVELOPER Jul 26 '12, 12:55 p.m.Hi William, I don't have a python example, but I can try helping with the authentication logic. How far did you get with the authentication logic? Besides the cookie, what headers are you getting back? Also, what version of RRC are you using?
Joseph Reedick
Jul 26 '12, 2:53 p.m.This may (or may not) help: https://jazz.net/wiki/bin/view/Main/JFSCoreSecurity#Application_Authentication The 'Implementation' section has the details.
What OAuth library are you using?
William Owen
JAZZ DEVELOPER Jul 26 '12, 4:32 p.m.Hi Joseph, I am trying two approaches: The first approach is using oauth2 from here: http://pypi.python.org/pypi/oauth2/.
The second approach is using urllib2 calls directly to implement the steps of the oauth protocol.