Created
August 22, 2016 23:42
-
-
Save alexandrevicenzi/288539b440e3a6fc4ac9254358726512 to your computer and use it in GitHub Desktop.
Python Tornado: Make email permission required when sign in with Facebook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Read more: | |
# http://blog.alexandrevicenzi.com/required-email-facebook-login.html | |
# | |
import logging | |
import tornado.auth | |
import tornado.ioloop | |
import tornado.web | |
from base64 import b64encode, b64decode | |
from tornado.concurrent import return_future | |
FACEBOOK_ID = 'YOUR ID HERE' | |
FACEBOOK_SECRET = 'YOUR SECRET HERE' | |
FACEBOOK_REDIRECT_URI = 'http://localhost:8888/login' | |
logging.basicConfig(format='%(levelname)s - %(asctime)s - %(module)s: %(message)s', level=logging.DEBUG) | |
logger = logging.getLogger() | |
class FacebookOAuthException(Exception): | |
pass | |
class MainHandler(tornado.web.RequestHandler): | |
def get(self): | |
user = self.get_cookie('user') | |
if user: | |
user = b64decode(user) | |
logger.info('User logged in.') | |
email = self.get_cookie('email') | |
if email: | |
self.write('Hello, %s. Your primary email on Facebook is %s.' % (user, b64decode(email))) | |
else: | |
self.write('Hello, %s. Your account has no email associated.' % user) | |
else: | |
logger.info('User not logged in.') | |
self.redirect('/login') | |
class FacebookLoginHandler(tornado.web.RequestHandler, tornado.auth.FacebookGraphMixin): | |
@tornado.gen.coroutine | |
def get(self, *args, **kwargs): | |
if self.get_argument('code', False): | |
# User logged in. get user data. | |
logger.info('Getting user data...') | |
data = yield self.get_authenticated_user(redirect_uri=FACEBOOK_REDIRECT_URI, | |
client_id=FACEBOOK_ID, | |
client_secret=FACEBOOK_SECRET, | |
code=self.get_argument('code'), | |
extra_fields=['email', 'permissions']) | |
# Check if user allow us to get his email. | |
logger.info('Validating user permissions...') | |
permissions = data.get('permissions', {}).get('data', []) | |
permission = next(p for p in permissions if p.get('permission') == 'email') | |
if permission: | |
granted_access = permission.get('status') == 'granted' | |
else: | |
# Opss! | |
raise FacebookOAuthException('Missing permission from facebook request.') | |
if granted_access: | |
# Everything is OK. move on. | |
logger.info('Access granted.') | |
yield self._on_auth(data) | |
else: | |
# If user don't grant access to email, keep it on facebook login page until he allow it. | |
# We need to pass 'auth_type' as 'rerequest' for this work. | |
logger.info('Access declined. Re-request...') | |
yield self.authorize_redirect(redirect_uri=FACEBOOK_REDIRECT_URI, | |
client_id=FACEBOOK_ID, | |
scope=['email'], | |
extra_params={'auth_type': 'rerequest'}) | |
else: | |
# User not logged in, request authorization. | |
logger.info('Requesting user authorization...') | |
yield self.authorize_redirect(redirect_uri=FACEBOOK_REDIRECT_URI, | |
client_id=FACEBOOK_ID, | |
scope=['email']) | |
@return_future | |
def _on_auth(self, facebook_data, callback): | |
# TODO: Use secure cookie, persist somewhere, but don't do this in prodution. | |
name = facebook_data.get('name') | |
email = facebook_data.get('email') | |
self.set_cookie('user', b64encode(name)) | |
if email: | |
self.set_cookie('email', b64encode(email)) | |
# Logged in. go back to home. | |
self.redirect('/') | |
callback() | |
def make_app(): | |
return tornado.web.Application([ | |
(r'/', MainHandler), | |
(r'/login', FacebookLoginHandler), | |
], debug=True) | |
if __name__ == '__main__': | |
app = make_app() | |
app.listen(8888) | |
logger.info('Server running at http://localhost:8888/') | |
tornado.ioloop.IOLoop.current().start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment