from authlib.integrations.base_client import OAuthError from authlib.integrations.django_client import OAuth from authlib.oauth2.rfc6749 import OAuth2Token from django.conf import settings from django.contrib.auth import login from django.shortcuts import redirect from django.utils.deprecation import MiddlewareMixin from lg.models import DiscordUser class OAuthMiddleware(MiddlewareMixin): def __init__(self, get_response=None): super().__init__(get_response) self.oauth = OAuth() def process_request(self, request): if settings.OAUTH_URL_WHITELISTS is not None: for w in settings.OAUTH_URL_WHITELISTS: if request.path.startswith(w): return self.get_response(request) def update_token(token, refresh_token, access_token): request.session['token'] = token return None sso_client = self.oauth.register( settings.OAUTH_CLIENT_NAME, overwrite=True, **settings.OAUTH_CLIENT, update_token=update_token ) if request.path.startswith('/oauth/callback'): self.clear_session(request) request.session['token'] = sso_client.authorize_access_token(request) if self.get_current_user(sso_client, request) is not None: redirect_uri = request.session.pop('redirect_uri', None) if redirect_uri is not None: return redirect(redirect_uri) return redirect("index") if request.session.get('token', None) is not None: current_user = self.get_current_user(sso_client, request) login(request, current_user) if current_user is not None: return self.get_response(request) # remember redirect URI for redirecting to the original URL. request.session['redirect_uri'] = request.path return sso_client.authorize_redirect(request, settings.OAUTH_CLIENT['redirect_uri']) # fetch current login user info # 1. check if it's in cache # 2. fetch from remote API when it's not in cache @staticmethod def get_current_user(sso_client, request): token = request.session.get('token', None) if token is None or 'access_token' not in token: return None if not OAuth2Token.from_dict(token).is_expired() and 'user' in request.session: return DiscordUser.objects.get(discord_id=request.session['user']) try: res = sso_client.get(settings.OAUTH_CLIENT['userinfo_endpoint'], token=OAuth2Token(token)) if res.ok: user_json = res.json() discord_id = user_json['id'] user, _ = DiscordUser.objects.get_or_create(discord_id=discord_id) user.username = user_json['username'] user.discriminator = user_json['discriminator'] user.avatar_id = user_json['avatar'] user.is_staff = user.is_superuser = f"{user.username}#{user.discriminator}" in settings.SUPERUSERS user.save() request.session['user'] = user.discord_id return user raise Exception(res, str(res.__dict__)) except OAuthError as e: print(e) return None @staticmethod def clear_session(request): try: del request.session['user'] del request.session['token'] except KeyError: pass def __del__(self): print('destroyed')