NextJS/Single Sign On(SSO)/

How to configure GitHub SSO in Django Rest Framework with Next.js?

Published on

How to configure GitHub SSO in Django Rest Framework with Next.js?
Single Sign On (SSO) gives your users convenient but secure access to all their web applications with a single set of credentials. We will learn how to configure GitHub SSO(Social Login) in the Django Rest Framework backend with a Next.js frontend (Next-Auth app).

Introduction :

Django (Django rest Framework):

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs.Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django. To know more about relative links click here

Next js :

Next.js is a React framework. Next.js is constitutionally an excellent tool to achieve great SEO performance. building super fast static websites that behave dynamically. Next.js when building UI and UX there is flexibility. and the important great community support. To know more about relative links click here

NextAuth.js:

NextAuth. js is a completely secured authentication solution for implementing authentication in Next. js applications. It is a flexible authentication library designed to sync with any OAuth service, with full support for passwordless sign-in.To know more about relative links click here

Single sign-on(SSO):

Single sign-on (SSO) is a time-saving and highly secure user authentication process. SSO lets users access multiple applications with a single account and sign out instantly with one click.

To create a Github client Id and client Secret

Check out this tutorial, to create a client Id and client secret.

 

How it Works:



work flow

Flow Diagram:



flow-diagram

Prerequisites:

  • Python

  • Django REST Framework

  • Node

  • Next js Library

  • NextAuth.js

Steps:

Step 1: Create a GitHub client Id and client Secret

Check out this tutorial, to create a client Id and client secret.

Step 2: Set up a Next.js project

Install Nodejs

Before we create Next App we need to install node.js.

if you already installed node.js please ignore this step 2 and go to step 3

Step 3: Create a new Next.js app for the Single sign-on process “frontend”

After installing Node.js open the terminal or command prompt and create the next.js app with the following commands

1npx create-next-app frontend

folder structue

Step 4: Configure the Next.js project to establish a connection with Github

After creating the Nextjs app then install next-auth commands.

1npm i next-auth
  • In your project, go to your /pages/api/auth directory and create a file called [...nextauth.js]. This means that all of the routes starting with /api/auth will be handled by the [...nextauth].js file.

  • In /pages/api/[...nextauth].js, write the following piece of code:

1import NextAuth from "next-auth"; 2import GitHubProvider from "next-auth/providers/github"; 3 4var user_credential = [] 5export default NextAuth({ 6 providers: [ 7 GitHubProvider({ 8 clientId: "Github client ID", 9 clientSecret: "Github Secret", 10 }), 11 ], 12 callbacks: { 13 async jwt(token, user, account, profile, isNewUser) { 14 var user_token = token.token.account 15 16 return token; 17 }, 18 async session({ session, token, user }) { 19 user_credential = { 20 "provider": "github", 21 } 22 if (token.token.account.access_token) { 23 user_credential["auth_token"] = token.token.account.access_token 24 } 25 if (token.token.account.id_token) { 26 user_credential["auth_token"] = token.token.account.id_token 27 } 28 29 return user_credential 30 } 31 }, 32});

Sample code for the /pages/api/[...nextauth].js file can be found in the following Github URL.

  • To make use of Next-auth in the app, please update the pages/_app.js file with the below snippet.

1import { SessionProvider } from "next-auth/react" 2 3export default function App({ 4 Component, 5 pageProps: { session, ...pageProps }, 6}) { 7 return ( 8 <SessionProvider session={session}> 9 <Component {...pageProps} /> 10 </SessionProvider> 11 ) 12}

