mirror of
				https://github.com/evgen-app/chess_rpg_backend.git
				synced 2025-11-04 01:37:30 +03:00 
			
		
		
		
	added jit for refresh token, refresh access token endpoint
This commit is contained in:
		
							parent
							
								
									1bb56c25fe
								
							
						
					
					
						commit
						c795a5807e
					
				
							
								
								
									
										0
									
								
								common/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								common/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								common/generators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								common/generators.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import string
 | 
			
		||||
import secrets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_charset(length: int):
 | 
			
		||||
    return "".join(
 | 
			
		||||
        secrets.choice(string.digits + string.ascii_letters) for _ in range(length)
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
from rest_framework import serializers
 | 
			
		||||
from rest_framework.exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
from game.models import Hero, Player, HeroInDeck, Deck
 | 
			
		||||
from game.models import Hero, Player, HeroInDeck, Deck, PlayerAuthSession
 | 
			
		||||
from game.services.jwt import read_jwt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CreateHeroSerializer(serializers.ModelSerializer):
 | 
			
		||||
| 
						 | 
				
			
			@ -113,3 +114,29 @@ class GetDeckSerializer(serializers.ModelSerializer):
 | 
			
		|||
    class Meta:
 | 
			
		||||
        model = Deck
 | 
			
		||||
        fields = ("player", "heroes")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObtainTokenPairSerializer(serializers.Serializer):
 | 
			
		||||
    refresh_token = serializers.CharField(max_length=300)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, instance=None, data=None, **kwargs):
 | 
			
		||||
        super().__init__(instance, data, **kwargs)
 | 
			
		||||
        self.player_id = None
 | 
			
		||||
 | 
			
		||||
    def validate_refresh_token(self, value):
 | 
			
		||||
        payload = read_jwt(value)
 | 
			
		||||
        if not payload:
 | 
			
		||||
            raise ValidationError("Token is incorrect or expired")
 | 
			
		||||
 | 
			
		||||
        if "jit" not in payload:
 | 
			
		||||
            raise ValidationError("Token is incorrect")
 | 
			
		||||
 | 
			
		||||
        jit = payload["jit"]
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            session = PlayerAuthSession.objects.get(jit=jit)
 | 
			
		||||
        except PlayerAuthSession.DoesNotExist:
 | 
			
		||||
            return ValidationError("Incorrect user session")
 | 
			
		||||
 | 
			
		||||
        self.player_id = session.player.id
 | 
			
		||||
        return value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@ from game.api.v1.serializers import (
 | 
			
		|||
    ListHeroSerializer,
 | 
			
		||||
    CreateDeckSerializer,
 | 
			
		||||
    GetDeckSerializer,
 | 
			
		||||
    ObtainTokenPairSerializer,
 | 
			
		||||
)
 | 
			
		||||
