This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
from __future__ import absolute_import
from datetime import timedelta
from requests import RequestException
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils import timezone
from allauth.core.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import get_adapter
from allauth.socialaccount.helpers import (
complete_social_login,
render_authentication_error,
)
from allauth.socialaccount.models import SocialLogin, SocialToken
from allauth.socialaccount.providers.base import ProviderException
from allauth.socialaccount.providers.base.constants import (
AuthAction,
AuthError,
)
from allauth.socialaccount.providers.base.mixins import OAuthLoginMixin
from allauth.socialaccount.providers.oauth2.client import (
OAuth2Client,
OAuth2Error,
)
from allauth.utils import build_absolute_uri, get_request_param
class OAuth2Adapter(object):
expires_in_key = "expires_in"
client_class = OAuth2Client
supports_state = True
redirect_uri_protocol = None
access_token_method = "POST"
login_cancelled_error = "access_denied"
scope_delimiter = " "
basic_auth = False
headers = None
def __init__(self, request):
self.request = request
def get_provider(self):
return get_adapter(self.request).get_provider(
self.request, provider=self.provider_id
)
def complete_login(self, request, app, access_token, **kwargs):
"""
Returns a SocialLogin instance
"""
raise NotImplementedError
def get_callback_url(self, request, app):
callback_url = reverse(self.provider_id + "_callback")
protocol = self.redirect_uri_protocol
return build_absolute_uri(request, callback_url, protocol)
def parse_token(self, data):
token = SocialToken(token=data["access_token"])
token.token_secret = data.get("refresh_token", "")
expires_in = data.get(self.expires_in_key, None)
if expires_in:
token.expires_at = timezone.now() + timedelta(seconds=int(expires_in))
return token
def get_access_token_data(self, request, app, client):
code = get_request_param(self.request, "code")
pkce_code_verifier = request.session.pop("pkce_code_verifier", None)
return client.get_access_token(code, pkce_code_verifier=pkce_code_verifier)
class OAuth2View(object):
@classmethod
def adapter_view(cls, adapter):
def view(request, *args, **kwargs):
self = cls()
self.request = request
if not isinstance(adapter, OAuth2Adapter):
self.adapter = adapter(request)
else:
self.adapter = adapter
try:
return self.dispatch(request, *args, **kwargs)
except ImmediateHttpResponse as e:
return e.response
return view
def get_client(self, request, app):
callback_url = self.adapter.get_callback_url(request, app)
provider = self.adapter.get_provider()
scope = provider.get_scope(request)
client = self.adapter.client_class(
self.request,
app.client_id,
app.secret,
self.adapter.access_token_method,
self.adapter.access_token_url,
callback_url,
scope,
scope_delimiter=self.adapter.scope_delimiter,
headers=self.adapter.headers,
basic_auth=self.adapter.basic_auth,
)
return client
class OAuth2LoginView(OAuthLoginMixin, OAuth2View):
def login(self, request, *args, **kwargs):
provider = self.adapter.get_provider()
app = provider.app
client = self.get_client(request, app)
action = request.GET.get("action", AuthAction.AUTHENTICATE)
auth_url = self.adapter.authorize_url
auth_params = provider.get_auth_params(request, action)
pkce_params = provider.get_pkce_params()
code_verifier = pkce_params.pop("code_verifier", None)
auth_params.update(pkce_params)
if code_verifier:
request.session["pkce_code_verifier"] = code_verifier
client.state = SocialLogin.stash_state(request)
try:
return HttpResponseRedirect(client.get_redirect_url(auth_url, auth_params))
except OAuth2Error as e:
return render_authentication_error(request, provider.id, exception=e)
class OAuth2CallbackView(OAuth2View):
def dispatch(self, request, *args, **kwargs):
if "error" in request.GET or "code" not in request.GET:
# Distinguish cancel from error
auth_error = request.GET.get("error", None)
if auth_error == self.adapter.login_cancelled_error:
error = AuthError.CANCELLED
else:
error = AuthError.UNKNOWN
return render_authentication_error(
request, self.adapter.provider_id, error=error
)
app = self.adapter.get_provider().app
client = self.get_client(self.request, app)
try:
access_token = self.adapter.get_access_token_data(request, app, client)
token = self.adapter.parse_token(access_token)
if app.pk:
token.app = app
login = self.adapter.complete_login(
request, app, token, response=access_token
)
login.token = token
if self.adapter.supports_state:
login.state = SocialLogin.verify_and_unstash_state(
request, get_request_param(request, "state")
)
else:
login.state = SocialLogin.unstash_state(request)
return complete_social_login(request, login)
except (
PermissionDenied,
OAuth2Error,
RequestException,
ProviderException,
) as e:
return render_authentication_error(
request, self.adapter.provider_id, exception=e
)