Step by Step Guide to Email + Social Logins in Django

drf oauth2

Let me explain what we want and how will authentication happen:

What we want:

How will authentication happen:

Before starting , let us go through some prerequisites:

Step 1: Creating Custom User Model Django

python manage.py startapp accounts
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# add this
'accounts',
]
from django.db import models
from django.contrib.auth.models import AbstractBaseUser,PermissionsMixin,BaseUserManager
from django.utils.translation import gettext_lazy as _
class Account(AbstractBaseUser,PermissionsMixin):
email=models.EmailField(unique=True)
username= models.CharField(_('User Name'),max_length=150)
first_name = models.CharField(_('First Name'),max_length=150)
last_name = models.CharField(_('last Name'),max_length=150)
is_staff=models.BooleanField(default=False)
is_active=models.BooleanField(default=True)
objects=CustomAccountManager() USERNAME_FIELD='email'
REQUIRED_FIELDS=['username','first_name']
def __str__(self):
return self.email
class CustomAccountManager(BaseUserManager):
def create_user(self,email,username,first_name,password,**other_fields):
if not email:
raise ValueError(_('Please provide an email address'))
email=self.normalize_email(email)
user=self.model(email=email,username=username,first_name=first_name,**other_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self,email,username,first_name,password,**other_fields):
other_fields.setdefault('is_staff',True)
other_fields.setdefault('is_superuser',True)
other_fields.setdefault('is_active',True)
if other_fields.get('is_staff') is not True:
raise ValueError(_('Please assign is_staff=True for superuser'))
if other_fields.get('is_superuser') is not True:
raise ValueError(_('Please assign is_superuser=True for superuser'))
return self.create_user(email,username,first_name,password,**other_fields)

Here create_user will create regular users while create_superuser is create super users (admin).

create_user

create_superuser

AUTH_USER_MODEL='accounts.Account'
python manage.py makemigrations
python manage.py migrate

Step 2: Making REST API Endpoints for Authentication Django

pip install djangorestframework
pip install django-cors-headers
pip install drf_social_oauth2
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# add these
'rest_framework',
'oauth2_provider',
'social_django',
'drf_social_oauth2',
'corsheaders',
# LOCAL
'accounts',
]
AUTHENTICATION_BACKENDS = (  
'drf_social_oauth2.backends.DjangoOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'drf_social_oauth2.authentication.SocialAuthentication',
)
}
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000"
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# add these
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# add these
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
},
},
]
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
# add these
path('api-auth/', include('rest_framework.urls')),
path('api-auth/', include('drf_social_oauth2.urls',namespace='drf')),#???? namespace='drf' is mandatory
]

After adding this and that as required let’s start coding again.

from rest_framework import serializers
from .models import Account
class RegistrationSerializer(serializers.ModelSerializer):
class Meta:
model=Account
fields=('email','username','password','first_name')
extra_kwargs={'password':{'write_only':True}}
def create(self,validated_data):
password=validated_data.pop('password',None)
instance=self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework import status,generics
from rest_framework.response import Response
from .serializers import RegistrationSerializer
from rest_framework import permissions
from .models import Account
class CreateAccount(APIView):
permission_classes=[permissions.AllowAny]
def post(self,request):
reg_serializer=RegistrationSerializer(data=request.data)
if reg_serializer.is_valid():
new_user=reg_serializer.save()
if new_user:
return Response(status=status.HTTP_201_CREATED)
return Response(reg_serializer.errors,status=status.HTTP_400_BAD_REQUEST)
from django.urls import path
from .views import CreateAccount
app_name = 'users'urlpatterns = [
path('create/', CreateAccount.as_view(), name="create_user"),]
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('api-auth/', include('drf_social_oauth2.urls',namespace='drf')),
#add this
path('api-auth/', include('accounts.urls'))
]
python manage.py runserver

Step 3 : Creating & Logging in user with email password Postman

import requests # add thisclass CreateAccount(APIView):
permission_classes=[permissions.AllowAny]
def post(self,request):
reg_serializer=RegistrationSerializer(data=request.data)
if reg_serializer.is_valid():
new_user=reg_serializer.save()
if new_user:
#add these
r=requests.post('http://127.0.0.1:8000/api-auth/token', data = {
'username':new_user.email,
'password':request.data['password'],
'client_id':'Your Client ID',
'client_secret':'Your Client Secret',
'grant_type':'password'
})
return Response(r.json(),status=status.HTTP_201_CREATED)
return Response(reg_serializer.errors,status=status.HTTP_400_BAD_REQUEST)

