From a4c7ee5e6de82c588357a30dd8a34f72ab1d27a3 Mon Sep 17 00:00:00 2001 From: orenzhang Date: Mon, 17 Feb 2025 15:48:09 +0800 Subject: [PATCH] feat(account): oidc login --- apps/account/serializers.py | 8 ++++++++ apps/account/utils.py | 16 ++++++++++++++++ apps/account/views.py | 15 +++++++++++++++ entry/settings.py | 9 +++++++++ entry/urls.py | 1 + requirements.txt | 3 +++ 6 files changed, 52 insertions(+) create mode 100644 apps/account/utils.py diff --git a/apps/account/serializers.py b/apps/account/serializers.py index 1c20301..d89e326 100644 --- a/apps/account/serializers.py +++ b/apps/account/serializers.py @@ -161,3 +161,11 @@ def validate(self, attrs: dict) -> dict: if not TCaptchaVerify(user_ip=self.context.get("user_ip", ""), **data.get("tcaptcha", {})).verify(): raise TCaptchaInvalid() return data + + +class OIDCLoginRequestSerializer(serializers.Serializer): + """ + OIDC Login + """ + + next = serializers.CharField(label=gettext_lazy("Next")) diff --git a/apps/account/utils.py b/apps/account/utils.py new file mode 100644 index 0000000..190aaee --- /dev/null +++ b/apps/account/utils.py @@ -0,0 +1,16 @@ +from rest_framework.settings import api_settings + + +def userinfo(claims, user): + claims.update( + { + "name": user.username, + "nickname": user.nick_name, + "updated_at": user.last_login.strftime(api_settings.DATETIME_FORMAT), + } + ) + return claims + + +def default_sub_generator(user): + return f"{user.username}" diff --git a/apps/account/views.py b/apps/account/views.py index 986580b..4cf5202 100644 --- a/apps/account/views.py +++ b/apps/account/views.py @@ -1,12 +1,14 @@ import datetime import json from json import JSONDecodeError +from urllib.parse import quote import httpx from django.conf import settings from django.contrib import auth from django.contrib.auth import get_user_model from django.core.cache import cache +from django.http import HttpResponseRedirect from ovinc_client.core.auth import SessionAuthenticate from ovinc_client.core.logger import logger from ovinc_client.core.utils import get_ip, uniq_id @@ -31,6 +33,7 @@ from apps.account.models import User from apps.account.rates import IPRateThrottle, SMSRateThrottle from apps.account.serializers import ( + OIDCLoginRequestSerializer, ResetPasswordRequestSerializer, SendVerifyCodeRequestSerializer, SignInSerializer, @@ -339,3 +342,15 @@ def phone_areas(self, request, *args, **kwargs) -> Response: """ return Response(data=[{"value": value, "label": str(label)} for value, label in PhoneNumberAreas.choices]) + + @action(methods=["GET"], detail=False, authentication_classes=[SessionAuthenticate]) + def oidc_login(self, request, *args, **kwargs) -> HttpResponseRedirect: + """ + OIDC Login, Redirect to Login Page + """ + + req_slz = OIDCLoginRequestSerializer(data=request.query_params) + req_slz.is_valid(raise_exception=True) + req_data = req_slz.validated_data + next_url = quote(settings.BACKEND_URL + req_data["next"]) + return HttpResponseRedirect(redirect_to=f"{settings.FRONTEND_URL}/login/?next={next_url}") diff --git a/entry/settings.py b/entry/settings.py index d52f079..a8c1fde 100644 --- a/entry/settings.py +++ b/entry/settings.py @@ -44,6 +44,7 @@ "django.contrib.staticfiles", "rest_framework", "sslserver", + "oidc_provider", "apps.account", "apps.application", "apps.cel", @@ -253,3 +254,11 @@ CAPTCHA_APP_ID = int(os.getenv("CAPTCHA_APP_ID", "0")) CAPTCHA_APP_SECRET = os.getenv("CAPTCHA_APP_SECRET", "") CAPTCHA_APP_INFO_TIMEOUT = int(os.getenv("CAPTCHA_APP_INFO_TIMEOUT", str(60 * 10))) + +# OIDC +OIDC_USERINFO = "apps.account.utils.userinfo" +OIDC_IDTOKEN_SUB_GENERATOR = "apps.account.utils.default_sub_generator" +OIDC_IDTOKEN_INCLUDE_CLAIMS = strtobool(os.getenv("OIDC_IDTOKEN_INCLUDE_CLAIMS", "True")) +OIDC_LOGIN_URL = "/account/oidc_login/" +OIDC_CODE_EXPIRE = int(os.getenv("OIDC_CODE_EXPIRE", "600")) +OIDC_IDTOKEN_EXPIRE = int(os.getenv("OIDC_IDTOKEN_EXPIRE", "600")) diff --git a/entry/urls.py b/entry/urls.py index 7bd410f..63eec1e 100644 --- a/entry/urls.py +++ b/entry/urls.py @@ -21,6 +21,7 @@ def serve_static(request, path, insecure=True, **kwargs): re_path(r"^static/(?P.*)$", serve_static, name="static"), path("admin/login/", RedirectView.as_view(url=ADMIN_PAGE_LOGIN_URL.replace("%", "%%"))), path("admin/", admin.site.urls), + path("openid/", include("oidc_provider.urls", namespace="oidc_provider")), path("", include("apps.home.urls")), path("account/", include("apps.account.urls")), path("application/", include("apps.application.urls")), diff --git a/requirements.txt b/requirements.txt index defab34..de673b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,3 +22,6 @@ pycryptodome==3.21.0 # tencent cloud tencentcloud-sdk-python==3.0.1282 + +# oidc +django-oidc-provider==0.8.3