Remove unused social authentication endpoints and update frontend

Remove deprecated social provider API endpoints from the backend and update the frontend JavaScript to hardcode Google and Discord as the available social login providers.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9bc9dd7a-5328-4cb7-91de-b3cb33a0c48c
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
pac7
2025-09-21 18:29:03 +00:00
committed by pacnpal
parent 164cc15d90
commit 821c94bc76
4 changed files with 6 additions and 350 deletions

View File

@@ -1,4 +1,4 @@
modules = ["bash", "web", "nodejs-20", "python-3.13"] modules = ["bash", "web", "nodejs-20", "python-3.13", "postgresql-16"]
[nix] [nix]
channel = "stable-25_05" channel = "stable-25_05"

View File

@@ -14,17 +14,10 @@ from .views import (
CurrentUserAPIView, CurrentUserAPIView,
PasswordResetAPIView, PasswordResetAPIView,
PasswordChangeAPIView, PasswordChangeAPIView,
SocialProvidersAPIView,
AuthStatusAPIView, AuthStatusAPIView,
# Email verification views # Email verification views
EmailVerificationAPIView, EmailVerificationAPIView,
ResendVerificationAPIView, ResendVerificationAPIView,
# Social provider management views
AvailableProvidersAPIView,
ConnectedProvidersAPIView,
ConnectProviderAPIView,
DisconnectProviderAPIView,
SocialAuthStatusAPIView,
) )
from rest_framework_simplejwt.views import TokenRefreshView from rest_framework_simplejwt.views import TokenRefreshView
@@ -52,38 +45,6 @@ urlpatterns = [
PasswordChangeAPIView.as_view(), PasswordChangeAPIView.as_view(),
name="auth-password-change", name="auth-password-change",
), ),
path(
"social/providers/",
SocialProvidersAPIView.as_view(),
name="auth-social-providers",
),
# Social provider management endpoints
path(
"social/providers/available/",
AvailableProvidersAPIView.as_view(),
name="auth-social-providers-available",
),
path(
"social/connected/",
ConnectedProvidersAPIView.as_view(),
name="auth-social-connected",
),
path(
"social/connect/<str:provider>/",
ConnectProviderAPIView.as_view(),
name="auth-social-connect",
),
path(
"social/disconnect/<str:provider>/",
DisconnectProviderAPIView.as_view(),
name="auth-social-disconnect",
),
path(
"social/status/",
SocialAuthStatusAPIView.as_view(),
name="auth-social-status",
),
path("status/", AuthStatusAPIView.as_view(), name="auth-status"), path("status/", AuthStatusAPIView.as_view(), name="auth-status"),

View File

