Skip to content

Instantly share code, notes, and snippets.

@jasonsalas
Created February 19, 2015 10:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonsalas/61e4185d508c52d00c58 to your computer and use it in GitHub Desktop.
Save jasonsalas/61e4185d508c52d00c58 to your computer and use it in GitHub Desktop.
OAuth2 consumer for App Engine to access Pushbullet's API
application: YOUR_APP_NAME_HERE
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: oauth.application
libraries:
- name: webapp2
version: latest
- name: jinja2
version: latest
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<style type="text/css">
BODY { font-family:Arial, sans-serif; font-size:14pt; }
</style>
<title>{{ app_title }}</title>
</head>
<body>
<h1>{{ app_title }}</h1>
<hr/>
<a href="{{ auth_url }}">{{ auth_link_text }}</a>
</body>
</html>
#
# OAuth2 consumer for Google App Engine to access Pushbullet
# -------------------------------------------------------------------
# This App Engine app handles the acquisition of an access token for
# the Pushbullet API (https://docs.pushbullet.com) using the OAuth2
# authorization code flow for web server applications.
#
# This same flow could be used to generate an access token using
# the OAuth2 implicit flow for client apps, by changing the
# "response_type" parameter in the authorization URL for your
# Pushbullet app at https://www.pushbullet.com/create-client.
#
# For more, see https://docs.pushbullet.com/#oauth.
#
# App Engine doesn't include a facility for web sessions, but such
# can be rolled manually using cookies and Memcache.
#
__author__ = 'jasonsalas671@gmail.com (Jason Salas)'
import webapp2
import json
import httplib2
import urllib
import datetime
import jinja2
import os
from google.appengine.ext import ndb
JINJA_ENVIRONMENT = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
''' Datastore model for user credentials '''
class PushbulletCredentials(ndb.Model):
access_token = ndb.StringProperty(indexed=True)
name = ndb.StringProperty(indexed=True)
email = ndb.StringProperty(indexed=True)
date = ndb.DateTimeProperty(auto_now_add=True)
class PushbulletAuthorize(webapp2.RequestHandler):
def get(self):
''' STEP 1: send the browser to Pushbullet's authorization URL '''
# check for valid credentials
if self.request.cookies.get("username") is None:
form_values = {
"app_title" : "OAuth2 login provider",
"auth_url" : "", # generate this URL at https://www.pushbullet.com/create-client
"auth_link_text" : "signin"
}
template = JINJA_ENVIRONMENT.get_template("login.html")
self.response.out.write(template.render(form_values))
else:
self.redirect('/content')
class OAuth2RedirectHandler(webapp2.RequestHandler):
''' STEP 2: get the authorization code and exchange it for an access token '''
def get(self):
error = self.request.get("error")
if error is not None:
# grab the code from Pushbullet
code = self.request.get("code")
headers = { 'Content-type': 'application/x-www-form-urlencoded' }
body = {
"grant_type" : "authorization_code",
# generate the following two values at https://www.pushbullet.com/create-client
"client_id" : YOUR_CLIENT_ID,
"client_secret" : YOUR_CLIENT_SECRET,
"code" : code
}
# exchange the code for an access token
http = httplib2.Http()
response, content = http.request("https://api.pushbullet.com/oauth2/token", "POST", headers=headers, body=urllib.urlencode(body))
data = json.loads(content)
# make an authorized request to Pushbullet to extract the user's real name
http.add_credentials(data["access_token"], "")
profile_response, profile_content = http.request("https://api.pushbullet.com/v2/users/me", "GET")
profile_data = json.loads(profile_content)
# save the user's credentials in the data store
'''
NOTE: Pushbullet's OAuth provider doesn't enforce an expiry for access tokens,
and thus doesn't issue refresh tokens, but they *might* expire at some point in the future.
'''
credentials = PushbulletCredentials()
credentials.access_token = data["access_token"]
credentials.name = profile_data["name"]
credentials.email = profile_data["email"]
credentials.put()
# save the user's e-mail address in a cookie to persist between handlers
cookie_expiry = datetime.datetime.now() + datetime.timedelta(weeks=104)
self.response.set_cookie("username", credentials.email, expires=cookie_expiry)
# send the browser to the content page to interact with the API
self.redirect('/content')
class Logout(webapp2.RequestHandler):
def get(self):
cookie = self.request.cookies.get("username")
if cookie is not None:
# backdate the cookie's expire date by 40 years to remove it from the browser
cookie_expiry = datetime.datetime.now() + datetime.timedelta(weeks=-480)
self.response.set_cookie("username", cookie, expires=cookie_expiry)
''' TODO: delete the credentials from the database '''
self.redirect('/login')
class PageContent(webapp2.RequestHandler):
def get(self):
# check for valid session credentials
cookie = self.request.cookies.get("username")
if cookie is not None:
''' YOUR AUTHORIZED API CODE GOES HERE '''
else:
self.redirect('/login')
application = webapp2.WSGIApplication([
('/oauth2_complete', OAuth2RedirectHandler),
('/login', PushbulletAuthorize),
('/logout', Logout),
('/content', PageContent)
], debug=False)
@jasonsalas
Copy link
Author

This is actually a pretty simple OAuth2 flow, as Pushbullet is really easy to connect to and start using. If you're just starting to learn OAuth, this is a good place to start, in addition to other providers that implement simple setups like Reddit and GitHub The more top-heavy authorization providers like Google and Facebook have more detailed flows with dedicated libraries to help you along the way.

@cindygis
Copy link

cindygis commented May 6, 2015

I've written a script which uses https://github.com/randomchars/pushbullet.py to send pushes based on a certain set of conditions. I use some other third-party libraries as well, and it works as expected on my local machine. I then converted it to a GAE app with the intention of scheduling it. I'm new to auth though (and GAE for that matter), so while every other part of my script works fine on GAE, the Pushbullet credentials will not authenticate.

If I comment out the line where I try to authenticate my token, everything else works correctly. It appears that your code here would solve my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment