How do I send a POST using 2-legged oauth2 in python?

11,943

Solution 1

Here is ACTUAL, WORKING code of how I got my POST or PUT to work, kindly supplied by Wes Barnes from Echo360 Lecture Capture. I don't want anyone else doing a 2-legged oauth POST/PUT to have to reinvent the wheel.

import oauth2 as oauth
import time
import urllib2 as urllib

echo_base_url = 'http://pilot.echo360.com/ess/scheduleapi/v1'

consumer = oauth.Consumer(key ='xxxxx', secret='xxxx')
client = oauth.Client(consumer)
params = "<person><first-name>Jon</first-name><last-name>Doe</last-name><title>Super  Hero</title><email-address>[email protected]</email-address><block-alerts>false</block-alerts><time-zone>US/Eastern</time-zone><locale>en_US</locale><credentials><user-name>[email protected]</user-name><password>password</password></credentials><organization-roles><organization-role><organization-id>b1973c39-dc76-4cab-a4aa-3f9efd628df2</organization-id><role>role-name-admin</role></organization-role></organization-roles></person>"

resp, content = client.request(
                echo_base_url + "/people/",
                method = "PUT",
                body=params,
                headers={'Content-type': 'application/xml'}
                #force_auth_header=True
                )
print resp, content

Solution 2

This is the code I have been using to make a POST request to Twitter using oauth2. Hope it helps you to figure out the syntax.

import oauth2 as oauth, urllib

def oauth_req(url, key, secret, http_method="POST", post_body=None, http_headers=None):
    CONSUMER_KEY = YOUR_KEY
    CONSUMER_SECRET = YOUR_SECRET
    consumer = oauth.Consumer(key=CONSUMER_KEY, secret=CONSUMER_SECRET)
    token = oauth.Token(key=key, secret=secret)
    client = oauth.Client(consumer, token)
    resp, content = client.request(
        url,
        method=http_method,
        body=urllib.urlencode({'status': post_body}),
        headers=http_headers,
        force_auth_header=True,
    )
    return content

oauth_req('http://api.twitter.com/1/statuses/update.json', KEY, SECRET, post_body=MESSAGE)
Share:
11,943
HelenM
Author by

HelenM

Updated on June 04, 2022

