NextJS/Single Sign On(SSO)/
2022-10-17T08:46:29.730408Z
Published on
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 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 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) 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.
Python
Django REST Framework
google-auth
Node
Next.js
NextAuth.js
Check out this tutorial, to create a client Id and client secret.
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
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
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
, add the following code.
1import NextAuth from "next-auth";
2import GoogleProvider from "next-auth/providers/google"
3
4var user_credential = []
5
6export default NextAuth({
7 providers: [
8 GoogleProvider({
9 clientId: process.env.GOOGLE_ID,
10 clientSecret: process.env.GOOGLE_SECRET,
11 }),
12 ],
13 callbacks: {
14 async jwt(token, user, account, profile, isNewUser) {
15 var user_token = token.token.account
16 return token;
17 },
18 async session({ session, token, user }) {
19 user_credential = {
20 "provider": token.token.account.provider,
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 return user_credential
29 }
30 },
31});
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.
google_sso_django_nextjs_example/_app.js at main · episyche/google_sso_django_nextjs_example
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 with google
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 === "google") {
12 var auth_token = session.auth_token
13 backendapi(auth_token)
14 }
15
16 }
17
18 }, [session])
19 function backendapi(auth_token) {
20 // var tag = document.getElementById("user_token").innerHTML = auth_token
21 fetch(`http://127.0.0.1:8000/accounts/google/`, {
22 method: "post",
23 headers: {
24 "Content-Type": "application/json",
25 },
26 body: JSON.stringify({ "auth_token": auth_token }),
27 }).then((data) => data.json())
28 .then((res) => {
29 if (res.tokens) {
30 document.getElementById("email_id").innerText = res.email
31 document.getElementById("token").innerText = res.tokens
32 }
33 })
34 }
35 return (
36 <>
37
38 <div id='google-login-btn'>
39 <div
40 type="submit"
41 id='googlelogin'
42 onClick={() => signIn("google")}
43 >
44 <link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
45
46 <div class="google-btn">
47 <div class="google-icon-wrapper">
48 <Image class="google-icon" src="/Google__G__Logo (1).svg" layout='fill' />
49 </div>
50 <p class="btn-text"><b>Sign in with google</b></p>
51 </div>
52 </div>
53 <h6 id='user_token'></h6>
54 <div>
55 <label>Email Id :</label>
56 <label id='email_id'></label>
57 </div>
58 <div >
59 <label>Auth token :</label>
60 <label id='token'></label>
61 </div>
62 </div>
63 </>
64 )
65}
66
Sample code for the index.js
file can be found in the following Github URL.
google_sso_django_nextjs_example/index.js at main · episyche/google_sso_django_nextjs_example
You can run your app via CLI with the following command and view it in your browser:
1npm run dev
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.
Create .env file
1cd <project_home_directory>
2touch .env
3
4For example:
5cd /home/ubuntu/project/nextjs/
6touch .env
7
Update the .env file with the following content:
1NEXTAUTH_URL=<Domain_name>
2
3For example:
4NEXTAUTH_URL=<Domain_name>
Python3.6 or above is needed in order to create a Django project, therefore please install python and proceed to the next step.
Install the Django framework using the following command.
1pip install django
Create a Django project using the below command.
1django-admin startproject backend
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.
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
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
google-api-python-client
python library is needed for Django to connect with google API. Therefore, please install the same using the following command.
1pip install google-api-python-client
In backend/settings.py
, add the following piece of code:
1GOOGLE_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 backend/settings.py
file can be found in the following Github URL.
google_sso_django_nextjs_example/settings.py at main · episyche/google_sso_django_nextjs_example
In your project, go to your /accounts
directory and create a file called serializers.py
.
In /accounts/serializers.py
, add the following code.
1from django.conf import settings
2from rest_framework import serializers
3from library.sociallib import google
4from library.register.register import register_social_user
5from rest_framework.exceptions import AuthenticationFailed
6
7class GoogleSocialAuthSerializer(serializers.Serializer):
8 auth_token = serializers.CharField()
9
10 def validate_auth_token(self, auth_token):
11 user_data = google.Google.validate(auth_token)
12 try:
13 user_data['sub']
14 except:
15 raise serializers.ValidationError(
16 'The token is invalid or expired. Please login again.'
17 )
18 print(user_data['aud'])
19 if user_data['aud'] != settings.GOOGLE_CLIENT_ID:
20
21 raise AuthenticationFailed('oops, who are you?')
22
23 user_id = user_data['sub']
24 email = user_data['email']
25 name = user_data['name']
26 provider = 'google'
27
28 return register_social_user(
29 provider=provider, user_id=user_id, email=email, name=name)
Sample code for the /accounts/serializers.py
can be found in the following Github URL.
google_sso_django_nextjs_example/serializers.py at main · episyche/google_sso_django_nextjs_example
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 GoogleSocialAuthView(GenericAPIView):
11
12 serializer_class = GoogleSocialAuthSerializer
13
14 def post(self, request):
15 """
16 POST with "auth_token"
17 Send an idtoken as from google 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)
Sample code for the /accounts/views.py
can be found in the following Github URL.
google_sso_django_nextjs_example/views.py at main · episyche/google_sso_django_nextjs_example
Create the sociallib and register directory in the following hierarchy.
library → socaillib
library → register.
After that, please create a google.py
file inside the sociallib directory
In library/sociallib/google.py
, add the following snippet.
1from google.auth.transport import requests
2from google.oauth2 import id_token
3
4
5class Google:
6 """Google class to fetch the user info and return it"""
7
8 @staticmethod
9 def validate(auth_token):
10 """
11 validate method Queries the Google oAUTH2 api to fetch the user info
12 """
13 try:
14 idinfo = id_token.verify_oauth2_token(
15 auth_token, requests.Request())
16
17 if 'accounts.google.com' in idinfo['iss']:
18 return idinfo
19
20 except:
21 return "The token is either invalid or has expired"
Sample code for the library/sociallib/google.py
file can be found in the following Github URL.
google_sso_django_nextjs_example/google.py at main · episyche/google_sso_django_nextjs_example
Navigate to the library/register
directory and create a file called register.py
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 }
Sample code for the library/register/register.py
file can be found in the following Github URL.
google_sso_django_nextjs_example/register.py at main · episyche/google_sso_django_nextjs_example
Add the accounts/urls.py
file with the following content.
1from django.urls import path
2from .views import*
3
4urlpatterns = [
5 path('google/', GoogleSocialAuthView.as_view()),
6]
An example screenshot of the Django project is shown below.
Sample code for the accounts/urls.py
file can be found in the following Github URL.
google_sso_django_nextjs_example/urls.py at main · episyche/google_sso_django_nextjs_example
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.
GitHub - episyche/google_sso_django_nextjs_example
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.
Comments
interested topics