Sample code for the pages/_app.js file can be found in the following Github URL.

 

  • With Next-auth added to our application, we can now build our sign-in page. First, we will set up our page to display the Sign in to GitHub button if the user is not authenticated, else it returns our application. To do this, we modify the index.js file as shown below:

    1import styles from '../styles/Home.module.css' 2import React, { useEffect } from "react"; 3 4import { useSession, signIn } from "next-auth/react" 5import Image from 'next/image'; 6 7export default function Home({ providers }) { 8 const { data: session, loading } = useSession() 9 useEffect(() => { 10 if (session) { 11 if (session.provider === "github") { 12 var auth_token = session.auth_token 13 backendapi(auth_token) 14 } 15 16 } 17 18 }, [session]) 19 function backendapi(auth_token) { 20 fetch(`http://127.0.0.1:8000/github/`, { 21 method: "post", 22 headers: { 23 "Content-Type": "application/json", 24 }, 25 body: JSON.stringify({ "auth_token": auth_token }), 26 }).then((data) => data.json()) 27 .then((res) => { 28 if (res.tokens) { 29 document.getElementById("email_id").innerText = res.email 30 document.getElementById("token").innerText = res.tokens 31 } 32 }) 33 } 34 return ( 35 <> 36 37 <div id='github-login-btn'> 38 <div type="submit" id='githublogin' onClick={() => signIn("github")}> 39 <div className='github-button'> 40 <h1> 41 <svg aria-hidden="true" class="octicon-octicon-mark-github " height="30" version="1.1" viewBox="0 0 16 16" width="30"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg> 42 </h1> 43 <h1>Sign in to GitHub</h1> 44 </div> 45 </div> 46 </div> 47 <div className='new_text'> 48 49 <div > 50 <label>Email Id :</label> 51 <label id='email_id'></label> 52 </div> 53 <div > 54 <label>Auth token :</label> 55 <label id='token'></label> 56 </div> 57 </div> 58 </> 59 ) 60}

    Sample code for the index.js file can be found in the following Github URL.

 

  • You can run your app via CLI with the following command and view it in your browser:

    1npm run dev

 

frontend results


Optional Step:

NOTE *:

The Below configuration is not mandatory for this example. When deploying to production, set the NEXTAUTH_URL environment variable to the canonical URL of your site.

  1. Create .env file

1cd <project_home_directory> 2touch .env 3 4For example: 5cd /home/ubuntu/project/nextjs/ 6touch .env 7 8
  1. Update the .env file with the following content:

1NEXTAUTH_URL=<Domain_name> 2 3For example: 4NEXTAUTH_URL=<Domain_name>

Step 5: Set up a Django project.

I: Install Python

Python3.6 or above is needed in order to create a Django project, therefore please install python and proceed to the next step.

2: Install Django:

Install the Django framework using the following command.

1pip install django

3: Create a Django Project:

Create a Django project using the below command.

1django-admin startproject backend

Step 6: Create a Django App:

After creating the Django project, then create the Django app

1python manage.py startapp accounts

A sample screenshot of Django project files is shown below.


Screenshot1

Step 7: Configure the Django project to establish a connection with Github.

1: Install djangorestframework:

In this blog, we are using the Django Rest framework python library to create APIs, therefore the please install same to proceed further.

1pip install djangorestframework

2: Install django-cors-headers:

django-cors-headers library needs to establish a connection from the next.js frontend to Django API, hence please install the same using the below command.

1pip install django-cors-headers
  • In backend/settings.py, add the following piece of code:

