x4zx
@x4zx
python developer

Как реализовать авторизацию через Discord?

Изначально при создании сайта решил использовать архитектуру REST API для этого применяю DRF. И если брать обычную ситуацию где со стороны фронта идет запрос на бек - забираются какие-то данные - выводятся на тем же фронтом, то всё понятно. Но вот пример с авторизацией не совсем понятен. Допустим есть кнопка "войти" при нажатии на которую отправляется запрос на бек и он должен что-то вернуть - чтобы фронт проверил и как бы "авторизировал" пользователя (изменил интерфейс страницы как для авторизированого пользователя). Знаю что для DRF есть модуль djangorestframework-simplejwt который как бы помогает с авторизацией, но опять же вообще не понимаю как это должно работать, так сказать не вижу всей картины, архитектуры как это должно происходить. Пример на реальном проекте: опять же фронт и бек разделены и на DRF пишутся только ендпоинты для запросов. И вот есть та самая кнопка Войти при нажатии на которую - отправляется GET запрос на получение сгенерированной ссылки авторизации через Discord, выглядит она следующим образом:
https://discord.com/api/oauth2/authorize/?response...

client_id - id специального бота
redirect_url - страница на которую произойдет редирект после авторизации
scope - не знаю как описать одним словом, но в общем то что вы хотите получить от пользователя
state - специальный хеш, так как я ранее говорил что ссылка генерируется беком - то это мера предосторожности от спама запросами. То есть человек нажимает Войти - генерируется эта ссылка с этим уникальным хешем и я сохраняю его в сессии для дальнейшей проверки на валидность.

После подтверждение авторизации со стороны пользователя его перекидывает на страницу из ранее сказанного redirect_url. И это также очередной ендпоинт который обрабатывает запрос и должен внутри себя проверь валидность хеша (state) и зарегистрировать или авторизировать пользователя. И вот как раз с этим возникли трудности, не понимаю как это должно между собой работать. Текущий код следующий:

import json
import secrets
import requests
from urllib.parse import urlencode

from django.conf import settings
from rest_framework.views import APIView
from django.contrib.auth import authenticate
from django.http import HttpRequest, JsonResponse,\
    HttpResponseBadRequest, HttpResponseRedirect
from rest_framework_simplejwt.tokens import RefreshToken


class GenerateDiscordAuthURLView(APIView):
    def get(self, request: HttpRequest):
        state = secrets.token_urlsafe()
        request.session['state'] = state

        params = {
            'client_id': settings.CLIENT_ID,
            'redirect_uri': settings.REDIRECT_URL,
            'scope': 'identify email guilds', 'state': state}

        url = ('https://discord.com/api/oauth2/authorize?'
               f'response_type=code&{urlencode(query=params)}')

        return JsonResponse({'auth_url': url})


class GetAuthorizationCodeView(APIView):
    def get(self, request: HttpRequest, *args, **kwargs):
        request_state = request.GET.get('state')
        authorization_code = request.GET.get('code')
        session_state = request.session.pop('state', None)

        # Проверка параметра state для защиты от CSRF-атак
        if not request_state or request_state != session_state:
            return HttpResponseBadRequest('Invalid state parameter')

        # Проверка наличия кода авторизации
        if not authorization_code:
            return HttpResponseBadRequest('Authorization code not provided')
        
        data = {
            'grant_type': 'authorization_code',
            'code': authorization_code,
            'redirect_uri': settings.REDIRECT_URL
        }

        headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        response = requests.post(settings.GET_USER_TOKEN_URL, data=data,
            headers=headers, auth=(settings.CLIENT_ID, settings.CLIENT_SECRET))
        
        print(response)
        
        if response.status_code != 200:
            return HttpResponseBadRequest('Unknown Error')
        
        access_token = response.json()['access_token']
        headers = {'Authorization': f'Bearer {access_token}'}

        response = requests.get(settings.GET_USER_DATA_URL, headers=headers)
        print(response)

        user_data = response.json()
        user = authenticate(request, data=user_data)

        if user:
            refresh = RefreshToken.for_user(user)
            access_token = str(refresh.access_token)
            refresh_token = str(refresh)

            # Кодирование данных пользователя в JSON и сохранение в куки
            user_data_json = json.dumps(user_data)
            
            response = HttpResponseRedirect('http://127.0.0.1:5500/index.html')
            response.set_cookie('access_token', access_token, httponly=True)
            response.set_cookie('refresh_token', refresh_token, httponly=True)
            response.set_cookie('user_data', user_data_json)
            return response

        else:
            return HttpResponseBadRequest('Authentication failed')
  • Вопрос задан
  • 345 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы