mirror of
https://github.com/Lyxminxx/jast.git
synced 2026-10-11 20:57:43 +02:00
Wire JWT lifetimes to settings, read secret key from env
This commit is contained in:
1 parent
b95303caa9
commit
6f68f71ce3
3 files changed
+46
-21
No files matched your search
+13
-14
@@ -1,5 +1,5 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import datetime, timedelta, date
|
from datetime import datetime, timedelta, timezone, date
|
||||||
from typing import List
|
from typing import List
|
||||||
import jwt
|
import jwt
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -22,31 +22,30 @@ class JWTAuth(HttpBearer):
|
|||||||
return None
|
return None
|
||||||
user = User.objects.get(id=payload["user_id"])
|
user = User.objects.get(id=payload["user_id"])
|
||||||
return user
|
return user
|
||||||
except (jwt.PyJWTError, User.DoesNotExist):
|
except (jwt.PyJWTError, User.DoesNotExist, KeyError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
auth = JWTAuth()
|
auth = JWTAuth()
|
||||||
|
|
||||||
|
|
||||||
def generate_access_token(user: User) -> str:
|
def _generate_token(user: User, token_type: str, lifetime: timedelta) -> str:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
payload = {
|
payload = {
|
||||||
"user_id": user.id,
|
"user_id": user.id,
|
||||||
"type": "access",
|
"type": token_type,
|
||||||
"exp": datetime.utcnow() + timedelta(minutes=30),
|
"exp": now + lifetime,
|
||||||
"iat": datetime.utcnow(),
|
"iat": now,
|
||||||
}
|
}
|
||||||
return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_access_token(user: User) -> str:
|
||||||
|
return _generate_token(user, "access", settings.JWT_ACCESS_TOKEN_LIFETIME)
|
||||||
|
|
||||||
|
|
||||||
def generate_refresh_token(user: User) -> str:
|
def generate_refresh_token(user: User) -> str:
|
||||||
payload = {
|
return _generate_token(user, "refresh", settings.JWT_REFRESH_TOKEN_LIFETIME)
|
||||||
"user_id": user.id,
|
|
||||||
"type": "refresh",
|
|
||||||
"exp": datetime.utcnow() + timedelta(days=14),
|
|
||||||
"iat": datetime.utcnow(),
|
|
||||||
}
|
|
||||||
return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
|
||||||
|
|
||||||
|
|
||||||
# Schemas
|
# Schemas
|
||||||
@@ -190,7 +189,7 @@ def refresh_token_view(request, payload: RefreshIn):
|
|||||||
user = User.objects.get(id=data["user_id"])
|
user = User.objects.get(id=data["user_id"])
|
||||||
new_access_token = generate_access_token(user)
|
new_access_token = generate_access_token(user)
|
||||||
return {"access_token": new_access_token}
|
return {"access_token": new_access_token}
|
||||||
except (jwt.PyJWTError, User.DoesNotExist):
|
except (jwt.PyJWTError, User.DoesNotExist, KeyError):
|
||||||
raise HttpError(401, "Invalid or expired refresh token")
|
raise HttpError(401, "Invalid or expired refresh token")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+29
-7
@@ -17,17 +17,43 @@ import os
|
|||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
DATA_DIR = BASE_DIR / 'data'
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/
|
||||||
|
|
||||||
|
def _read_secret_key() -> str:
|
||||||
|
"""Prefer DJANGO_SECRET_KEY, else a key persisted in the (gitignored) data dir."""
|
||||||
|
env_key = os.getenv('DJANGO_SECRET_KEY', '').strip()
|
||||||
|
if env_key:
|
||||||
|
return env_key
|
||||||
|
|
||||||
|
key_file = DATA_DIR / 'secret_key.txt'
|
||||||
|
if key_file.exists():
|
||||||
|
stored = key_file.read_text().strip()
|
||||||
|
if stored:
|
||||||
|
return stored
|
||||||
|
|
||||||
|
from django.core.management.utils import get_random_secret_key
|
||||||
|
|
||||||
|
generated = get_random_secret_key()
|
||||||
|
key_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
key_file.write_text(generated)
|
||||||
|
key_file.chmod(0o600)
|
||||||
|
return generated
|
||||||
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = 'django-insecure-m#==%hz74d192o)4!#_!dpz%$yox@!hkabc9)pggkkiuvc7((q'
|
# This also signs every JWT, so leaking it means anyone can forge a token.
|
||||||
|
SECRET_KEY = _read_secret_key()
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
# Lifetimes for the hand-rolled JWTs issued in core/api.py.
|
||||||
|
JWT_ACCESS_TOKEN_LIFETIME = timedelta(days=int(os.getenv('JWT_ACCESS_DAYS', '14')))
|
||||||
|
JWT_REFRESH_TOKEN_LIFETIME = timedelta(days=int(os.getenv('JWT_REFRESH_DAYS', '30')))
|
||||||
|
|
||||||
ALLOWED_HOSTS = os.getenv(
|
ALLOWED_HOSTS = os.getenv(
|
||||||
"ALLOWED_HOSTS",
|
"ALLOWED_HOSTS",
|
||||||
@@ -107,7 +133,7 @@ WSGI_APPLICATION = 'core.wsgi.application'
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': BASE_DIR / 'data' / 'db.sqlite3',
|
'NAME': DATA_DIR / 'db.sqlite3',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +194,3 @@ SESSION_ENGINE = "django.contrib.sessions.backends.db"
|
|||||||
SESSION_COOKIE_AGE = 1209600 # Persist session for 2 weeks (in seconds)
|
SESSION_COOKIE_AGE = 1209600 # Persist session for 2 weeks (in seconds)
|
||||||
SESSION_SAVE_EVERY_REQUEST = True
|
SESSION_SAVE_EVERY_REQUEST = True
|
||||||
SESSION_COOKIE_HTTPONLY = True
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
NINJA_JWT = {
|
|
||||||
'ACCESS_TOKEN_LIFETIME': timedelta(days=14),
|
|
||||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
|
|
||||||
}
|
|
||||||
@@ -8,4 +8,8 @@ services:
|
|||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
|
environment:
|
||||||
|
JWT_ACCESS_DAYS: "14"
|
||||||
|
JWT_REFRESH_DAYS: "30"
|
||||||
|
# DJANGO_SECRET_KEY: "..." # else auto-generated into ./data/secret_key.txt
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
Reference in new issue
Block a user