from game.services.jwt import sign_jwt
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,9 +76,8 @@ class PlayerCreateView(GenericAPIView, CreateModelMixin):
 | 
			
		|||
        serializer.is_valid(raise_exception=True)
 | 
			
		||||
        instance = self.perform_create(serializer)
 | 
			
		||||
 | 
			
		||||
        # TODO: add JTI to refresh token
 | 
			
		||||
        access_jwt = sign_jwt({"id": instance.id, "type": "access"}, t_life=3600)
 | 
			
		||||
        refresh_jwt = sign_jwt({"id": instance.id, "type": "refresh"})
 | 
			
		||||
        refresh_jwt = sign_jwt({"jit": instance.get_auth_session(), "type": "refresh"})
 | 
			
		||||
        return Response(
 | 
			
		||||
            {
 | 
			
		||||
                "access_token": access_jwt,
 | 
			
		||||
| 
						 | 
				
			
			@ -151,3 +151,13 @@ class RetireUpdateDeleteDeckView(
 | 
			
		|||
                "id", flat=True
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RefreshAuthKey(GenericAPIView):
 | 
			
		||||
    serializer_class = ObtainTokenPairSerializer
 | 
			
		||||
 | 
			
		||||
    def post(self, request, *args, **kwargs):
 | 
			
		||||
        serializer = self.get_serializer(data=request.data)
 | 
			
		||||
        serializer.is_valid(raise_exception=True)
 | 
			
		||||
        access_jwt = sign_jwt({"id": serializer.player_id, "type": "access"}, t_life=3600)
 | 
			
		||||
        return Response({"access_token": access_jwt}, status=status.HTTP_200_OK)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
from jwt import DecodeError
 | 
			
		||||
from rest_framework import authentication
 | 
			
		||||
from rest_framework import exceptions
 | 
			
		||||
from .models import Player
 | 
			
		||||
| 
						 | 
				
			
			@ -12,13 +13,20 @@ class PlayerAuthentication(authentication.BaseAuthentication):
 | 
			
		|||
        ):
 | 
			
		||||
            raise exceptions.AuthenticationFailed("No credentials provided.")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            t = read_jwt(token)
 | 
			
		||||
        except DecodeError:
 | 
			
		||||
            raise exceptions.AuthenticationFailed("Token is incorrect")
 | 
			
		||||
 | 
			
		||||
        if not t:
 | 
			
		||||
            raise exceptions.AuthenticationFailed("Token is incorrect of expired")
 | 
			
		||||
 | 
			
		||||
        if "id" not in t:
 | 
			
		||||
        if "id" not in t and "type" not in t:
 | 
			
		||||
            raise exceptions.AuthenticationFailed("No user data")
 | 
			
		||||
 | 
			
		||||
        if t["type"] != "access":
 | 
			
		||||
            raise exceptions.AuthenticationFailed("Incorrect token type")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            user = Player.objects.get(id=int(t["id"]))
 | 
			
		||||
        except Player.DoesNotExist:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,8 @@ from django.core.validators import (
 | 
			
		|||
from django.db import models
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from common.generators import generate_charset
 | 
			
		||||
 | 
			
		||||
HER0_TYPES = [("WIZARD", "wizard"), ("ARCHER", "archer"), ("WARRIOR", "warrior")]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +33,7 @@ class Player(models.Model):
 | 
			
		|||
    ):
 | 
			
		||||
        """saves user and creates deck for him with 16 heroes"""
 | 
			
		||||
        super(Player, self).save()
 | 
			
		||||
        PlayerAuthSession.objects.create(player=self)
 | 
			
		||||
        deck = Deck.objects.create(player=self)
 | 
			
		||||
        types = (
 | 
			
		||||
            ["ARCHER" for _ in range(4)]
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +64,9 @@ class Player(models.Model):
 | 
			
		|||
    def get_last_deck(self):
 | 
			
		||||
        return Deck.objects.filter(player=self).last()
 | 
			
		||||
 | 
			
		||||
    def get_auth_session(self):
 | 
			
		||||
        return PlayerAuthSession.objects.get(player=self).jit
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -155,3 +161,10 @@ class HeroInDeck(models.Model):
 | 
			
		|||
        db_table = "hero_in_deck"
 | 
			
		||||
        verbose_name = "Hero in deck"
 | 
			
		||||
        verbose_name_plural = "Heroes in decks"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlayerAuthSession(models.Model):
 | 
			
		||||
    player = models.OneToOneField(
 | 
			
		||||
        Player, unique_for_month=True, on_delete=models.CASCADE
 | 
			
		||||
    )
 | 
			
		||||
    jit = models.CharField(max_length=30, default=generate_charset(30))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,3 @@ def read_jwt(token: str) -> dict | bool:
 | 
			
		|||
    payload.pop("exp", None)
 | 
			
		||||
 | 
			
		||||
    return payload
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_refresh_token(payload: dict) -> str:
 | 
			
		||||
    return sign_jwt(payload)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,11 +6,13 @@ from game.api.v1.views import (
 | 
			
		|||
    PlayerCreateView,
 | 
			
		||||
    DeckCreateView,
 | 
			
		||||
    RetireUpdateDeleteDeckView,
 | 
			
		||||
    RefreshAuthKey,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path("v1/hero/", ListCreateHeroView.as_view(), name="hero_api_create"),
 | 
			
		||||
    path("v1/hero/<uuid:uuid>", RetrieveHeroView.as_view(), name="hero_api_retrieve"),
 | 
			
		||||
    path("v1/player/refresh", RefreshAuthKey.as_view(), name="player_create_api"),
 | 
			
		||||
    path("v1/player/", PlayerCreateView.as_view(), name="player_create_api"),
 | 
			
		||||
    path("v1/deck/", DeckCreateView.as_view(), name="deck_create_api"),
 | 
			
		||||
    path(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user