-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #47 from unb-mds/task/oauth
oauth(jwt): definição do fluxo de autênticação social com oauth2
- Loading branch information
Showing
18 changed files
with
295 additions
and
7 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ POSTGRES_DB="postgres" | |
POSTGRES_USER="suagradeunb" | ||
POSTGRES_PASSWORD="suagradeunb" | ||
|
||
|
||
# Credenciais de acesso ao admin | ||
ADMIN_NAME="admin" | ||
ADMIN_PASS="admin" | ||
ADMIN_PASS="admin" | ||
ADMIN_EMAIL="[email protected]" |
This file contains 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 file contains 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 file contains 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
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from django.contrib import admin | ||
from django import forms | ||
from .models import User | ||
|
||
|
||
class UserForm(forms.ModelForm): | ||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
for field in self.fields: | ||
self.fields[field].required = False | ||
self.fields['email'].required = True | ||
|
||
class Meta: | ||
model = User | ||
fields = '__all__' | ||
|
||
|
||
@admin.register(User) | ||
class UserAdmin(admin.ModelAdmin): | ||
form = UserForm | ||
list_display = ['uuid', 'email'] | ||
search_fields = ['email'] | ||
fields = [('first_name', 'last_name'), 'email'] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class UsersConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'users' |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import requests | ||
from users.models import User | ||
|
||
|
||
class GoogleOAuth2: | ||
GOOGLE_OAUTH2_PROVIDER = 'https://www.googleapis.com/oauth2/v3' | ||
|
||
@classmethod | ||
def get_user_data(cls, access_token: str) -> dict | None: | ||
if not access_token: | ||
return None | ||
|
||
user_info_url = cls.GOOGLE_OAUTH2_PROVIDER + '/userinfo' | ||
params = {'access_token': access_token} | ||
|
||
try: | ||
response = requests.get(user_info_url, params=params) | ||
if response.status_code == 200: | ||
user_data = response.json() | ||
return user_data | ||
else: | ||
return None | ||
except requests.exceptions.RequestException as e: | ||
return None | ||
|
||
@staticmethod | ||
def do_auth(user_data: dict) -> User | None: | ||
user, created = User.objects.get_or_create( | ||
first_name=user_data['given_name'], | ||
last_name=user_data['family_name'], | ||
) | ||
|
||
if created: | ||
user.email = user_data['email'] | ||
user.save() | ||
|
||
return user |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from importlib import import_module | ||
|
||
|
||
def get_backend(backend: str) -> object | None: | ||
try: | ||
module = import_module('users.backends.' + backend) | ||
return getattr(module, backend.title() + 'OAuth2') | ||
except (ImportError, AttributeError): | ||
return None |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Generated by Django 4.2.5 on 2023-10-13 19:19 | ||
|
||
import django.contrib.auth.models | ||
import django.contrib.auth.validators | ||
from django.db import migrations, models | ||
import django.utils.timezone | ||
import uuid | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('auth', '0012_alter_user_first_name_max_length'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='User', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('password', models.CharField(max_length=128, verbose_name='password')), | ||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), | ||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), | ||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), | ||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), | ||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), | ||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), | ||
('first_name', models.CharField(max_length=64)), | ||
('last_name', models.CharField(max_length=128)), | ||
('email', models.EmailField(max_length=254, unique=True)), | ||
('is_active', models.BooleanField(default=True)), | ||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), | ||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), | ||
], | ||
options={ | ||
'verbose_name': 'user', | ||
'verbose_name_plural': 'users', | ||
'abstract': False, | ||
}, | ||
managers=[ | ||
('objects', django.contrib.auth.models.UserManager()), | ||
], | ||
), | ||
] |
Empty file.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.contrib.auth.models import AbstractUser | ||
from django.db import models | ||
import uuid | ||
|
||
|
||
class User(AbstractUser): | ||
uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) | ||
first_name = models.CharField(max_length=64) | ||
last_name = models.CharField(max_length=128) | ||
email = models.EmailField(blank=False, unique=True) | ||
is_active = models.BooleanField(default=True) | ||
|
||
REQUIRED_FIELDS = ["email"] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from django.conf import settings | ||
from rest_framework import status | ||
from rest_framework.request import Request | ||
from rest_framework.response import Response | ||
import functools | ||
|
||
|
||
def move_refresh_token_to_cookie(view_func: callable) -> callable: | ||
|
||
@functools.wraps(view_func) | ||
def wrapper(request: Request, *args, **kwargs) -> Response: | ||
response = view_func(request, *args, **kwargs) | ||
|
||
if (response.status_code == status.HTTP_200_OK or response.status_code == status.HTTP_201_CREATED): | ||
jwt_settings = settings.SIMPLE_JWT | ||
refresh_token = response.data.pop('refresh') | ||
response.set_cookie(key="refresh", value=refresh_token, | ||
max_age=jwt_settings["REFRESH_TOKEN_LIFETIME"], | ||
secure=jwt_settings["REFRESH_TOKEN_SECURE"], | ||
httponly=True, samesite="Lax") | ||
return response | ||
return wrapper |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from rest_framework_simplejwt.serializers import TokenRefreshSerializer | ||
from rest_framework_simplejwt.authentication import JWTAuthentication | ||
|
||
|
||
class RefreshJWTSerializer(TokenRefreshSerializer): | ||
def validate(self, attrs: dict[str, any]) -> dict[str, any]: | ||
data = super().validate(attrs) | ||
|
||
jwt_authentication = JWTAuthentication() | ||
|
||
validated_token = jwt_authentication.get_validated_token(data["access"]) | ||
user = jwt_authentication.get_user(validated_token) | ||
|
||
data["first_name"] = str(user.first_name) | ||
data["last_name"] = str(user.last_name) | ||
data["email"] = str(user.email) | ||
|
||
return data |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from django.urls import re_path, path | ||
from users import views | ||
|
||
app_name = 'users' | ||
|
||
urlpatterns = [ | ||
re_path('register/' + r'(?P<oauth2>[^/]+)/$', views.Register.as_view(), name='register'), | ||
path('login/', views.RefreshJWTView.as_view(), name='login'), | ||
] |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from rest_framework import status | ||
from rest_framework import exceptions | ||
from rest_framework.response import Response | ||
from rest_framework.request import Request | ||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView | ||
from users.backends.utils import get_backend | ||
from users.simplejwt.decorators import move_refresh_token_to_cookie | ||
|
||
|
||
class Register(TokenObtainPairView): | ||
|
||
@move_refresh_token_to_cookie | ||
def post(self, request: Request, *args, **kwargs) -> Response: | ||
token = request.data.get('access_token') | ||
|
||
backend = get_backend(kwargs['oauth2']) | ||
if not backend: | ||
return Response( | ||
{ | ||
'errors': f'Invalid provider {kwargs["oauth2"]}' | ||
}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
user_data = backend.get_user_data(token) | ||
if user_data: | ||
user = backend.do_auth(user_data) | ||
|
||
serializer = self.get_serializer() | ||
refresh = serializer.get_token(user) | ||
data = { | ||
'access': str(refresh.access_token), | ||
'refresh': str(refresh), | ||
'first_name': user.first_name, | ||
'last_name': user.last_name, | ||
'email': user.email, | ||
} | ||
|
||
return Response(data, status.HTTP_200_OK) | ||
|
||
return Response( | ||
{ | ||
'errors': 'Invalid token' | ||
}, status.HTTP_400_BAD_REQUEST) | ||
|
||
|
||
class RefreshJWTView(TokenRefreshView): | ||
|
||
@move_refresh_token_to_cookie | ||
def post(self, request, *args, **kwargs): | ||
try: | ||
request.data['refresh'] = request.COOKIES['refresh'] | ||
except KeyError: | ||
raise exceptions.NotAuthenticated('Refresh cookie error.') | ||
|
||
return super().post(request, *args, **kwargs) |
This file contains 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