Flask-OAuthlib

OAuth is an open standard used as a way for people to log into third party sites through other accounts such as Facebook, Google, Twitter, Linkedin, etc.

I got the idea to have a Facebook or Google log-in through Bento.io (a site I was using to learn web development). As a user, I really liked that creating an account was easy and also that I didn’t have to remember a username and password.

While I was traveling, I decided to work on a project that would allow people to look for other people to play ping pong with. I wanted to manage the user creation and login through OAuth.

I was finally able to get the authorization working by using Flask-OAuthlib. It has Google and Facebook examples on its Github. I did have to start using Python 2.7.

In order to use Oauth for Facebook and Google, you need to register an app and get an ID and SECRET. You use these along with the appropriate urls to create OAuth objects that you can later use to access authorized user information.

# __init__.py

from flask_oauthlib.client import OAuth

google = oauth.remote_app(
	'google',
	consumer_key=app.config.get('GOOGLE_ID'),
	consumer_secret=app.config.get('GOOGLE_SECRET'),
	request_token_params={
	  'scope': 'email'
	},
	base_url='https://www.googleapis.com/oauth2/v1/',
	request_token_url=None,
	access_token_method='POST',
	access_token_url='https://accounts.google.com/o/oauth2/token',
	authorize_url='https://accounts.google.com/o/oauth2/auth',
)

facebook = oauth.remote_app(
	'facebook',
	consumer_key=app.config.get('FACEBOOK_APP_ID'),
	consumer_secret=app.config.get('FACEBOOK_APP_SECRET'),
	request_token_params={'scope': 'email'},
	base_url='https://graph.facebook.com',
	request_token_url=None,
	access_token_url='/oauth/access_token',
	access_token_method='GET',
	authorize_url='https://www.facebook.com/dialog/oauth'
)

When the user clicks on the Google/Facebook Login, they are taken to a Google or Facebook page and are asked if they want to grant the third-party client (my app) permission to access the specified information. In my case, I was just requesting the name and email. If the request goes through fine and permission is granted, the app will be able to access this information which will be stored in the ‘me’ object.

# views.py

from app import app, db, google, facebook, lm

@app.route('/login/<string:server_name>')
def login(server_name):
    if server_name == "Facebook":
        callback = url_for(
            'facebook_authorized',
            next=request.args.get('next')
                or request.referrer 
                or None,
            _external=True
        )
        return facebook.authorize(callback=callback)

    return google.authorize(
        callback=url_for('g_authorized', _external=True)
    )

@app.route('/login/fb_authorized')
def facebook_authorized():
    resp = facebook.authorized_response()
    if resp is None:
        return 'Access denied: reason=%s error=%s' % (
            request.args['error_reason'],
            request.args['error_description']
        )
    if isinstance(resp, OAuthException):
        return 'Access denied: %s' % resp.message

    session['oauth_token'] = (resp['access_token'], '')
    me = facebook.get(
        '/me/?fields=email,name,id,picture.height(200).width(200)'
    )
    return set_user('Facebook', me)

@facebook.tokengetter
def get_facebook_oauth_token():
    return session.get('oauth_token')

@app.route('/login/g_authorized')
def g_authorized():
    resp = google.authorized_response()
    if resp is None:
        return 'Access denied: reason=%s error=%s' % (
            request.args['error_reason'],
            request.args['error_description']
        )
    session['google_token'] = (resp['access_token'], '')
    me = google.get('userinfo')
    return set_user('Google', me)

@google.tokengetter
def get_google_oauth_token():
    return session.get('google_token')

I then use this information to either create a new account or log my user in.

def create_user(me, auth_server_name):  
    if auth_server_name == 'Facebook':
        profile_url = me.data['picture']['data']['url']
    else:
        profile_url = me.data['picture']
    
    new_user = User(
        auth_server=auth_server_name, 
        auth_server_id=me.data['id'],
        name=me.data['name'],
        email=me.data['email'],
        profile_pic=profile_url
    )  

    db.session.add(new_user)
    db.session.commit()
    login_user(new_user, remember=True)
    return new_user


def set_user(server_name, me):
    user = User.query.filter_by(
        auth_server=server_name, 
        auth_server_id=me.data['id']
    ).first()
    if user is None:
        user = create_user(me, server_name)
        return redirect(url_for('set_location'))

    login_user(user, remember=True)
    return redirect(url_for('find_game'))

The full code for my project is on my github.