Switch to class-based OAuthUserResponse

Instead of returning an arbirary number of items, we provide a standardized and better documented response.
This commit is contained in:
2017-08-23 17:58:52 +02:00
parent 99866542a1
commit 45275c3831
2 changed files with 35 additions and 24 deletions

View File

@@ -1,10 +1,22 @@
import abc
import attr
import json import json
from rauth import OAuth2Service from rauth import OAuth2Service
from flask import current_app, url_for, request, redirect, session from flask import current_app, url_for, request, redirect, session
class OAuthSignIn: @attr.s
class OAuthUserResponse:
"""Represents user information requested to an OAuth provider after
authenticating.
"""
id = attr.ib(validator=attr.validators.instance_of(str))
email = attr.ib(validator=attr.validators.instance_of(str))
class OAuthSignIn(metaclass=abc.ABCMeta):
providers = None providers = None
def __init__(self, provider_name): def __init__(self, provider_name):
@@ -13,10 +25,22 @@ class OAuthSignIn:
self.consumer_id = credentials['id'] self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret'] self.consumer_secret = credentials['secret']
def authorize(self): @abc.abstractmethod
def authorize(self) -> redirect:
"""Redirect to the corret authorization endpoint for the current provider
Depending on the provider, we sometimes have to specify a different
'scope'.
"""
pass pass
def callback(self): @abc.abstractmethod
def callback(self) -> OAuthUserResponse:
"""Callback performed after authorizing the user
This is usually a request to a protected /me endpoint to query for
user information, such as user id and email address.
"""
pass pass
def get_callback_url(self): def get_callback_url(self):
@@ -72,14 +96,9 @@ class BlenderIdSignIn(OAuthSignIn):
# TODO handle exception for failed oauth or not authorized # TODO handle exception for failed oauth or not authorized
me = oauth_session.get('user').json()
# TODO handle case when user chooses not to disclose en email
session['blender_id_oauth_token'] = oauth_session.access_token session['blender_id_oauth_token'] = oauth_session.access_token
return ( me = oauth_session.get('user').json()
me['id'], return OAuthUserResponse(str(me['id']), me['email'])
me.get('email'),
oauth_session.access_token
)
class FacebookSignIn(OAuthSignIn): class FacebookSignIn(OAuthSignIn):
@@ -115,11 +134,8 @@ class FacebookSignIn(OAuthSignIn):
) )
me = oauth_session.get('me?fields=id,email').json() me = oauth_session.get('me?fields=id,email').json()
# TODO handle case when user chooses not to disclose en email # TODO handle case when user chooses not to disclose en email
return ( # see https://developers.facebook.com/docs/graph-api/reference/user/
me['id'], return OAuthUserResponse(me['id'], me.get('email'))
me.get('email'),
None
)
class GoogleSignIn(OAuthSignIn): class GoogleSignIn(OAuthSignIn):
@@ -154,9 +170,4 @@ class GoogleSignIn(OAuthSignIn):
decoder=decode_json decoder=decode_json
) )
me = oauth_session.get('userinfo').json() me = oauth_session.get('userinfo').json()
# TODO handle case when user chooses not to disclose en email return OAuthUserResponse(str(me['id']), me['email'])
return (
me['id'],
me.get('email'),
None
)

View File

@@ -40,13 +40,13 @@ def oauth_callback(provider):
if not current_user.is_anonymous: if not current_user.is_anonymous:
return redirect(url_for('main.homepage')) return redirect(url_for('main.homepage'))
oauth = OAuthSignIn.get_provider(provider) oauth = OAuthSignIn.get_provider(provider)
social_id, email, access_token = oauth.callback() oauth_user = oauth.callback()
if social_id is None: if oauth_user.id is None:
log.debug('Authentication failed for user with {}'.format(provider)) log.debug('Authentication failed for user with {}'.format(provider))
return redirect(url_for('main.homepage')) return redirect(url_for('main.homepage'))
# Find or create user # Find or create user
user_info = {'id': social_id, 'email': email, 'full_name': ''} user_info = {'id': oauth_user.id, 'email': oauth_user.email, 'full_name': ''}
db_user = find_user_in_db(user_info, provider=provider) db_user = find_user_in_db(user_info, provider=provider)
db_id, status = upsert_user(db_user) db_id, status = upsert_user(db_user)
token = generate_and_store_token(db_id) token = generate_and_store_token(db_id)