mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 02:35:18 -05:00
feat: add security event taxonomy and optimize park queryset - Add comprehensive security_event_types ChoiceGroup with categories for authentication, MFA, password, account, session, and API key events - Include severity levels, icons, and CSS classes for each event type - Fix park queryset optimization by using select_related for OneToOne location relationship - Remove location property fields (latitude/longitude) from values() call as they are not actual DB columns - Add proper location fields (city, state, country) to values() for map display This change enhances security event tracking capabilities and resolves a queryset optimization issue where property decorators were incorrectly used in values() queries.
97 lines
2.6 KiB
Python
97 lines
2.6 KiB
Python
"""
|
|
Custom JWT Token Generation for ThrillWiki
|
|
|
|
This module provides custom JWT token generation that includes authentication
|
|
method claims for enhanced MFA satisfaction logic.
|
|
|
|
Claims added:
|
|
- auth_method: How the user authenticated (password, passkey, totp, google, discord)
|
|
- mfa_verified: Whether MFA was verified during this login
|
|
- provider_mfa: Whether the OAuth provider (Discord) has MFA enabled
|
|
"""
|
|
|
|
from typing import Literal, TypedDict
|
|
|
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
|
|
# Type definitions for auth methods
|
|
AuthMethod = Literal["password", "passkey", "totp", "google", "discord"]
|
|
|
|
|
|
class TokenClaims(TypedDict, total=False):
|
|
"""Type definition for custom JWT claims."""
|
|
|
|
auth_method: AuthMethod
|
|
mfa_verified: bool
|
|
provider_mfa: bool
|
|
|
|
|
|
def create_tokens_for_user(
|
|
user,
|
|
auth_method: AuthMethod = "password",
|
|
mfa_verified: bool = False,
|
|
provider_mfa: bool = False,
|
|
) -> dict[str, str]:
|
|
"""
|
|
Generate JWT tokens with custom authentication claims.
|
|
|
|
Args:
|
|
user: The Django user object
|
|
auth_method: How the user authenticated
|
|
mfa_verified: True if MFA (TOTP/passkey) was verified at login
|
|
provider_mfa: True if OAuth provider (Discord) has MFA enabled
|
|
|
|
Returns:
|
|
Dictionary with 'access' and 'refresh' token strings
|
|
"""
|
|
refresh = RefreshToken.for_user(user)
|
|
|
|
# Add custom claims to both refresh and access tokens
|
|
refresh["auth_method"] = auth_method
|
|
refresh["mfa_verified"] = mfa_verified
|
|
refresh["provider_mfa"] = provider_mfa
|
|
|
|
access = refresh.access_token
|
|
|
|
return {
|
|
"access": str(access),
|
|
"refresh": str(refresh),
|
|
}
|
|
|
|
|
|
def get_auth_method_for_provider(provider: str) -> AuthMethod:
|
|
"""
|
|
Map OAuth provider name to AuthMethod type.
|
|
|
|
Args:
|
|
provider: The provider name (e.g., 'google', 'discord')
|
|
|
|
Returns:
|
|
The corresponding AuthMethod
|
|
"""
|
|
provider_map: dict[str, AuthMethod] = {
|
|
"google": "google",
|
|
"discord": "discord",
|
|
}
|
|
return provider_map.get(provider, "password")
|
|
|
|
|
|
def get_provider_mfa_status(provider: str, extra_data: dict) -> bool:
|
|
"""
|
|
Extract MFA status from OAuth provider extra_data.
|
|
|
|
Only Discord exposes mfa_enabled. Google does not share this info.
|
|
|
|
Args:
|
|
provider: The OAuth provider name
|
|
extra_data: The extra_data dict from SocialAccount
|
|
|
|
Returns:
|
|
True if provider has MFA enabled, False otherwise
|
|
"""
|
|
if provider == "discord":
|
|
return extra_data.get("mfa_enabled", False)
|
|
|
|
# Google and other providers don't expose MFA status
|
|
return False
|