mirror of
https://github.com/evgen-app/chess_rpg_backend.git
synced 2024-11-25 19:14:02 +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 import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
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):
|
class CreateHeroSerializer(serializers.ModelSerializer):
|
||||||
|
@ -113,3 +114,29 @@ class GetDeckSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Deck
|
model = Deck
|
||||||
fields = ("player", "heroes")
|
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,
|
ListHeroSerializer,
|
||||||
CreateDeckSerializer,
|
CreateDeckSerializer,
|
||||||
GetDeckSerializer,
|
GetDeckSerializer,
|
||||||
|
ObtainTokenPairSerializer,
|
||||||
)
|
)
|
||||||
from game.services.jwt import sign_jwt
|
from game.services.jwt import sign_jwt
|
||||||
|
|
||||||
|
@ -75,9 +76,8 @@ class PlayerCreateView(GenericAPIView, CreateModelMixin):
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
instance = self.perform_create(serializer)
|
instance = self.perform_create(serializer)
|
||||||
|
|
||||||
# TODO: add JTI to refresh token
|
|
||||||
access_jwt = sign_jwt({"id": instance.id, "type": "access"}, t_life=3600)
|
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(
|
return Response(
|
||||||
{
|
{
|
||||||
"access_token": access_jwt,
|
"access_token": access_jwt,
|
||||||
|
@ -151,3 +151,13 @@ class RetireUpdateDeleteDeckView(
|
||||||
"id", flat=True
|
"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 authentication
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from .models import Player
|
from .models import Player
|
||||||
|
@ -12,13 +13,20 @@ class PlayerAuthentication(authentication.BaseAuthentication):
|
||||||
):
|
):
|
||||||
raise exceptions.AuthenticationFailed("No credentials provided.")
|
raise exceptions.AuthenticationFailed("No credentials provided.")
|
||||||
|
|
||||||
|
try:
|
||||||
t = read_jwt(token)
|
t = read_jwt(token)
|
||||||
|
except DecodeError:
|
||||||
|
raise exceptions.AuthenticationFailed("Token is incorrect")
|
||||||
|
|
||||||
if not t:
|
if not t:
|
||||||
raise exceptions.AuthenticationFailed("Token is incorrect of expired")
|
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")
|
raise exceptions.AuthenticationFailed("No user data")
|
||||||
|
|
||||||
|
if t["type"] != "access":
|
||||||
|
raise exceptions.AuthenticationFailed("Incorrect token type")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = Player.objects.get(id=int(t["id"]))
|
user = Player.objects.get(id=int(t["id"]))
|
||||||
except Player.DoesNotExist:
|
except Player.DoesNotExist:
|
||||||
|
|
|
@ -11,6 +11,8 @@ from django.core.validators import (
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from common.generators import generate_charset
|
||||||
|
|
||||||
HER0_TYPES = [("WIZARD", "wizard"), ("ARCHER", "archer"), ("WARRIOR", "warrior")]
|
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"""
|
"""saves user and creates deck for him with 16 heroes"""
|
||||||
super(Player, self).save()
|
super(Player, self).save()
|
||||||
|
PlayerAuthSession.objects.create(player=self)
|
||||||
deck = Deck.objects.create(player=self)
|
deck = Deck.objects.create(player=self)
|
||||||
types = (
|
types = (
|
||||||
["ARCHER" for _ in range(4)]
|
["ARCHER" for _ in range(4)]
|
||||||
|
@ -61,6 +64,9 @@ class Player(models.Model):
|
||||||
def get_last_deck(self):
|
def get_last_deck(self):
|
||||||
return Deck.objects.filter(player=self).last()
|
return Deck.objects.filter(player=self).last()
|
||||||
|
|
||||||
|
def get_auth_session(self):
|
||||||
|
return PlayerAuthSession.objects.get(player=self).jit
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@ -155,3 +161,10 @@ class HeroInDeck(models.Model):
|
||||||
db_table = "hero_in_deck"
|
db_table = "hero_in_deck"
|
||||||
verbose_name = "Hero in deck"
|
verbose_name = "Hero in deck"
|
||||||
verbose_name_plural = "Heroes in decks"
|
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)
|
payload.pop("exp", None)
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def generate_refresh_token(payload: dict) -> str:
|
|
||||||
return sign_jwt(payload)
|
|
||||||
|
|
|
@ -6,11 +6,13 @@ from game.api.v1.views import (
|
||||||
PlayerCreateView,
|
PlayerCreateView,
|
||||||
DeckCreateView,
|
DeckCreateView,
|
||||||
RetireUpdateDeleteDeckView,
|
RetireUpdateDeleteDeckView,
|
||||||
|
RefreshAuthKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("v1/hero/", ListCreateHeroView.as_view(), name="hero_api_create"),
|
path("v1/hero/", ListCreateHeroView.as_view(), name="hero_api_create"),
|
||||||
path("v1/hero/<uuid:uuid>", RetrieveHeroView.as_view(), name="hero_api_retrieve"),
|
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/player/", PlayerCreateView.as_view(), name="player_create_api"),
|
||||||
path("v1/deck/", DeckCreateView.as_view(), name="deck_create_api"),
|
path("v1/deck/", DeckCreateView.as_view(), name="deck_create_api"),
|
||||||
path(
|
path(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user