Files
dj52/accounts/views.py
Beyhan Oğur ec28a2024d first commit
2026-04-26 22:22:29 +03:00

272 lines
9.8 KiB
Python

from django.shortcuts import redirect
from django.views import View
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework_simplejwt.tokens import RefreshToken
from social_django.utils import load_strategy, load_backend
from social_core.backends.oauth import BaseOAuth2
from social_core.exceptions import AuthException, AuthForbidden
from .serializers import SocialLoginSerializer, CustomUserSerializer
import json
class SocialLoginView(APIView):
"""
Social authentication endpoint.
Accepts access_token from social provider and returns JWT tokens.
POST /api/v1/auth/social/<provider>/
Body: { "access_token": "..." }
Supported providers: google-oauth2, github, facebook
"""
permission_classes = [AllowAny]
serializer_class = SocialLoginSerializer
def post(self, request, provider):
"""
Authenticate user with social provider token.
"""
# Validate provider
valid_providers = ['google-oauth2', 'github', 'facebook']
if provider not in valid_providers:
return Response(
{'error': f'Invalid provider. Must be one of: {", ".join(valid_providers)}'},
status=status.HTTP_400_BAD_REQUEST
)
# Get access_token from request
access_token = request.data.get('access_token')
id_token = request.data.get('id_token', None)
if not access_token:
return Response(
{'error': 'access_token is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
# Load social auth strategy and backend
strategy = load_strategy(request)
backend = load_backend(
strategy=strategy,
name=provider,
redirect_uri=None
)
# Verify token and get user
if isinstance(backend, BaseOAuth2):
# For OAuth2 providers, use access_token to get user info
user = backend.do_auth(access_token)
else:
return Response(
{'error': 'Unsupported authentication backend'},
status=status.HTTP_400_BAD_REQUEST
)
if not user:
return Response(
{'error': 'Authentication failed. Invalid token.'},
status=status.HTTP_401_UNAUTHORIZED
)
# Check if user is active
if not user.is_active:
# This shouldn't happen for social auth users, but just in case
user.is_active = True
user.save(update_fields=['is_active'])
# Generate JWT tokens
refresh = RefreshToken.for_user(user)
# Serialize user data
user_serializer = CustomUserSerializer(user)
return Response({
'access': str(refresh.access_token),
'refresh': str(refresh),
'user': user_serializer.data
}, status=status.HTTP_200_OK)
except AuthForbidden:
return Response(
{'error': 'Authentication forbidden. Email not provided by provider or permission denied.'},
status=status.HTTP_403_FORBIDDEN
)
except AuthException as e:
return Response(
{'error': f'Authentication error: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return Response(
{'error': f'An error occurred during authentication: {str(e)}'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
class SocialAuthCallbackView(View):
"""
Callback view for OAuth flow completion.
After successful authentication, redirects to frontend with tokens.
"""
permission_classes = [AllowAny]
authentication_classes = [] # No authentication required for callback
def get(self, request):
"""Handle OAuth callback and redirect to frontend with JWT tokens."""
from django.http import HttpResponseRedirect
# Get the authenticated user from the session
user = request.user
if user and user.is_authenticated:
# Generate JWT tokens
refresh = RefreshToken.for_user(user)
# Redirect to SPA with tokens (for testing)
redirect_url = f"/api/v1/spa/?access={str(refresh.access_token)}&refresh={str(refresh)}"
print(f"[OAuth Callback] Redirecting to: {redirect_url}")
return HttpResponseRedirect(redirect_url)
else:
# Authentication failed
return HttpResponseRedirect("/api/v1/auth/social/error/?error=authentication_failed")
class SocialAuthSuccessView(APIView):
"""
Success page after social authentication.
Displays tokens for testing purposes.
"""
permission_classes = [AllowAny]
authentication_classes = [] # No authentication required
def get(self, request):
"""Display success page with tokens."""
access_token = request.GET.get('access', '')
refresh_token = request.GET.get('refresh', '')
# Also check if user is in session
if not access_token and request.user.is_authenticated:
refresh = RefreshToken.for_user(request.user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Authentication Successful</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}}
.container {{
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 40px;
max-width: 600px;
width: 100%;
}}
h1 {{
color: #28a745;
text-align: center;
margin-bottom: 20px;
}}
.success-icon {{
text-align: center;
font-size: 64px;
margin-bottom: 20px;
}}
.token-box {{
background: #f8f9fa;
border: 2px solid #e1e4e8;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
word-break: break-all;
}}
.token-label {{
font-weight: bold;
color: #333;
margin-bottom: 5px;
}}
.token-value {{
font-family: 'Courier New', monospace;
font-size: 12px;
color: #666;
background: white;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}}
.btn {{
display: block;
width: 100%;
padding: 12px;
margin-top: 20px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}}
.btn:hover {{
background: #5568d3;
}}
</style>
</head>
<body>
<div class="container">
<div class="success-icon">✅</div>
<h1>Authentication Successful!</h1>
<p style="text-align: center; color: #666; margin-bottom: 30px;">
You have successfully authenticated with your social account.
</p>
<div class="token-box">
<div class="token-label">Access Token:</div>
<div class="token-value" id="accessToken">{access_token}</div>
</div>
<div class="token-box">
<div class="token-label">Refresh Token:</div>
<div class="token-value" id="refreshToken">{refresh_token}</div>
</div>
<button class="btn" onclick="copyTokens()">Copy Tokens to Clipboard</button>
<button class="btn" onclick="window.close()" style="background: #6c757d;">Close Window</button>
</div>
<script>
function copyTokens() {{
const tokens = {{
access: "{access_token}",
refresh: "{refresh_token}"
}};
navigator.clipboard.writeText(JSON.stringify(tokens, null, 2))
.then(() => alert('Tokens copied to clipboard!'))
.catch(err => alert('Failed to copy: ' + err));
}}
</script>
</body>
</html>
"""
from django.http import HttpResponse
return HttpResponse(html_content)