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:
@@ -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
|
|
||||||
)
|
|
||||||
|
@@ -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)
|
||||||
|
Reference in New Issue
Block a user