Este artículo gira en torno a un proyecto Django. Incluye integración de Email + Social Login en cualquier proyecto de Django. Hemos utilizado React como interfaz para demostrar el funcionamiento del proyecto. Puede usar su propia interfaz para int
Requisitos –
- Necesitamos registrar un usuario, necesitamos pasar nombre de usuario, correo electrónico, nombre, contraseña (debido a nuestro modelo).
- Necesitamos iniciar sesión en ese usuario y autenticar al usuario.
- El usuario debe poder iniciar sesión con múltiples plataformas sociales.
- Necesitamos hacer API para todos los requisitos.
Cómo ocurrirá la autenticación:
Antes de comenzar, debemos saber que enviaremos requests autenticadas mediante el envío de un token de portador en los encabezados de autorización de la solicitud. Este token le indicará al servidor qué usuario envió la solicitud . Para eso haremos esto:
- Enviaremos el nombre de usuario y la contraseña a un punto final para obtener el token y esto servirá como nuestro inicio de sesión
- A cambio, obtendremos el token de acceso y el token de actualización, estableceremos el encabezado de autorización de todas las requests para este token de acceso.
- Sin embargo, el token de acceso caducará después de un breve período de tiempo, luego enviaremos el token de actualización al punto final para obtener un nuevo token de acceso.
- Por lo tanto, repetiremos los pasos 2 y 3 internamente sin que el usuario sepa cuándo expira el token de acceso.
Antes de comenzar, repasemos algunos requisitos previos:
- Conocimientos básicos de Django – Tutorial de Django
- Cree un proyecto Django (preferiblemente dentro de un entorno virtual) – Cree un proyecto de demostración en Django
- Postman instalado en su sistema (puede descargarlo desde aquí o usar cualquier alternativa de cartero)
#Paso 1: Creación de un modelo de usuario personalizado en Django
Después de haber creado un proyecto de demostración. Crear una aplicación Cuentas .
python manage.py startapp accounts
Luego puede ver que se crea una nueva carpeta con el nombre de la cuenta, ahora agréguela a INSTALLED_APPS en settings.py. Así que debería verse algo como esto:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # add this 'accounts', ]
Dentro de models.py vamos a crear la Cuenta modelo y su Administrador y también importarlos:
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
- aquí estamos usando el correo electrónico como CAMPO_NOMBRE DE USUARIO predeterminado y queremos que el nombre de usuario y el nombre sean campos obligatorios (también puede tener cualquier otro campo, pero el campo de nombre de usuario debe estar allí
- is_active debe ser True por defecto e is_staff debe ser False por defecto
- para crear objetos (usuarios) necesitamos un administrador personalizado ( que estamos creando a continuación )
- escriba » nombre de usuario » en lugar de nombre de usuario o cualquier otro estilo porque el inicio de sesión de Facebook o Google devuelve el nombre de usuario
- usar gettextlazy es opcional
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)
Aquí create_user creará usuarios regulares mientras que create_superuser creará superusuarios (administrador).
crear usuario
- para crear usuarios, necesitamos pasar el correo electrónico, el nombre de usuario, el nombre y la contraseña, así como cualquier otro campo
- si no hay correo electrónico, genere un error; de lo contrario, normalice el correo electrónico ( Lea aquí)
- luego cree un objeto de cuenta modelo con correo electrónico, nombre de usuario y otros campos
- luego configure la contraseña, set_password en realidad establece la contraseña como una contraseña codificada en el objeto del modelo para que nadie pueda ver la contraseña real
- luego guarde el objeto de usuario y devuélvalo
crear_superusuario
- is_staff, is_superuser, is_active deben establecerse en True de forma predeterminada
- si no se establece en True o se pasa a False, genera errores; de lo contrario, create_user con estos valores y otros campos
Ahora agregue esto en settings.py para usar nuestro modelo de usuario personalizado:
AUTH_USER_MODEL='accounts.Account'
Luego necesitamos migrar este modelo a la base de datos.
python manage.py makemigrations python manage.py migrate
#Paso 2: Creación de puntos finales de la API REST para la autenticación
Para eso, primero debemos instalar algunas bibliotecas, que explicaré a continuación por qué las necesitamos:
pip install djangorestframework pip install django-cors-headers pip install drf_social_oauth2
- djangorestframework es para puntos finales de API REST
- Se requiere django-cors -headers para que nuestra aplicación React pueda comunicarse con el servidor django
- drf_social_oauth2 : esta es la biblioteca principal que nos permite la autenticación basada en token oauth2 para la contraseña de correo electrónico, así como para Google y Facebook
Ahora, dentro de settings.py, debemos agregarlos a INSTALLED_APPS para que nuestra aplicación funcione como se espera:
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', ]
Agreguemos también estas configuraciones a settings.py, que explicaré a continuación:
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" ]
- AUTHENTICATION_BACKENDS : para que podamos autenticarnos con OAuth2 (basado en token) o autenticación básica ( sin token )
- Las requests de punto final REST_FRAMEWORK se pueden autenticar solo con tokens
- CORS_ALLOWED_ORIGINS será la dirección de nuestra interfaz (aquí está la dirección del sitio web de reacción)
Muy bien, ahora agreguemos alguna configuración a MIDDLEWARE y TEMPLATES:
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', ], }, }, ]
Esos son requeridos por inicios de sesión sociales y cors.
Agreguemos alguna URL a urls.py del proyecto django, ya que son necesarios tanto para el marco de descanso como para oauth
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')), ]
Después de agregar esto y aquello según sea necesario, comencemos a codificar nuevamente.
Para Registrar/Registrar un usuario necesitamos escribir un serializador y ver. Tenga en cuenta que después de registrarse, ese usuario debe iniciar sesión por sí mismo, eso no sucederá automáticamente. ¡Sin embargo, también he cubierto eso! ¡Entonces un usuario puede registrarse y nuestro backend iniciará sesión con ese usuario!
Vamos a crear el serializador, ya he creado un serializers.py en la carpeta de cuentas, así que ahora escribe este código:
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
Dentro del RegistrySerializer:
- class Meta tiene qué modelo, los campos se serializarán y la contraseña no se puede leer, solo se escribirá (para formularios)
- El método create creará una nueva instancia de Cuenta y la llenará con datos validados que pasamos desde nuestra vista.
- necesitamos esa contraseña y si esa contraseña no es Ninguna, podemos establecer una contraseña cifrada para que la extraigamos de los datos validados
- luego guardaremos la instancia y la devolveremos
Ahora escribiremos la vista para crear usuarios:
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)
- importamos lo necesario, permitimos que cualquier persona acceda a esta vista y solo se permite publicar requests en esta vista
- cuando ocurra la solicitud posterior, inicializaremos RegistrationSerializer con request.data y, si los datos son válidos, guárdelos; de lo contrario, devolverá el error
- *Nota: volveremos a visitar esta vista para iniciar sesión con el usuario después de guardar los datos serializados más adelante
también cree un urls.py en el directorio de cuentas y escriba esto
from django.urls import path from .views import CreateAccount app_name = 'users' urlpatterns = [ path('create/', CreateAccount.as_view(), name="create_user"),]
finalmente agregue esta url a urls.py del proyecto
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')) ]
Ahora, para iniciar sesión, no necesitamos escribir ningún código adicional, ya que el inicio de sesión no es más que obtener un token de acceso del servidor y drf-social-oauth2 ya se encarga de todo esto. ¡Ni siquiera necesitamos escribir puntos finales para eso!
Necesitamos crear una aplicación y obtener client_id y client_secret para obtener tokens de acceso, así que visitemos el administrador que se ejecuta en http://127.0.0.1:8000/admin/ y debería ver algo como esto:
Aquí puede ver Aplicaciones, presione el botón Agregar de eso y cree una nueva aplicación. No toque la identificación del cliente y el secreto del cliente , seleccione el usuario como su superusuario administrador , y el tipo de cliente será confidencial , el tipo de concesión de autorización estará basado en la contraseña del propietario del recurso , luego guárdelo.
Eso es todo, estamos listos para verificar nuestra aplicación ahora, así que creemos un superusuario y pasemos los valores requeridos y luego ejecutemos el servidor dentro del entorno virtual.
python manage.py runserver
#Paso 3: Creación e inicio de sesión de usuario con contraseña de correo electrónico
¡Ahora abra Postman y escriba esta URL y en el cuerpo escriba esto y envíe la solicitud!
Debería obtener una respuesta en blanco como esta
¡ Así se crea el usuario, por lo que funciona bien!
Para enviar la solicitud de token, pasaremos client_id, client_secret , correo electrónico como nombre de usuario, contraseña y grant_type=password para que el servidor sepa que esto es para obtener un nuevo access_token y actualizar el token.
Ahora podemos enviar requests de token, así que de vuelta en cartero envíe esta solicitud a http://127.0.0.1:8000/api-auth/token:
Tenga en cuenta que pasamos el correo electrónico al campo de nombre de usuario en la solicitud, ya que es obligatorio pasar el nombre de usuario
- Debería devolver el token de acceso y el token de actualización , así como el estado de 200 Ok
- Este access_token se puede configurar en los encabezados de autorización de la solicitud para enviar requests autenticadas (lo haremos con reaccionar)
Recuerde que dije que volveremos a visitar esa vista CreateAccount para iniciar la sesión del usuario después de guardar los datos serializados, hagámoslo
import requests # add this 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: #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)
Entonces, después de guardar al nuevo usuario, enviaremos una solicitud posterior para obtener el token y enviarlo como Respuesta. ¡Esto iniciará automáticamente la sesión del usuario después de registrarse!
Las autenticaciones de Google y Facebook necesitan una interfaz para demostraciones completas, así que le mostraré eso con reaccionar, sin embargo, ¡cualquier interfaz puede funcionar si sabe qué hacer! (Lo haré después de los pasos necesarios)
#Paso 4: preparación de demostración de solicitud autenticada
Crearemos un serializador diferente que devolverá información sobre los usuarios y el usuario actual a través de dos vistas diferentes, una será una solicitud autenticada y otra será una solicitud no autenticada.
entonces el serializador se verá así:
class UsersSerializer(serializers.ModelSerializer): class Meta: model=Account fields=('email','username','first_name')
Escribiremos estas dos vistas:
- AllUsers devuelve todos los usuarios y cualquiera puede ver los datos
- CurrentUser que devuelve solo el usuario actual y solo se permiten requests autenticadas
from rest_framework import status,generics class 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)
urls.py se verá así:
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"), ]
#Paso 5: Demostración de solicitud autenticada
Entonces, enviemos primero una solicitud no autenticada y devuelve esta respuesta ( tenga en cuenta que el encabezado de autorización no tiene nada )
Ahora obtengamos el token de acceso que recibimos anteriormente en el paso 3
- establezca el tipo de autorización en token de portador desde el menú desplegable
- envíe una solicitud autenticada a http://127.0.0.1:8000/api-auth/currentUser/
Ahora devolverá al usuario actual si por casualidad envió la solicitud sin el encabezado de Autorización, habría regresado así
Ahora, ¿qué pasa si el token de acceso caduca, qué hacer entonces?
Para eso, necesitamos enviar un token de actualización al mismo punto final para obtener un nuevo acceso y un token de actualización.
Déjame enseñarte como ! Entonces enviamos la solicitud a /token con client_id, client_secret, grant_type = refresh_token (para que el servidor entienda que la solicitud tiene un token de actualización que luego convierte el acceso antiguo y el token de actualización en un nuevo acceso y token de actualización) y refresh_token será el token que recibimos en tercer paso
Devuelve nuevos tokens como respuesta . Puede usar este nuevo token de acceso como token de portador para autenticar requests
#Paso 6: Inicio de sesión en Facebook y Google
Agregue estos en settings.py y para obtener las claves requeridas para Facebook y Google , debe visitar aquí para Facebook y aquí para Google y realizar los pasos necesarios allí.
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', ]
#Paso 7: Inicio de sesión de Google y Facebook usando React JS
Antes de usar esta parte del tutorial, consulta el Tutorial de React JS . Después de haber creado una aplicación de reacción, realice los siguientes pasos:
Es muy fácil autenticarse una vez que tenga las claves, ahora necesitamos instalar axios, react-facebook-login, react-google-login
Luego, cree un componente de inicio de sesión en el que representemos estos botones de inicio de sesión de fb y google que tomarán las claves requeridas y cada usuario cuando intente iniciar sesión con estos botones, devolverá un token de acceso, así como información sobre el usuario.
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"} /> </> ); }
Sin embargo , nuestro servidor no tiene idea de que hemos iniciado sesión con un usuario, ya que este token de acceso lo devuelve Google, Facebook y no nuestro servidor .
Entonces, ¿cómo avisar a nuestro servidor? ¿Enviar este token a nuestro servidor o establecer este token como nuestro token de portador autorizado? No, en ese caso no podemos crear un objeto de cuenta con nuestro modelo de Cuentas ni acceder a ningún objeto de cuenta como usuario actual, por lo que nuestro usuario no estará técnicamente registrado ni conectado.
Entonces, necesitamos convertir este token para obtener el token de acceso, actualizar el token desde nuestro servidor (registrar a nuestro usuario) y para eso creé un archivo axios.js y allí puse este código:
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); }); }
lo que estamos haciendo aquí es:
- haga una solicitud de publicación a http://127.0.0.1:8000/api-auth/convert-token para convertir el token
- backend será el backend desde donde obtuvo el token de acceso para convertir
- grant_type será convert_token para que el servidor sepa que queremos que este token se convierta en el token de acceso de nuestro servidor
- client_id y client_secret serán como los pasos anteriores
Entonces, ¿cómo actualizar un token si los tokens de acceso caducan? Queremos que eso suceda automáticamente sin que el usuario se moleste.
- Para eso, usaremos interceptores axios que devolverán respuestas, pero si hay un error, manejará esos errores.
- si el estado del error es 401 (Solicitud no autorizada ), si no hay un token de actualización en el almacenamiento local, le pedirá al usuario que inicie sesión y, si existe un token de actualización, lo enviará a http://127.0.0.1:8000/api -auth/token para obtener nuevos tokens de acceso y actualización
- Tenga en cuenta que estoy usando el almacenamiento local para almacenar tokens de acceso como ejemplo, pero probablemente debería usar un método seguro de cookies web (seguro, HttpOnly, mismo sitio)
- la ventana debe volver a cargarse para permitir que Reaccione obtenga la autenticación del sitio web con el token de acceso más reciente
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)); } } );
De esta manera podemos autenticar a un usuario de Django y React usando inicios de sesión sociales.