Step 4 : Authenticated request demonstration preparation Django

class UsersSerializer(serializers.ModelSerializer):
class Meta:
model=Account
fields=('email','username','first_name')
from rest_framework import status,genericsclass AllUsers(generics.ListAPIView):
permission_classes=[permissions.AllowAny]
queryset=Account.objects.all()
serializer_class=UsersSerializer
class CurrentUser(APIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
serializer = UsersSerializer(self.request.user)
return Response(serializer.data)
from django.urls import path
from .views import CreateAccount,AllUsers,CurrentUser
app_name = 'users'urlpatterns = [
path('create/', CreateAccount.as_view(), name="create_user"),
path('all/', AllUsers.as_view(), name="all"),
path('currentUser/', CurrentUser.as_view(), name="current"),
]

Step 5 : Authenticated request demonstration Postman

Step 6 : Facebook and Google Login Django

AUTHENTICATION_BACKENDS = (  
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.facebook.FacebookAppOAuth2',
'social_core.backends.facebook.FacebookOAuth2',

'drf_social_oauth2.backends.DjangoOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
# Facebook configuration
SOCIAL_AUTH_FACEBOOK_KEY = 'your facebook key'
SOCIAL_AUTH_FACEBOOK_SECRET = 'your facebook secret'
# Define SOCIAL_AUTH_FACEBOOK_SCOPE to get extra permissions from Facebook.
# Email is not sent by default, to get it, you must request the email permission.
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id, name, email'
}
SOCIAL_AUTH_USER_FIELDS=['email','first_name','username','password']
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = "your google oauth2 key"
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = "your google oauth2 secret"
# Define SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE to get extra permissions from Google.
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
]

Thats it we are done with django🙌

Step 7 : Google and Facebook Login on React

import ReactFacebookLogin from "react-facebook-login";
import ReactGoogleLogin from "react-google-login";
import { facebookLogin, googleLogin } from "../axios";# I'll create this later
export default function LogIn() {

function responseFb(response) {
console.log(response);
facebookLogin(response.accessToken);
}
function responseGoogle(response) {
console.log(response);
googleLogin(response.accessToken);
}

return (
<>
<ReactFacebookLogin
appId="Your App Id"
fields="name,email"
callback={responseFb}
/>
<ReactGoogleLogin
clientId="your google client id"
buttonText="Login"
onSuccess={responseGoogle}
onFailure={responseGoogle}
cookiePolicy={"single_host_origin"}
/>
</>
);
}
export function facebookLogin(accessToken) {
axios
.post(`http://127.0.0.1:8000/api-auth/convert-token`, {
token: accessToken,
backend: "facebook",
grant_type: "convert_token",
client_id: "your client id",
client_secret:"your client secret ",
})
.then((res) => {
// Save somewhere these access and refresh tokens
console.log(res.data);
});
}
export function googleLogin(accessToken) {
axios
.post(`http://127.0.0.1:8000/api-auth/convert-token`, {
token: accessToken,
backend: "google-oauth2",
grant_type: "convert_token",
client_id: "your client id",
client_secret: "your client secret",
})
.then((res) => {
// Save somewhere these access and refresh tokens
console.log(res.data);
});
}

That’s It !!! We are done with Authentication

axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async function (error) {
const originalRequest = error;
console.log(originalRequest);
if (typeof error.response === "undefined") {
alert("a server error happNeD, we will fix it shortly");
return Promise.reject(error);
}
if (
error.response.status === 401 &&
!localStorage.getItem("refresh_token")
) {
window.location.href = "/login/";
return Promise.reject(error);
}
if (
error.response.status === 401 &&
error.response.statusText === "Unauthorized" &&
localStorage.getItem("refresh_token") !== undefined
) {
const refreshToken = localStorage.getItem("refresh_token");
return axios
.post("http://127.0.0.1:8000/api-auth/token", {
client_id: "Your client id ",
client_secret:
"Your client secret",
grant_type: "refresh_token",
refresh_token: refreshToken,
})
.then((response) => {
localStorage.setItem("access_token", response.data.access_token);
localStorage.setItem("refresh_token", response.data.refresh_token);
window.location.reload();
axiosInstance.defaults.headers["Authorization"] =
"Bearer " + response.data.access_token;
})
.catch((err) => console.log(err));
}
}
);

Hope this helps 🤞

Interested in frontend developement & UI design

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store