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,34 @@
import requests
from allauth.socialaccount.providers.oauth2.client import (
OAuth2Client,
OAuth2Error,
)
class DingTalkOAuth2Client(OAuth2Client):
def get_access_token(self, code, pkce_code_verifier=None):
data = {
"clientId": self.consumer_key,
"clientSecret": self.consumer_secret,
"code": code,
"grantType": "authorization_code",
}
params = None
if pkce_code_verifier:
data["code_verifier"] = pkce_code_verifier
self._strip_empty_keys(data)
url = self.access_token_url
if self.access_token_method == "GET":
params = data
data = None
resp = requests.request(self.access_token_method, url, params=params, json=data)
resp.raise_for_status()
access_token = resp.json()
if not access_token or "accessToken" not in access_token:
raise OAuth2Error("Error retrieving access token: %s" % resp.content)
access_token["access_token"] = access_token.pop("accessToken")
access_token["refresh_token"] = access_token.pop("refreshToken")
access_token["expires_in"] = access_token.pop("expireIn")
return access_token

View File

@@ -0,0 +1,30 @@
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
class DingTalkAccount(ProviderAccount):
def get_avatar_url(self):
return self.account.extra_data.get("avatarUrl")
def to_str(self):
return self.account.extra_data.get(
"nick", super(DingTalkAccount, self).to_str()
)
class DingTalkProvider(OAuth2Provider):
id = "dingtalk"
name = "DingTalk"
account_class = DingTalkAccount
def extract_uid(self, data):
return data["openId"]
def get_default_scope(self):
return ["openid", "corpid"]
def extract_common_fields(self, data):
return dict(username=data.get("nick"), name=data.get("nick"))
provider_classes = [DingTalkProvider]

View File

@@ -0,0 +1,26 @@
from allauth.socialaccount.tests import OAuth2TestsMixin
from allauth.tests import MockedResponse, TestCase
from .provider import DingTalkProvider
class DingTalkTests(OAuth2TestsMixin, TestCase):
provider_id = DingTalkProvider.id
def get_mocked_response(self):
return MockedResponse(
200,
"""{
"nick": "aiden",
"unionId": "hTaCSb1nM4RXii6jaQvHZqQiEiE",
"avatarUrl": "https://static-legacy.dingtalk.com/media/lADPDg7mViaksW3NBJPNBJI_1170_1171.jpg",
"openId": "ELdCPlk0V2LodZHx3n0p5AiEiE"
}""",
)
def get_login_response_json(self, with_refresh_token=True):
return """{
"accessToken": "testac",
"expireIn": "3600",
"refreshToken": "testrf"
}"""

View File

@@ -0,0 +1,6 @@
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from .provider import DingTalkProvider
urlpatterns = default_urlpatterns(DingTalkProvider)

View File

@@ -0,0 +1,38 @@
import requests
from allauth.socialaccount.providers.oauth2.views import (
OAuth2Adapter,
OAuth2CallbackView,
OAuth2LoginView,
)
from .client import DingTalkOAuth2Client
from .provider import DingTalkProvider
class DingTalkOAuth2Adapter(OAuth2Adapter):
provider_id = DingTalkProvider.id
access_token_url = "https://api.dingtalk.com/v1.0/oauth2/userAccessToken"
authorize_url = "https://login.dingtalk.com/oauth2/auth"
profile_url = "https://api.dingtalk.com/v1.0/contact/users/me"
client_class = DingTalkOAuth2Client
def __init__(self, request):
# dingtalk set "authCode" instead of "code" in callback url
if "authCode" in request.GET:
request.GET._mutable = True
request.GET["code"] = request.GET["authCode"]
request.GET._mutable = False
super(DingTalkOAuth2Adapter, self).__init__(request)
def complete_login(self, request, app, token, **kwargs):
headers = {"x-acs-dingtalk-access-token": token.token}
resp = requests.get(self.profile_url, headers=headers)
resp.raise_for_status()
extra_data = resp.json()
return self.get_provider().sociallogin_from_response(request, extra_data)
oauth2_login = OAuth2LoginView.adapter_view(DingTalkOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(DingTalkOAuth2Adapter)