@@ -6,16 +6,6 @@ login, signup, logout, password management, social authentication,
user profiles, and top lists. user profiles, and top lists.
""" """
from .serializers_package.social import (
ConnectedProviderSerializer,
AvailableProviderSerializer,
SocialAuthStatusSerializer,
ConnectProviderInputSerializer,
ConnectProviderOutputSerializer,
DisconnectProviderOutputSerializer,
SocialProviderErrorSerializer,
)
from apps.accounts.services.social_provider_service import SocialProviderService
from django.contrib.auth import authenticate, login, logout, get_user_model from django.contrib.auth import authenticate, login, logout, get_user_model
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@@ -42,7 +32,6 @@ from .serializers import (
PasswordResetOutputSerializer, PasswordResetOutputSerializer,
PasswordChangeInputSerializer, PasswordChangeInputSerializer,
PasswordChangeOutputSerializer, PasswordChangeOutputSerializer,
SocialProviderOutputSerializer,
AuthStatusOutputSerializer, AuthStatusOutputSerializer,
) )
@@ -406,69 +395,6 @@ class PasswordChangeAPIView(APIView):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@extend_schema_view(
get=extend_schema(
summary="Get social providers",
description="Retrieve available social authentication providers.",
responses={200: "List of social providers"},
tags=["Authentication"],
),
)
class SocialProvidersAPIView(APIView):
"""API endpoint to get available social authentication providers."""
permission_classes = [AllowAny]
serializer_class = SocialProviderOutputSerializer
def get(self, request: Request) -> Response:
from django.core.cache import cache
# get_current_site expects a django HttpRequest; _get_underlying_request now returns HttpRequest
site = get_current_site(_get_underlying_request(request))
# Cache key based on site and request host - use getattr to avoid attribute errors
site_id = getattr(site, "id", getattr(site, "pk", None))
cache_key = f"social_providers:{site_id}:{request.get_host()}"
# Try to get from cache first (cache for 15 minutes)
cached_providers = cache.get(cache_key)
if cached_providers is not None:
return Response(cached_providers)
providers_list = []
# Optimized query: filter by site and order by provider name
from allauth.socialaccount.models import SocialApp
social_apps = SocialApp.objects.filter(sites=site).order_by("provider")
for social_app in social_apps:
try:
provider_name = (
social_app.name or getattr(social_app, "provider", "").title()
)
auth_url = request.build_absolute_uri(
f"/accounts/{social_app.provider}/login/"
)
providers_list.append(
{
"id": social_app.provider,
"name": provider_name,
"authUrl": auth_url,
}
)
except Exception:
continue
serializer = SocialProviderOutputSerializer(providers_list, many=True)
response_data = serializer.data
cache.set(cache_key, response_data, 900)
return Response(response_data)
@extend_schema_view( @extend_schema_view(
@@ -501,234 +427,14 @@ class AuthStatusAPIView(APIView):
return Response(serializer.data) return Response(serializer.data)
# === SOCIAL PROVIDER MANAGEMENT API VIEWS ===
@extend_schema_view(
get=extend_schema(
summary="Get available social providers",
description="Retrieve list of available social authentication providers.",
responses={
200: AvailableProviderSerializer(many=True),
},
tags=["Social Authentication"],
),
)
class AvailableProvidersAPIView(APIView):
"""API endpoint to get available social providers."""
permission_classes = [AllowAny]
serializer_class = AvailableProviderSerializer
def get(self, request: Request) -> Response:
providers = [
{
"provider": "google",
"name": "Google",
"login_url": "/auth/social/google/",
"connect_url": "/auth/social/connect/google/",
},
{
"provider": "discord",
"name": "Discord",
"login_url": "/auth/social/discord/",
"connect_url": "/auth/social/connect/discord/",
}
]
serializer = AvailableProviderSerializer(providers, many=True)
return Response(serializer.data)
@extend_schema_view(
get=extend_schema(
summary="Get connected social providers",
description="Retrieve list of social providers connected to the user's account.",
responses={
200: ConnectedProviderSerializer(many=True),
401: "Unauthorized",
},
tags=["Social Authentication"],
),
)
class ConnectedProvidersAPIView(APIView):
"""API endpoint to get user's connected social providers."""
permission_classes = [IsAuthenticated]
serializer_class = ConnectedProviderSerializer
def get(self, request: Request) -> Response:
service = SocialProviderService()
providers = service.get_connected_providers(request.user)
serializer = ConnectedProviderSerializer(providers, many=True)
return Response(serializer.data)
@extend_schema_view(
post=extend_schema(
summary="Connect social provider",
description="Connect a social authentication provider to the user's account.",
request=ConnectProviderInputSerializer,
responses={
200: ConnectProviderOutputSerializer,
400: SocialProviderErrorSerializer,
401: "Unauthorized",
},
tags=["Social Authentication"],
),
)
class ConnectProviderAPIView(APIView):
"""API endpoint to connect a social provider."""
permission_classes = [IsAuthenticated]
serializer_class = ConnectProviderInputSerializer
def post(self, request: Request, provider: str) -> Response:
# Validate provider
if provider not in ['google', 'discord']:
return Response(
{
"success": False,
"error": "INVALID_PROVIDER",
"message": f"Provider '{provider}' is not supported",
"suggestions": ["Use 'google' or 'discord'"]
},
status=status.HTTP_400_BAD_REQUEST
)
serializer = ConnectProviderInputSerializer(data=request.data)
if not serializer.is_valid():
return Response(
{
"success": False,
"error": "VALIDATION_ERROR",
"message": "Invalid request data",
"details": serializer.errors,
"suggestions": ["Provide a valid access_token"]
},
status=status.HTTP_400_BAD_REQUEST
)
access_token = serializer.validated_data['access_token']
try:
service = SocialProviderService()
result = service.connect_provider(request.user, provider, access_token)
response_serializer = ConnectProviderOutputSerializer(result)
return Response(response_serializer.data)
except Exception as e:
return Response(
{
"success": False,
"error": "CONNECTION_FAILED",
"message": str(e),
"suggestions": [
"Verify the access token is valid",
"Ensure the provider account is not already connected to another user"
]
},
status=status.HTTP_400_BAD_REQUEST
)
@extend_schema_view(
post=extend_schema(
summary="Disconnect social provider",
description="Disconnect a social authentication provider from the user's account.",
responses={
200: DisconnectProviderOutputSerializer,
400: SocialProviderErrorSerializer,
401: "Unauthorized",
},
tags=["Social Authentication"],
),
)
class DisconnectProviderAPIView(APIView):
"""API endpoint to disconnect a social provider."""
permission_classes = [IsAuthenticated]
serializer_class = DisconnectProviderOutputSerializer
def post(self, request: Request, provider: str) -> Response:
# Validate provider
if provider not in ['google', 'discord']:
return Response(
{
"success": False,
"error": "INVALID_PROVIDER",
"message": f"Provider '{provider}' is not supported",
"suggestions": ["Use 'google' or 'discord'"]
},
status=status.HTTP_400_BAD_REQUEST
)
try:
service = SocialProviderService()
# Check if disconnection is safe
can_disconnect, reason = service.can_disconnect_provider(
request.user, provider)
if not can_disconnect:
return Response(
{
"success": False,
"error": "UNSAFE_DISCONNECTION",
"message": reason,
"suggestions": [
"Set up email/password authentication before disconnecting",
"Connect another social provider before disconnecting this one"
]
},
status=status.HTTP_400_BAD_REQUEST
)
# Perform disconnection
result = service.disconnect_provider(request.user, provider)
response_serializer = DisconnectProviderOutputSerializer(result)
return Response(response_serializer.data)
except Exception as e:
return Response(
{
"success": False,
"error": "DISCONNECTION_FAILED",
"message": str(e),
"suggestions": [
"Verify the provider is currently connected",
"Ensure you have alternative authentication methods"
]
},
status=status.HTTP_400_BAD_REQUEST
)
@extend_schema_view(
get=extend_schema(
summary="Get social authentication status",
description="Get comprehensive social authentication status for the user.",
responses={
200: SocialAuthStatusSerializer,
401: "Unauthorized",
},
tags=["Social Authentication"],
),
)
class SocialAuthStatusAPIView(APIView):
"""API endpoint to get social authentication status."""
permission_classes = [IsAuthenticated]
serializer_class = SocialAuthStatusSerializer
def get(self, request: Request) -> Response:
service = SocialProviderService()
auth_status = service.get_auth_status(request.user)
serializer = SocialAuthStatusSerializer(auth_status)
return Response(serializer.data)
# === EMAIL VERIFICATION API VIEWS === # === EMAIL VERIFICATION API VIEWS ===

View File

@@ -340,8 +340,11 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
open: false, open: false,
mode: defaultMode, // 'login' or 'register' mode: defaultMode, // 'login' or 'register'
showPassword: false, showPassword: false,
socialProviders: [], socialProviders: [
socialLoading: true, {id: 'google', name: 'Google', auth_url: '/accounts/google/login/'},
{id: 'discord', name: 'Discord', auth_url: '/accounts/discord/login/'}
],
socialLoading: false,
// Login form data // Login form data
loginForm: { loginForm: {
@@ -364,8 +367,6 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
registerError: '', registerError: '',
init() { init() {
this.fetchSocialProviders();
// Listen for auth modal events // Listen for auth modal events
this.$watch('open', (value) => { this.$watch('open', (value) => {
if (value) { if (value) {
@@ -377,18 +378,6 @@ Alpine.data('authModal', (defaultMode = 'login') => ({
}); });
}, },
async fetchSocialProviders() {
try {
const response = await fetch('/api/v1/auth/social/providers/');
const data = await response.json();
this.socialProviders = data.available_providers || [];
} catch (error) {
console.error('Failed to fetch social providers:', error);
this.socialProviders = [];
} finally {
this.socialLoading = false;
}
},
show(mode = 'login') { show(mode = 'login') {
this.mode = mode; this.mode = mode;