Generate a Github OAuth2 Token¶
There are two ways to authenticate with the GitHub API: HTTP basic auth, and OAuth2. [1] It is preferable to use OAuth2, so your script can run without user input, and without storing your password.
The OAauth2 token can be sent in the request header, or as a parameter. We will send it as a header in later examples.
POST Request¶
First, we will prompt the user for username/password, and compose a POST request to the API. The request format is documented in the OAuth section of the Github API docs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | GITHUB_API = 'https://api.github.com'
import requests
import json
from urlparse import urljoin
def main():
#
# User Input
#
username = raw_input('Github username: ')
password = raw_input('Github password: ')
#
# Compose Request
#
url = urljoin(GITHUB_API, 'authorizations')
payload = {}
res = requests.post(
url,
auth = (username, password),
data = json.dumps(payload),
)
print res.text
if __name__ == '__main__':
main()
|
Let’s give it a try:
(class)$ python authtoken.py
Github username: jmcvetta
Github password: fooba
That’s not good - our password is shown when we type it!
Password Privacy¶
We can protect the user’s privacy while inputting their password with the
getpass
library. While we’re at it, we can prompt the user for an optional
note to describe how this token will be used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | GITHUB_API = 'https://api.github.com'
import requests
import getpass
import json
from urlparse import urljoin
def main():
#
# User Input
#
username = raw_input('Github username: ')
password = getpass.getpass('Github password: ')
note = raw_input('Note (optional): ')
#
# Compose Request
#
url = urljoin(GITHUB_API, 'authorizations')
payload = {}
if note:
payload['note'] = note
res = requests.post(
url,
auth = (username, password),
data = json.dumps(payload),
)
print res.text
if __name__ == '__main__':
main()
|
Let’s give it a try:
(class)$ python authtoken.py
Github username: jmcvetta
Github password:
Note (optional): admin script
{"scopes":[],"note_url":null,"created_at":"2012-10-21T05:32:30Z","url":"https://api.github.com/authorizations/744660","app":{"url":"http://developer.github.com/v3/oauth/#oauth-authorizations-api","name":"admin script (API)"},"updated_at":"2012-10-21T05:32:30Z","token":"a977026974077e83e593744aa9308422e92a26bd","note":"admin script","id":744660}
Seems to have worked! The response is a big JSON blob.
JSON Parsing¶
We can parse the JSON response and provide just the token, in nice human-readable form, to the user.
Explore the response data by setting a breakpoint and running our program in the
debugger. Start by parsing res.text
into JSON, and examining the keys.
The token lives in the creatively-named field token
. We will extract it and
print it for the user.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | GITHUB_API = 'https://api.github.com'
import requests
import getpass
import json
from urlparse import urljoin
def main():
#
# User Input
#
username = raw_input('Github username: ')
password = getpass.getpass('Github password: ')
note = raw_input('Note (optional): ')
#
# Compose Request
#
url = urljoin(GITHUB_API, 'authorizations')
payload = {}
if note:
payload['note'] = note
res = requests.post(
url,
auth = (username, password),
data = json.dumps(payload),
)
#
# Parse Response
#
j = json.loads(res.text)
token = j['token']
print 'New token: %s' % token
if __name__ == '__main__':
main()
|
Let’s give it a try:
(class)$ python authtoken.py
Github username: jmcvetta
Github password:
Note (optional): admin script
New token: 9c0e2ab295ee0e92130142ad3c90bbf5fe93642f
Bingo - it worked!
Error Handling¶
But what if we don’t type the right username/password combo?
(class)$ python authtoken.py
Github username: jmcvetta
Github password:
Note (optional):
Traceback (most recent call last):
File "authtoken.2.py", line 46, in <module>
main()
File "authtoken.2.py", line 42, in main
token = j['token']
KeyError: 'token'
Gross.
Once again we can run our program in the debugger to explore the server’s
response. It looks like we have a res.status_code
of 401. Any HTTP
response code 400 or above indicates an error. It also looks like the server
helpfully provides a message
field with an error message.
We can look for response codes >= 400 and present the user a friendly error message:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | GITHUB_API = 'https://api.github.com'
import requests
import getpass
import json
from urlparse import urljoin
def main():
#
# User Input
#
username = raw_input('Github username: ')
password = getpass.getpass('Github password: ')
note = raw_input('Note (optional): ')
#
# Compose Request
#
url = urljoin(GITHUB_API, 'authorizations')
payload = {}
if note:
payload['note'] = note
res = requests.post(
url,
auth = (username, password),
data = json.dumps(payload),
)
#
# Parse Response
#
j = json.loads(res.text)
if res.status_code >= 400:
msg = j.get('message', 'UNDEFINED ERROR (no error description from server)')
print 'ERROR: %s' % msg
return
token = j['token']
print 'New token: %s' % token
if __name__ == '__main__':
main()
|
Let’s give it a try:
(class)$ python authtoken.py
Github username: jmcvetta
Github password:
Note (optional):
ERROR: Bad credentials
Now we have a friendly, useful program.
Footnotes
[1] | http://developer.github.com/v3/#authentication |