Every now and then users forget their passwords. This is why I prefer using OAuth and social network accounts like GitHub or Twitter. But what do you do when somebody forgets which OAuth provider they used to login to your site? Your website needs a reminder. This is how to implement one if using django-social-auth.
Create a similar view on your Django back-end
def ajax_social_auth_provider_reminder(request):
"""
Remind the user which social auth provider they used to login.
"""
if not request.POST:
return HttpResponse("Not a POST", mimetype='text/plain', status=403)
email = request.POST.get('email', "")
email = email.strip()
if not email or (email.find("@") == -1):
return HttpResponse("Invalid address!", mimetype='text/plain', status=400)
try:
user = User.objects.filter(email=email, is_active=True).only('pk')[0]
except:
return HttpResponse("No user with address '%s' found!" % email, mimetype='text/plain', status=400)
providers = []
for sa in UserSocialAuth.objects.filter(user=user.pk).only('provider'):
providers.append(sa.provider.title())
if len(providers) > 0:
send_templated_mail(
template_name='social_provider_reminder',
from_email='Difio <reminder@dif.io>',
recipient_list=[email],
context={'providers' : providers},
)
return HttpResponse("Reminder sent to '%s'" % email, mimetype='text/plain', status=200)
else:
return HttpResponse("User found but no social providers found!", mimetype='text/plain', status=400)
This example assumes it is called via POST request which contains the email address. All responses are handled at the front-end via JavaScript. If a user with the specified email address exists this address will receive a reminder listing all social auth providers associated with the user account.
On the browser side I like to use Dojo. Here is a simple script which connects to a form and POSTs the data back to the server.
require(["dojo"]);
require(["dijit"]);
function sendReminderForm(){
var form = dojo.byId("reminderForm");
dojo.connect(form, "onsubmit", function(event){
dojo.stopEvent(event);
dijit.byId("dlgForgot").hide();
var xhrArgs = {
form: form,
handleAs: "text",
load: function(data){alert(data);},
error: function(error, ioargs){alert(ioargs.xhr.responseText);}
};
var deferred = dojo.xhrPost(xhrArgs);
});
}
dojo.ready(sendReminderForm);
You can try this out at Difio and let me know how it works for you!
There are comments.
I have been experimenting with the twitter module for Python and decided to combine it with django-social-auth to welcome new users who join Difio. In this post I will show you how to tweet on behalf of the user when they join your site and send them a welcome email.
In django-social-auth the authentication workflow is handled by an operations pipeline where custom functions can be added or default items can be removed to provide custom behavior. This is how our pipeline looks:
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
#'social_auth.backends.pipeline.associate.associate_by_email',
'social_auth.backends.pipeline.user.get_username',
'social_auth.backends.pipeline.user.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.update_user_details',
'myproject.tasks.welcome_new_user'
)
This is the default plus an additional method at the end to welcome new users.
You also have to create and configure a Twitter application so that users can login with Twitter OAuth to your site. RTFM for more information on how to do this.
This is how the custom pipeline action should look:
from urlparse import parse_qs
def welcome_new_user(backend, user, social_user, is_new=False, new_association=False, *args, **kwargs):
"""
Part of SOCIAL_AUTH_PIPELINE. Works with django-social-auth==0.7.21 or newer
@backend - social_auth.backends.twitter.TwitterBackend (or other) object
@user - User (if is_new) or django.utils.functional.SimpleLazyObject (if new_association)
@social_user - UserSocialAuth object
"""
if is_new:
send_welcome_email.delay(user.email, user.first_name)
if backend.name == 'twitter':
if is_new or new_association:
access_token = social_user.extra_data['access_token']
parsed_tokens = parse_qs(access_token)
oauth_token = parsed_tokens['oauth_token'][0]
oauth_secret = parsed_tokens['oauth_token_secret'][0]
tweet_on_join.delay(oauth_token, oauth_secret)
return None
This code works with django-social-auth==0.7.21 or newer. In older versions the
new_association
parameter is missing as
I discovered.
If you use an older version you won't be able to distinguish between newly created
accounts and ones which have associated another OAuth backend. You are warned!
Sending the welcome email is out of the scope of this post. I am using django-templated-email to define how emails look and sending them via Amazon SES. See Email Logging for Django on RedHat OpenShift With Amazon SES for more information on how to configure emailing with SES.
Here is how the Twitter code looks:
import twitter
from celery.task import task
from settings import TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET
@task
def tweet_on_join(oauth_token, oauth_secret):
"""
Tweet when the user is logged in for the first time or
when new Twitter account is associated.
@oauth_token - string
@oauth_secret - string
"""
t = twitter.Twitter(
auth=twitter.OAuth(
oauth_token, oauth_secret,
TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET
)
)
t.statuses.update(status='Started following open source changes at http://www.dif.io!')
This will post a new tweet on behalf of the user, telling everyone they joined your website!
NOTE:
tweet_on_join
and send_welcome_email
are Celery tasks, not ordinary Python
functions. This has the advantage of being able to execute these actions async
and not slow down the user interface.
Are you doing something special when a user joins your website? Please share your comments below. Thanks!
There are comments.
Common functionality for websites is the 'DELETE ACCOUNT' or 'DISABLE ACCOUNT' button. This is how to implement it if using django-social-auth.
delete_objects_for_user(request.user.pk) # optional
UserSocialAuth.objects.filter(user=request.user).delete()
User.objects.filter(pk=request.user.pk).update(is_active=False, email=None)
return HttpResponseRedirect(reverse('django.contrib.auth.views.logout'))
This snippet does the following:
User
object. You could also delete it but mind foreign keys;User
object - if a new user is created after deletion
we don't want duplicated email addresses in the database;There are comments.