1GITHUB_CLIENT_ID = <CLIENT_ID> 2SOCIAL_SECRET = <SOCIAL_SECRET>
1CORS_ORIGIN_ALLOW_ALL = True
1MIDDLEWARE = [ 2 'django.middleware.security.SecurityMiddleware', 3 'django.contrib.sessions.middleware.SessionMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.clickjacking.XFrameOptionsMiddleware', 9 'corsheaders.middleware.CorsMiddleware', # <<< newly added line 10]

Sample code for the /accounts/settings.py can be found in the following Github URL.

 

  • In your project, go to your /accounts directory and create a file called serializers.py.



Screenshot2

In /accounts/serializers.py, add the following code.

1from django.conf import settings 2from rest_framework import serializers 3from library.sociallib import github 4from library.register.register import register_social_user 5from rest_framework.exceptions import* 6 7class GithubSocialAuthSerializer(serializers.Serializer): 8 """Handles serialization of facebook related data""" 9 auth_token = serializers.CharField() 10 11 def validate_auth_token(self, auth_token): 12 user_data = github.Github.validate(auth_token) 13 14 try: 15 email = user_data['email'] 16 provider = 'github' 17 except: 18 raise serializers.ValidationError( 19 'The token is invalid or expired. Please login again.' 20 ) 21 return register_social_user( 22 provider=provider, user_id=None, email=email, name=None)

Sample code for the /accounts/serializers.py can be found in the following Github URL.

 

  • Update the accounts/views.py file with the following code.

1from rest_framework.generics import GenericAPIView 2from .serializers import* 3from rest_framework.response import Response 4from rest_framework import status 5from rest_framework.permissions import AllowAny 6from rest_framework.decorators import permission_classes 7 8 9@permission_classes((AllowAny, )) 10class GithubSocialAuthView(GenericAPIView): 11 12 serializer_class = GithubSocialAuthSerializer 13 14 def post(self, request): 15 """ 16 POST with "auth_token" 17 Send an access token as from github to get user information 18 """ 19 20 serializer = self.serializer_class(data=request.data) 21 serializer.is_valid(raise_exception=True) 22 data = ((serializer.validated_data)['auth_token']) 23 return Response(data, status=status.HTTP_200_OK) 24

Sample code for the /accounts/views.py can be found in the following Github URL.

 

  • Create the sociallib and register directory in the following hierarchy.

    library → socaillib

    library → register.

  • After that, please create a github.py the file inside the sociallib directory



Screenshot3

In library/sociallib/github.py, add the following snippet.

1from django.conf import settings 2import urllib.request as requests 3import json 4 5 6class Github: 7 """ 8 Github class to fetch the user info and return it 9 """ 10 @staticmethod 11 def validate(access_token): 12 """ 13 validate method Queries the github url to fetch the user info 14 """ 15 try: 16 headers = { 17 "Authorization": f"token {access_token}", 18 "content-type": "application/json", 19 "Access-Control-Expose-Headers": "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval" 20 } 21 user_info_url = "https://api.github.com/user/emails" 22 req = requests.Request(user_info_url, headers=headers) 23 response = requests.urlopen(req) 24 response = response.read() 25 data = response.decode('utf-8') 26 user_info = json.loads(data) 27 return user_info[0] 28 except: 29 return "The token is either invalid or has expired" 30

Sample code for the library/sociallib/github.py file can be found in the following Github URL.

  • Navigate to the library/register directory and create a file called register.py



Screenshot5

In library/register/register.py, add the following code.

1from rest_framework.authtoken.models import Token 2 3from accounts.models import User 4from django.conf import settings 5from rest_framework.exceptions import AuthenticationFailed 6 7 8def register_social_user(provider, user_id, email, name): 9 filtered_user_by_email = User.objects.filter(email=email) 10 11 if filtered_user_by_email.exists(): 12 if provider == filtered_user_by_email[0].auth_provider: 13 new_user = User.objects.get(email=email) 14 15 registered_user = User.objects.get(email=email) 16 registered_user.check_password(settings.SOCIAL_SECRET) 17 18 Token.objects.filter(user=registered_user).delete() 19 Token.objects.create(user=registered_user) 20 new_token = list(Token.objects.filter( 21 user_id=registered_user).values("key")) 22 23 return { 24 'username': registered_user.username, 25 'email': registered_user.email, 26 'tokens': str(new_token[0]['key'])} 27 28 else: 29 raise AuthenticationFailed( 30 detail='Please continue your login using ' + filtered_user_by_email[0].auth_provider) 31 32 else: 33 user = { 34 'username': email, 'email': email, 35 'password': settings.SOCIAL_SECRET 36 } 37 user = User.objects.create_user(**user) 38 user.is_active = True 39 user.auth_provider = provider 40 user.save() 41 new_user = User.objects.get(email=email) 42 new_user.check_password(settings.SOCIAL_SECRET) 43 Token.objects.create(user=new_user) 44 new_token = list(Token.objects.filter(user_id=new_user).values("key")) 45 return { 46 'email': new_user.email, 47 'username': new_user.username, 48 'tokens': str(new_token[0]['key']), 49 } 50

Sample code for the library/register/register.py file can be found in the following Github URL.

 

  • Add the accounts/urls.py file with the following content.

1from django.urls import path 2from .views import* 3 4urlpatterns = [ 5 path('github/', GithubSocialAuthView.as_view()), 6] 7

An example screenshot of the Django project is shown below.


Screenshot6


The sample code for the accounts/urls.py file can be found in the following Github URL.

 

  • Finally, Run the Django application using the following command.

1 python manage.py runserver

 

A Sample Github repo, with all the required configurations, is given below.

Result:

After finishing all the steps mentioned above, please go to the web browser, and try to access the next.js application (i.e http://localhost:3000). An example output screenshot is given below.

 

result

Comments