Comments

  • HelenM
    HelenM almost 2 years

    I have a working GET using 2-legged oauth2 in python. Here is the WORKING GET code:

    the imports:

    import oauth2 
    import urllib #for url-encode
    import urllib2 #for getting and receiving data from server
    import time #Unix timestamp import oauth2
    

    the call:

    resourceUrl = "https://test.mysite:8443/ess/scheduleapi/v1/people"
    request = build_request(resourceUrl,'GET')
    u = urllib2.urlopen(request.to_url())
    people_data = u.read()
    

    the function to build the request:

    def build_request(url, method):
        params = {                                            
            'oauth_version': "1.0",
            'oauth_nonce': oauth2.generate_nonce(),
            'oauth_timestamp': int(time.time())
        }
        consumer = oauth2.Consumer(key='mykey',secret='mysecret')
        params['oauth_consumer_key'] = consumer.key
        req = oauth2.Request(method=method, url=url, parameters=params)
        signature_method = oauth2.SignatureMethod_HMAC_SHA1()
        req.sign_request(signature_method, consumer, None)
        return req
    #end build_request
    

    So, I thought I could copy the part of the GET that I thought I'd need, plus combine it with the syntax I got off of some urllib2 documentation, and cook up a working POST. Not so. Keep in mind I have the same imports and the same build_request function. Here is the BROKEN POST code. Please advise!

    the call:

    myurl =  "https://test.mysite:8443/ess/scheduleapi/v1/people" 
    myaction = 'POST'
    myxml = somexmlIalreadygenerated
    person_added, err = post_or_put_me(myaction,myxml,myurl)
    

    the function to POST:

    def post_or_put_me(action,xml,url)
        request = build_request(url,action) # use same header-generating code as GET did?
        post_data = urllib.urlencode(xml)
        req = urllib2.Request(request,post_data)
        try:
            u = urllib2.urlopen(req)
            post_or_put_returned_data = u.read()
            print 'LENGTH  :', len(post_or_put_returned_data)
            print 'DATA    :', post_or_put_returned_data
        except urllib2.HTTPError, e:
            server_error = 'HTTPError = ' + str(e.code)
        except urllib2.URLError, e:
            server_error = 'URLError = ' + str(e.reason)
        except httplib.HTTPException, e:
            server_error = 'HTTPException'
        except Exception:
            import traceback
            server_error = 'generic exception: ' + traceback.format_exc()
        #endtry
    
        if server_error:
            err_msg = server_error
        else:   
            succ_msg = 'you had a successful post or put'
        #endif
    
        return succ_msg, err_msg
    #end post_or_put_me
    

    Here's my second attempt:

    def post_or_put_me(action,xml,url):
        myrequest = build_request(url,'POST')
    
        CONSUMER_KEY = 'admin_access'
        CONSUMER_SECRET = 'xxxxxxxxxx' 
        consumer = oauth2.Consumer(key=CONSUMER_KEY, secret=CONSUMER_SECRET)
        token = oauth2.Token(key=CONSUMER_KEY, secret=CONSUMER_SECRET)
        client = oauth2.Client(consumer, token)
        resp, content = client.request(
            url,
            method=action,
            body=urllib.urlencode(str(xml)),
            headers= myrequest.headers,
            force_auth_header=True,
        )
        print 'resp, content are', resp, content
    
  • HelenM
    HelenM over 11 years
    I am still working on trying to get this to work...will get back to the comment once I have something useful to add--thanks!
  • HelenM
    HelenM over 11 years
    so if I use this code instead of mine, how do I encode my post data (it is xml, not a dictionary, as you have done), and how do I get my headers? Reference 'build_request' in my code above...is some portion of this to go in my headers=? If so, how do I reference it? Thank you again!
  • r_31415
    r_31415 over 11 years
    If your post data is an XML file, then you need to stringify it first and pass it to post_body as I did. Your headers should go in the client.request method in the snippet I posted. There you can find a headers variable as a dictionary. In this case, you can pass it to http_headers in the oauth_req function. You can see what happens with your headers in the source of the Client class github.com/simplegeo/python-oauth2/blob/master/oauth2/…
  • HelenM
    HelenM over 11 years
    I am pasting an edit of your code, as I have tried it, above. It doesn't like my stringify of xml, to begin with. It will probably hate my attempt to fill the headers, too, but one problem at a time. Sorry for being slow on the uptake, here!
  • r_31415
    r_31415 over 11 years
    What error do you get? Try doing a simple test with a dummy post_body and see if it works.
  • HelenM
    HelenM over 11 years
    Hi, If I fill it with a blank post body, I get hung up on my lack of headers:Error 401 HTTP ERROR: 401 Unauthorized The request requires user authentication
  • r_31415
    r_31415 over 11 years
    Well, a blank post body can produce issues. Didn't you add your headers? Why? Obviously you need them.
  • HelenM
    HelenM over 11 years
    Yes, yes, of course. I am pretty sure that my myrequest=build_request function generates the headers. Can you tell me how to extract them from myrequest? After that, I'll move the xml question off of this post. Thank you!
  • r_31415
    r_31415 over 11 years
    Have you looked at github.com/simplegeo/python-oauth2/blob/master/oauth2/… ? You don't need to use build_request as it's part of what auth_req is doing already. Furthermore, build_request returns an instance of Request class which doesn't have a headers property, that's probably the reason your authentication is failing. Just pass your headers as a dictionary to oauth_req and take a look at the source so you can get a grasp of what oauth2 is doing.
  • r_31415
    r_31415 over 11 years
    This is exactly the same that the code I posted but in your situation you didn't need a token and your required parameters are a bit too much (<password></password>) so this won't work in most cases. Just to let you know.
  • HelenM
    HelenM over 11 years
    @RobertSmith Hi, Robert. Yes, and in my case, I apparently did not need to urlencode my xml (which it looked to me like I did in all the documentation I saw). I also misunderstood the headers in that they are GENERATED BY THE OAUTH2 FUNCTION, and all I needed to do was tell it (in my case) Content-Type as an additional header parm. Thanks for your advice.
  • Piyush S. Wanare
    Piyush S. Wanare almost 6 years
    can I get engagement data with using gnip?