Socraticqs2 Social Auth registration¶
Introduction¶
Socraticqs2 users can authorize using SSO.
We a using Python Social Auth library for that process.
Source code available at github.
Available backend(s) now are:
Google OAuth2
Facebook OAuth2
Twitter OAuth
LinkedIn OAuth2
KhanAcademy OAuth1
Configuration Python Social Auth¶
To configure Python Social Auth (PSA) we need to set appropriate KEY/SECRET for available backends on local_conf.py file:
SOCIAL_AUTH_TWITTER_KEY = 'key'
SOCIAL_AUTH_TWITTER_SECRET = 'secret'
SOCIAL_AUTH_FACEBOOK_KEY = 'key'
SOCIAL_AUTH_FACEBOOK_SECRET = 'secret'
SOCIAL_AUTH_LINKEDIN_OAUTH2_KEY = 'key'
SOCIAL_AUTH_LINKEDIN_OAUTH2_SECRET = 'secret'
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'key'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'secret'
SOCIAL_AUTH_KHANACADEMY_OAUTH1_KEY = 'key'
SOCIAL_AUTH_KHANACADEMY_OAUTH1_SECRET = 'secret'
Manuals for backend(s) configuration:
Also we can re-define auth backends on local_conf.py:
AUTHENTICATION_BACKENDS = (
# 'social.backends.twitter.TwitterOAuth',
# 'social.backends.facebook.FacebookOAuth2',
# 'social.backends.google.GoogleOAuth2',
# 'social.backends.linkedin.LinkedinOAuth2',
# 'social.backends.khanacademy.KhanAcademyOAuth1',
# 'social.backends.email.EmailAuth',
'django.contrib.auth.backends.ModelBackend',
)
There are main settings for PSA in settings/default.py:
MIDDLEWARE_CLASSES = (
.............
'ct.middleware.MySocialAuthExceptionMiddleware',
)
INSTALLED_APPS = (
........
# Socials
'social.apps.django_app.default',
'psa',
)
TEMPLATE_CONTEXT_PROCESSORS = (
................
'social.apps.django_app.context_processors.backends',
'social.apps.django_app.context_processors.login_redirect',
)
AUTHENTICATION_BACKENDS = (
'social.backends.twitter.TwitterOAuth',
'social.backends.facebook.FacebookOAuth2',
'social.backends.google.GoogleOAuth2',
'social.backends.linkedin.LinkedinOAuth2',
'social.backends.khanacademy.KhanAcademyOAuth1',
'social.backends.email.EmailAuth',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'psa.pipeline.social_user',
'social.pipeline.user.get_username',
'psa.pipeline.custom_mail_validation',
'psa.pipeline.associate_by_email',
'social.pipeline.user.create_user',
'psa.pipeline.validated_user_details',
'psa.pipeline.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details',
)
SOCIAL_AUTH_DISCONNECT_PIPELINE = (
'social.pipeline.disconnect.allowed_to_disconnect',
'social.pipeline.disconnect.get_entries',
'social.pipeline.disconnect.revoke_tokens',
'social.pipeline.disconnect.disconnect'
)
PROTECTED_USER_FIELDS = ['first_name', 'last_name', 'email']
FORCE_EMAIL_VALIDATION = True
PASSWORDLESS = True
SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION = 'psa.mail.send_validation'
SOCIAL_AUTH_EMAIL_VALIDATION_URL = '/email-sent/'
SOCIAL_AUTH_STRATEGY = 'psa.custom_django_strategy.CustomDjangoStrategy'
SOCIAL_AUTH_STORAGE = 'psa.custom_django_storage.CustomDjangoStorage'
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'
]
# Facebook email scope declaring
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
# Add email to requested authorizations.
SOCIAL_AUTH_LINKEDIN_OAUTH2_SCOPE = ['r_basicprofile', 'r_emailaddress']
# Add the fields so they will be requested from linkedin.
SOCIAL_AUTH_LINKEDIN_OAUTH2_FIELD_SELECTORS = ['email-address', 'headline', 'industry']
# Arrange to add the fields to UserSocialAuth.extra_data
SOCIAL_AUTH_LINKEDIN_OAUTH2_EXTRA_DATA = [('id', 'id'),
('firstName', 'first_name'),
('lastName', 'last_name'),
('emailAddress', 'email_address'),
('headline', 'headline'),
('industry', 'industry')]
About these and many other parameters you can read at the PSA docs.
User Auth flow procedure¶
We have next providers:
- Django user, with primary email
- for STRANGER, when he adds email, and we show pop-up with validation link, do search email associated with social accounts, and if there are similar one, and propose to login on the social auth
- LTI user
- Python social-auth accounts: email and social accounts
Principles: do not merge two social account from same providers
Assumptions: We trust LTI and email providers on email validation they send to us
Social Auth pipelines¶
Module define custom pipeline to handle custom cases
custom_mail_validation - > implement code obj inspect
-
psa.pipeline.
associate_by_email
(backend, details, user=None, *args, **kwargs)¶ Associate current auth with a user with the same email address in the DB.
This pipeline entry is not 100% secure unless you know that the providers enabled enforce email verification on their side, otherwise a user can attempt to take over another user account by using the same (not validated) email address on some provider. This pipeline entry is disabled by default.
-
psa.pipeline.
associate_user
(backend, details, uid, user=None, social=None, *args, **kwargs)¶ Create UserSocialAuth.
-
psa.pipeline.
custom_mail_validation
(strategy, pipeline_index, *args, **kwargs)¶ Email validation pipeline
Verify email or send email with validation link.
-
psa.pipeline.
not_allowed_to_merge
(user, user_social)¶ Check if two users are allowed to merge
Check all social-auth from two users to predict providers intersection.
Merge UserSocialAuth
Re-assign UserSocialAuth objects to given user.
Search for UserSocialAuth.
-
psa.pipeline.
union_merge
(tmp_user, user)¶ Union merge
Merging Roles, UnitStatus, FSMState, Response, StudentError objects.
In Roles merge doing UNION merge to not repeat roles to the same course with the save role. Also we reassigning UnitStatuses, FSMStates, Responses and StudentErrors here.
-
psa.pipeline.
validated_user_details
(strategy, pipeline_index, *args, **kwargs)¶ Merge actions
Make different merge actions based on user type.
Social Auth Models¶
-
class
psa.models.
AnonymEmail
(*args, **kwargs)¶ Temporary anonymous user emails
Model for temporary storing anonymous user emails to allow to restore anonymous sessions.
-
class
psa.models.
SecondaryEmail
(*args, **kwargs)¶ Model for storing secondary emails
We can store emails here from social_auth or LTI login.
-
class
psa.models.
UserSession
(*args, **kwargs)¶ User<->Session model
Model for linking user to session. Solution from http://gavinballard.com/associating-django-users-sessions/
-
psa.models.
user_logged_in_handler
(sender, request, user, **kwargs)¶ Create UserSession object to store User<=>Session relation.
Social Auth Views¶
-
psa.views.
ask_stranger
(request, *args, **kwargs)¶ View to handle stranger whend asking email.
-
psa.views.
context
(**extra)¶ Adding default context to rendered page.
-
psa.views.
custom_login
(request)¶ Custom login to integrate social auth and default login.
-
psa.views.
done
(request, *args, **kwargs)¶ Login complete view, displays user data.
-
psa.views.
set_pass
(request, *args, **kwargs)¶ View to handle password set / change action.
-
psa.views.
validation_sent
(request, *args, **kwargs)¶ View to handle validation_send action.
Custom Django Strategy and Storage¶
We used in Socraticqs2 Social Auth implementation custom Strategy and Storage to move around issue 557.
Module define improved entries
CustomCode -> add user_id attr to handle user generated validation link CustomDjangoStorage -> Storage to use this CustomCode
-
class
psa.custom_django_storage.
CustomCode
(*args, **kwargs)¶ Custom Code object to track user_id through different sessions.
-
class
psa.custom_django_storage.
CustomDjangoStorage
¶ Redefine code field to CustomCode model that add user_id to track.
-
code
¶ alias of
CustomCode
-
Custom Strategy to implement handling user_id attr in Code object
-
class
psa.custom_django_strategy.
CustomDjangoStrategy
(storage, request=None, tpl=None)¶ Custom DjangoStrategy
Needed to add custom login and fix different session issue.
Social auth¶
Stranger clicks on social-auth button on login page¶
https://docs.google.com/drawings/d/1v3fBfb3Y1V1EwJ6Kwt3sTiX0v-1HGb8Me5cWeMIB55k/edit
TEMPORARY user clicks validation link¶
https://docs.google.com/drawings/d/1fnlXom0eFG7pfnkc80iJtfu_vI_28WDHgsD05js0m_c/edit
VALIDATED user clicks on social-auth button¶
https://docs.google.com/drawings/d/1BfVylifaSDsO97OVP2l7Nxfqj4YwYYYajrJTV8KF9ww/edit
Users merge¶
We are VALIDATED user.
When we click to one of social/email buttons to associate with - system search such provider and if found start to analyze possible social/email provider conflicts after possible merge.
This mean that if after merge user would have more than one auth records with the same provider (google for example) we prevent this action with pop-up “warning about intersected providers”.
Temporary user validation¶
We are TEMPORARY user.
We make some progress and click validation link.
After that system will search email provider with email we validating or search django users by primary email.
If found - TEMPORARY user is logged out, start history merging process and new user is logged in.
If we can not find appropriate user via email - we start to modify user detail.
We change username to part before @ in email, remove ‘Temporary user’ full user name. Because of user is currently logged in and has all history we do not doing logout/login and history merge action.
User login process¶
When STRANGER click on social/email auth button on login page system starts login/register process.
It is possible to login at any time by validate social or email (via confirmation link) auth.
If user will set password after login to the system it will be possible to login using username/password method.
When we login using social/email auth system searching for such social auth records.
If found - login user associated with that social auth.