Last active
April 20, 2016 16:34
-
-
Save harshvladha/31ef0e09bb7619a0d6d0914ccc385abf to your computer and use it in GitHub Desktop.
Simple "Facebook-Login" flow for django - without using any extra app (look last - settings.py file for details)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.http import HttpResponse | |
from django.views.generic import View | |
from django.views.generic.base import TemplateView, RedirectView | |
from {$APP_NAME}.models import UserProfile, OAuthProvider | |
from django.contrib.auth import authenticate, login, logout | |
from django.contrib.auth.models import User | |
import requests | |
BASE_URL = "http://straightmart.com/" | |
class FacebookLoginRedirect(RedirectView): | |
provider = None | |
permanent = False | |
query_string = True | |
response_type = 'code' | |
client_id = None | |
oauth_url = None | |
redirect_uri = BASE_URL + '/login/callback' | |
url = None | |
def get_redirect_url(self, *args, **kwargs): | |
self.provider = OAuthProvider.objects.filter(name='facebook')[0] | |
self.client_id = self.provider.app_id | |
self.oauth_url = self.provider.oauth_url | |
self.url = self.oauth_url+'?client_id='+self.client_id+'&redirect_uri='+self.redirect_uri | |
return super(FacebookLoginRedirect, self).get_redirect_url(*args, **kwargs) | |
class FacebookLoginCallback(RedirectView): | |
provider = None | |
permanent = False | |
query_string = False | |
code = None | |
fb_error = False | |
client_id = None | |
client_secret = None | |
access_token_url = None | |
debug_url = None | |
api_url = None | |
profile_url = None | |
user_access_token = None | |
request_uri = BASE_URL + '/login/callback' | |
url = BASE_URL + '/home' | |
fb_uid = None | |
def get_redirect_url(self, *args, **kwargs): | |
self.provider = OAuthProvider.objects.filter(name='facebook')[0] | |
self.client_id = self.provider.app_id | |
self.client_secret = self.provider.app_secret | |
self.access_token_url = self.provider.access_token_url | |
self.debug_url = self.provider.debug_token_url | |
self.api_url = self.provider.api_url | |
self.profile_url = self.provider.profile_url | |
if 'code' in self.request.GET: | |
self.code = self.request.GET.get('code') | |
elif 'error' in self.request.GET: | |
self.fb_error = True | |
if not self.fb_error: | |
self.get_user_access_token() | |
if self.inspect_access_token(): | |
self.login_user() | |
return super(FacebookLoginCallback, self).get_redirect_url(*args, **kwargs) | |
def get_user_access_token(self): | |
params = dict(client_id=self.client_id, client_secret=self.client_secret, redirect_uri=self.request_uri, code=self.code) | |
r = requests.get(self.access_token_url, params=params) | |
data = r.json() | |
self.user_access_token = data['access_token'] | |
def inspect_access_token(self): | |
admin = UserProfile.objects.filter(user_id=1)[0] | |
params = dict(input_token=self.user_access_token, access_token=admin.fb_access_token) | |
r = requests.get(self.debug_url, params=params) | |
data = r.json() | |
data = data['data'] | |
if data['is_valid']: | |
self.fb_uid = data['user_id'] | |
return data['is_valid'] | |
def login_user(self): | |
user_profile = None | |
try: | |
# check if user already registered, then get that object | |
user_profile = UserProfile.objects.get(fb_uid=self.fb_uid) | |
except UserProfile.DoesNotExist: | |
# sign up the user over here | |
# check permissions granted in the access_token we've got | |
r_url = self.profile_url+'/permissions' | |
params = dict(access_token=self.user_access_token, client_id=self.provider.app_id, client_secret=self.provider.app_secret) | |
r = requests.get(r_url, params) | |
data = r.json() | |
data = data['data'] | |
# create new params for getting user profile | |
params = dict(fields='first_name, last_name, email', access_token=self.user_access_token, client_id=self.provider.app_id, client_secret=self.provider.app_secret) | |
for p in data: | |
if p['permission'] == 'email' and p['status'] != 'granted': | |
params['fields'] = 'first_name, last_name' | |
break | |
# get user profile from facebook | |
r = requests.get(self.profile_url, params=params) | |
data = r.json() | |
# create new user in the database User Object | |
new_user = User(first_name=data['first_name'], last_name=data['last_name']) | |
if 'email' in data: | |
new_user.email = data['email'] | |
new_user.username = data['email'].split('@')[0] | |
new_user.save() | |
# create UserProfile object in database | |
user_profile = UserProfile(user=new_user, fb_uid=self.fb_uid) | |
user_profile.save() | |
# get long lived access token when user is about to log in | |
params = dict(fb_exchange_token=self.user_access_token, grant_type='fb_exchange_token', client_id=self.provider.app_id, client_secret=self.provider.app_secret) | |
r = requests.get('https://graph.facebook.com/oauth/access_token', params=params) | |
data = r.content.decode('utf-8').split('=')[1] | |
data = data.split('&')[0] | |
self.user_access_token = data | |
user_profile.fb_access_token = self.user_access_token | |
user_profile.save() | |
user = user_profile.user | |
user = authenticate(username=user.username, type='oauth') | |
if user: | |
if user.is_active: | |
login(self.request, user) | |
class FacebookLoginLogout(RedirectView): | |
url = BASE_URL + '/index/' | |
permanent = False | |
def get_redirect_url(self, *args, **kwargs): | |
logout(self.request) | |
return super(FacebookLoginLogout, self).get_redirect_url(*args, **kwargs) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
from django.template.defaultfilters import slugify | |
from django.contrib.auth.models import User | |
class UserProfile(models.Model): | |
user = models.OneToOneField(User) | |
def _get_full_name(self): | |
return '%s %s' % (self.user.first_name, self.user.last_name) | |
full_name = property(_get_full_name) | |
location = models.ForeignKey(Location, null=True) | |
institute = models.ForeignKey(Institute, null=True) | |
fb_access_token = models.CharField(max_length=250, null=True, blank=True) | |
mobile = models.BigIntegerField(null=True,blank=True) | |
rating = models.SmallIntegerField(default=3) | |
verified = models.BooleanField(default=False) | |
date_of_birth = models.DateField(auto_now=False, auto_now_add=False, blank=True, null=True) | |
fb_uid = models.CharField(max_length=50, unique=True, blank=True, null=True) | |
def __str__(self): | |
return self.full_name | |
class OAuthProvider(models.Model): | |
name = models.CharField(max_length=20) | |
oauth_url = models.URLField() | |
access_token_url = models.URLField() | |
profile_url = models.URLField() | |
api_url = models.URLField() | |
debug_token_url = models.URLField() | |
app_id = models.CharField(max_length=50) | |
app_secret = models.CharField(max_length=100) | |
def __str__(self): | |
return self.name |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib.auth.backends import ModelBackend | |
from django.contrib.auth.models import User | |
class PasswordlessAuthBackend(ModelBackend): | |
""" | |
Login to the system without providing a password | |
""" | |
def authenticate(self, username=None, **kwargs): | |
if kwargs['type'] == 'oauth': | |
try: | |
return User.objects.get(username=username) | |
except User.DoesNotExist: | |
return None | |
else: | |
return None | |
def get_user(self, user_id): | |
try: | |
return User.objects.get(pk=user_id) | |
except User.DoesNotExist: | |
return None | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
This is a small section of settings.py | |
What should be your Project Structure be like (According to this model) : | |
You have a basic or say "main" django-app in which you need to implement Facebook Login. | |
../{$PROJECT_NAME}/settings.py | |
../{$APP_NAME}/oauth_backend.py | |
../{$APP_NAME}/models.py | |
Note : Replace every instance of $PROJECT_NAME and $APP_NAME with your django project name and django app name. | |
''' | |
AUTHENTICATION_BACKENDS = ( | |
# Default backend | |
'django.contrib.auth.backends.ModelBackend', | |
'{$APP_NAME}.oauth_backend.PasswordlessAuthBackend' | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment