Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move auth logic from view to backend #81

Merged
merged 2 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ __pycache__/
.env
.vscode/
.Pipfile.lock
*client_secret.json*
30 changes: 26 additions & 4 deletions backend/users/backend.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from google.auth.transport import requests
from google.oauth2 import id_token
from google_auth_oauthlib.flow import Flow

from .mail import send_validation_mail
from .models import User


class OAuthAuthenticationBackend(ModelBackend):
"""
Authentication backend to allow authenticating users against a
Microsoft ADFS server with an authorization code.
Authentication backend to allow user use Google to login.
"""

def authenticate(self, request, username=None, password=None, **kwargs):
if kwargs.get('id_info'):
user = self.get_google_oauth_user(kwargs.get('id_info'))
state = kwargs.get('oauth_state')
flow = Flow.from_client_secrets_file(
f'{settings.ROOT_DIR}/backend/client_secret.json',
scopes=None,
redirect_uri=request.build_absolute_uri('/users/google-callback/'),
)

# Use the authorization response to get tokens
flow.fetch_token(authorization_response=request.build_absolute_uri(), state=state)

# Use the id_token to get user information
token = flow.credentials.id_token
request_google_oauth = requests.Request()
id_info = id_token.verify_oauth2_token(
token,
request_google_oauth,
)

user = self.get_google_oauth_user(id_info)
return user

def get_google_oauth_user(self, id_info):
Expand All @@ -24,5 +45,6 @@ def get_google_oauth_user(self, id_info):
user = User.objects.get(email=data['email'])
except User.DoesNotExist:
user = User.objects.create(**data)
send_validation_mail(email=data['email'])

return user
1 change: 1 addition & 0 deletions backend/users/mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ def send_validation_mail(email: str):
recipient_list,
html_message=f'{message}<br><a href={url}{token}>{url}{token}</a>',
)
print(f'Email sent successfully to {recipient_list} !')
return ({'title': 'Success', 'detail': 'Validation email sent.'},)
41 changes: 15 additions & 26 deletions backend/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
from django.shortcuts import redirect
from django.utils import timezone
from django.utils.http import url_has_allowed_host_and_scheme
from google.auth.transport import requests
from google.oauth2 import id_token
from google_auth_oauthlib.flow import Flow
from rest_framework import generics, permissions, status
from rest_framework.decorators import action
Expand All @@ -26,11 +24,20 @@ class OAuthViewset(GenericViewSet):
@action(detail=False, methods=['get'], url_path='google-login')
def google_login(self, request):
# Set up the Google OAuth2.0 flow
flow = Flow.from_client_secrets_file(
f'{settings.ROOT_DIR}/backend/client_secret.json',
scopes=['openid', 'profile', 'email'],
redirect_uri=request.build_absolute_uri('/users/google-callback/'),
)
try:
flow = Flow.from_client_secrets_file(
f'{settings.ROOT_DIR}/backend/client_secret.json',
scopes=['openid', 'profile', 'email'],
redirect_uri=request.build_absolute_uri('/users/google-callback/'),
)
except FileNotFoundError:
return Response(
{
'title': 'Error',
'Detail': 'Google login is currently disabled due'
+ 'to missing client secrets on the host server.',
},
)

authorization_url, state = flow.authorization_url(prompt='consent')
# Store the state so the callback can verify the response.
Expand All @@ -42,25 +49,7 @@ def google_callback(self, request): # pragma: no cover
# Get the state from the session to verify the response.
state = request.session.get('oauth_state')

flow = Flow.from_client_secrets_file(
f'{settings.ROOT_DIR}/backend/client_secret.json',
scopes=None,
redirect_uri=request.build_absolute_uri('/users/google-callback/'),
)

# Use the authorization response to get tokens
flow.fetch_token(authorization_response=request.build_absolute_uri(), state=state)

# Use the id_token to get user information
token = flow.credentials.id_token
request_google_oauth = requests.Request()
id_info = id_token.verify_oauth2_token(
token,
request_google_oauth,
)

user = authenticate(request=request, id_info=id_info)

user = authenticate(request=request, oauth_state=state)
if user:
if user.is_active:
login(request, user)
Expand Down