It’s been a while since I wrote the last one, but a user on reddit asked how to get OAuth working with Imgur, and I couldn’t resist giving it one more shot. Thanks to my recent experience with the Netflix API, even though it was in C# rather than in Python, I was able to wrangle a quick and easy script for getting started.
Here’s the PasteBin with the complete code, thought it’s shown at the bottom of the page, as usual.
Here’s the basic imports, nothing much to say. Make sure you’re running the latest version of the requests module, as Reitz has made some significant changes and you wouldn’t want to be left behind, would you? pip install requests --upgrade usually solves that quickly enough.
import requests import pprint
In this next section, we’re making a request the the Imgur API, with the client_id, response_type, and application_state passed in. The client_id is one of two strings that you receive from the Imgur API application registration. The response_type can be either “code”, “token”, or “pin”. The API page deals with all three pretty well, so I won’t go too much into it, just that we’re going to use the ‘pin’ setting here.
The getPin method just builds a simple URL and prints it out so you can manually visit it and enter the PIN back into the application, so that we can continue along with the process. This is the sort of tricky part of OAuth, in that all 3 paths to authorization require user input, you’ll always have to ask the user to “allow” your application to act on their behalf.
#https://api.imgur.com/oauth2#auth_url def getPin(client_id, client_secret): """build a URL for the user to navigate to and "Allow" the application""" resp = "pin" #can be any string at all state = "anything" url = r"https://api.imgur.com/oauth2/authorize?client_id={cid}&response_type={resp}&state={app_state}" print "browse to the following URL and grab the pin:" pin_url = url.format(cid=client_id,resp= resp, app_state=state) print pin_url return pin_url
Here, we pass along the client_id, client_secret and the pin we got from getPin’s URL page. We pass the poorly-named ‘params’ dict as ‘data’ to the POST request. We include the grant_type, so that the API knows that we’re trying to use a ‘pin’ as our way to authenticate. We also have to make sure we set ‘verify’ to False for the request, or otherwise we get a SSLError saying that “api.imgur” is not “imgur”. I don’t know enough about SSL to tell you why, other than maybe the certificate is only for imgur.com rather than api.imgur.com, but I really don’t know.
def exchangePinForTokens(client_id, client_secret, pin): """takes the client_id and client_secret from the registered application URL, along with the pin returned from `getPin()`, and return an access_token and a refresh_token""" #the query parameters you'll send along with the POST request params ={ "client_id" :client_id, "client_secret" : client_secret, "grant_type" : "pin", "pin": pin} url = r"https://api.imgur.com/oauth2/token/" #make sure the data is sent with the POST request, along with disabling the # SSL verification, potential security warning r = requests.post(url, data = params, verify=False)
The last half of the exchangePinForTokens method just prints off the tokens to the screen and returns them. Note that in the recent changes to python-requests, ‘.json’ has become a method, rather than a property, so you’ll need to add the parens to call it. We’re also using the pprint module to get a prettier output to the terminal.
The access_token is what this was all about. You’ll be sending the access_token along with all future requests, in the headers, in order to prove that you’re securely authenticated. The refresh_token is used to get a new access_token once the current one expires. You’ll get the expiration time in the same response that the two access_token and refresh_tokens are in.
j= r.json() print "The exchangePinForTokens API response:" pprint.pprint(j) #add the access_token to the headers as # Authorization: Bearer YOUR_ACCESS_TOKEN access_token= j['access_token'] refresh_token= j['refresh_token'] print "Access Token: {0}\nRefresh Token: {1}".format(access_token, refresh_token) return (access_token, refresh_token)
Finally, we want to try to upload something to Imgur to make sure that the access_token works as expected. As mentioned, we add the ‘authorization’ header with the “Bearer {access_token}” value along with the regular POST payload, of the image URL, the type of image we’re uploaded, and the title of the image.
Then we quickly parse the returned JSON response for the image URL and voila! we’re done. Not too tricky eh?
def uploadImage(access_token, image_url): """uploads an image using it's URL, the access_token is required""" #need to include the authorization headers, # in order to make use of the access token headers = {"authorization":"Bearer {0}".format(access_token)} upload_url = r'https://api.imgur.com/3/upload' #this is the data we'll POST to the api's URL payload = {'image' : image_url, 'type':'url', 'title':"WORKS"} #make the upload, ensuring that the data, headers are included, and # make sure to disable the verification of the SSL. Potential insecurty though r = requests.post(upload_url, data=payload, headers=headers, verify=False) #save the json response, print it to screen j = r.json() print "The UploadImage API response:" pprint.pprint(j) #print the img URL to verify that the image is still there uploaded_url = j['data']['link'] print "The uploaded image URL is: {0}".format(uploaded_url)
This is just to make sure that when the code is being imported, it doesn’t try
to run all of the logic without you directly telling it to.
#a popular Python idiom to make sure that the following code gets run when this # file is ran as __main__, rather than imported if __name__ == '__main__': """Run the following if module is top module""" #found here: https://api.imgur.com/oauth2/addclient client_id= r"client_id_from_addclient" client_secret= r"client_secret_from_addclient" image_url = r'http://www.personal.psu.edu/afr3/blogs/siowfa12/success.jpeg' #URL needed to have the user visit and allow the application to use the pin getPin(client_id,client_secret) pin = raw_input("input PIN\n") access_token, refresh_token = exchangePinForTokens(client_id,client_secret,pin) uploadImage(access_token, image_url)
Here’s the entire thing again.
import requests import pprint #https://api.imgur.com/oauth2#auth_url def getPin(client_id, client_secret): """build a URL for the user to navigate to and "Allow" the application""" resp = "pin" #can be any string at all state = "anything" url = r"https://api.imgur.com/oauth2/authorize?client_id={cid}&response_type={resp}&state={app_state}" print "browse to the following URL and grab the pin:" pin_url = url.format(cid=client_id,resp= resp, app_state=state) print pin_url return pin_url def exchangePinForTokens(client_id, client_secret, pin): """takes the client_id and client_secret from the registered application URL, along with the pin returned from `getPin()`, and return an access_token and a refresh_token""" #the query parameters you'll send along with the POST request params ={ "client_id" :client_id, "client_secret" : client_secret, "grant_type" : "pin", "pin": pin} url = r"https://api.imgur.com/oauth2/token/" #make sure the data is sent with the POST request, along with disabling the # SSL verification, potential security warning r = requests.post(url, data = params, verify=False) j= r.json() print "The exchangePinForTokens API response:" pprint.pprint(j) #add the access_token to the headers as # Authorization: Bearer YOUR_ACCESS_TOKEN access_token= j['access_token'] refresh_token= j['refresh_token'] print "Access Token: {0}\nRefresh Token: {1}".format(access_token, refresh_token) return (access_token, refresh_token) def uploadImage(access_token, image_url): """uploads an image using it's URL, the access_token is required""" #need to include the authorization headers, # in order to make use of the access token headers = {"authorization":"Bearer {0}".format(access_token)} upload_url = r'https://api.imgur.com/3/upload' #this is the data we'll POST to the api's URL payload = {'image' : image_url, 'type':'url', 'title':"WORKS"} #make the upload, ensuring that the data, headers are included, and # make sure to disable the verification of the SSL. Potential insecurty though r = requests.post(upload_url, data=payload, headers=headers, verify=False) #save the json response, print it to screen j = r.json() print "The UploadImage API response:" pprint.pprint(j) #print the img URL to verify that the image is still there uploaded_url = j['data']['link'] print "The uploaded image URL is: {0}".format(uploaded_url) #a popular Python idiom to make sure that the following code gets run when this # file is ran as __main__, rather than imported if __name__ == '__main__': """Run the following if module is top module""" #found here: https://api.imgur.com/oauth2/addclient client_id= r"client_id_from_addclient" client_secret= r"client_secret_from_addclient" image_url = r'http://www.personal.psu.edu/afr3/blogs/siowfa12/success.jpeg' #URL needed to have the user visit and allow the application to use the pin getPin(client_id,client_secret) pin = raw_input("input PIN\n") access_token, refresh_token = exchangePinForTokens(client_id,client_secret,pin) uploadImage(access_token, image_url)
This is great tankor!
Do you know if there is an automatic way to get the PIN number though? (instead of visiting the page, accepting conditions and copying and pasting it)
No, I’m not aware of anyway to get the pin number. I mean you could use Mechnanize or Selenium and drive a browser, but other than that, I don’t think so. Glad you love it man, if you’ve got any other topics you’d like covered, let me know.
This makes the API so much easier to understand. Thanks for taking the time to do this.