refreshing and replacing old user data

This commit is contained in:
Corban-Lee Jones 2024-06-01 18:58:43 +01:00
parent 5a486de8f4
commit 25c168a982
8 changed files with 115 additions and 14 deletions

View File

@ -51,7 +51,8 @@ class DiscordAuthenticationBackend(BaseBackend):
if existing_user:
# The previous access token may have expired, so update it.
existing_user.access_token = discord_user_data["access_token"]
existing_user.update(discord_user_data)
# existing_user.access_token = discord_user_data["access_token"]
existing_user.save()
return existing_user

View File

@ -46,6 +46,7 @@ class DiscordUserOAuth2Manager(BaseUserManager):
mfa_enabled=user["mfa_enabled"],
access_token=user["access_token"],
token_expires=user["token_expires"],
refresh_token=user["refresh_token"],
**extra_fields
)

View File

@ -1,6 +1,7 @@
# Generated by Django 5.0.4 on 2024-05-31 22:41
from django.db import migrations, models
from django.utils import timezone
class Migration(migrations.Migration):
@ -13,7 +14,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='discorduser',
name='token_expires',
field=models.DateTimeField(default=0, help_text='when to request a new access token.', verbose_name='token expires'),
field=models.DateTimeField(default=timezone.now, help_text='when to request a new access token.', verbose_name='token expires'),
preserve_default=False,
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 5.0.4 on 2024-06-01 16:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0002_discorduser_token_expires'),
]
operations = [
migrations.AddField(
model_name='discorduser',
name='refresh_token',
field=models.CharField(default='1', help_text='token for the application to request a new access token.', max_length=100, verbose_name='refresh token'),
preserve_default=False,
),
]

View File

@ -1,5 +1,7 @@
# -*- encoding: utf-8 -*-
import logging
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
@ -7,6 +9,8 @@ from django.contrib.auth.models import PermissionsMixin
from .managers import DiscordUserOAuth2Manager
log = logging.getLogger(__name__)
class DiscordUser(PermissionsMixin):
"""
@ -65,11 +69,15 @@ class DiscordUser(PermissionsMixin):
max_length=100,
help_text=_("token for the application to make api calls on behalf of the user.")
)
token_expires = models.DateTimeField(
_("token expires"),
help_text=_("when to request a new access token.")
)
refresh_token = models.CharField(
_("refresh token"),
max_length=100,
help_text=_("token for the application to request a new access token.")
)
# Custom Attributes
@ -129,3 +137,23 @@ class DiscordUser(PermissionsMixin):
def get_username(self):
return self.username
def update(self, raw: dict):
log.debug("updating user: %s", self.username)
log.debug("raw update data: %s", raw)
log.debug("public flags: %s, %s", raw["public_flags"], type(raw["public_flags"]))
self.username=raw["username"]
self.global_name=raw["global_name"]
self.avatar=raw["avatar"]
self.public_flags=raw["public_flags"]
self.flags=raw["flags"]
self.locale=raw["locale"]
self.mfa_enabled=raw["mfa_enabled"]
self.access_token=raw["access_token"]
self.token_expires=raw["token_expires"]
self.refresh_token=raw["refresh_token"]
self.save(force_update=True)

View File

@ -2,8 +2,10 @@
import logging
import requests
from datetime import timedelta
from django.conf import settings
from django.utils import timezone
from django.http import JsonResponse
from django.views.generic import View, TemplateView
from django.shortcuts import redirect
@ -31,17 +33,25 @@ class DiscordLoginRedirect(View):
return redirect("auth:login")
code = request.GET.get("code")
access_token, token_expires = self.exchange_code_for_token(code)
raw_user_data = self.get_raw_user_data(access_token)
raw_user_data["access_token"] = access_token
raw_user_data["token_expires"] = token_expires
exchange_data = self.exchange_code(code)
access_token = exchange_data["access_token"]
# Get raw user data from discord
raw_user_data = self.get_raw_user_data(access_token)
# Add the token and expires datetime to the user data
token_expire_datetime = timezone.now() + timedelta(seconds=exchange_data["expires_in"])
raw_user_data["token_expires"] = token_expire_datetime
raw_user_data["access_token"] = access_token
raw_user_data["refresh_token"] = exchange_data["refresh_token"]
# authenticate (creates user if not exists) and login
discord_user = authenticate(request, discord_user_data=raw_user_data)
login(request, discord_user)
return redirect("home:index")
def exchange_code_for_token(self, code: str) -> tuple[str, str]:
def exchange_code(self, code: str) -> dict:
"""
Exchanges the given code for an access token.
A call is made to the Discord API.
@ -59,12 +69,27 @@ class DiscordLoginRedirect(View):
headers=request_data["headers"]
)
resp_json = response.json()
log.debug(resp_json)
access_token = resp_json["access_token"]
expires = resp_json["expires"]
return response.json()
return access_token, expires
def refresh_token(self, refresh_token: str) -> dict:
"""
Refresh the access token if expired, using the refresh
token. Returns a new access token, expire time and refresh
token.
"""
request_data = settings.DISCORD_REFRESH_TOKEN_REQUEST
request_data["data"]["refresh_token"] = refresh_token
log.debug("request data: %s", request_data)
response = requests.post(
url=f"{settings.DISCORD_API_URL}/oauth2/token",
data=request_data["data"],
headers=request_data["headers"]
)
return response.json()
def get_raw_user_data(self, access_token: str):
"""

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-05-31 22:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0021_alter_subscription_filters'),
]
operations = [
migrations.AlterField(
model_name='subscription',
name='filters',
field=models.ManyToManyField(blank=True, to='home.filter'),
),
]

View File

@ -137,7 +137,15 @@ DISCORD_CODE_EXCHANGE_REQUEST = {
"client_secret": DISCORD_SECRET,
"grant_type": "authorization_code",
"redirect_uri": env("DISCORD_REDIRECT_URL"),
"scope": " ".join(DISCORD_SCOPES)
"scope": " ".join(DISCORD_SCOPES),
"code": ""
}
}
DISCORD_REFRESH_TOKEN_REQUEST = {
"headers": {"Content-Type": "application/x-www-form-urlencoded"},
"data": {
"grant_type": "refresh_token",
"refresh_token": ""
}
}
DISCORD_API_URL = env("DISCORD_API_URL")