mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:31:13 -05:00
4.0 KiB
4.0 KiB
Passkey/WebAuthn Implementation Plan
Status: 🟡 In Progress
Priority: CRITICAL (Required for Phase 2 Authentication)
Estimated Time: 12-16 hours
Overview
Implementing passkey/WebAuthn support to provide modern, passwordless authentication as required by Phase 2 of the authentication migration. This will work alongside existing JWT/password authentication.
Architecture
Backend (Django)
- WebAuthn Library:
webauthn==2.1.0(already added to requirements) - Storage: PostgreSQL models for storing passkey credentials
- Integration: Works with existing JWT authentication system
Frontend (Next.js)
- Browser API: Native WebAuthn API (navigator.credentials)
- Fallback: Graceful degradation for unsupported browsers
- Integration: Seamless integration with AuthContext
Phase 1: Django Backend Implementation
1.1: Database Models
File: django/apps/users/models.py
class PasskeyCredential(models.Model):
"""
Stores WebAuthn/Passkey credentials for users.
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='passkey_credentials')
# WebAuthn credential data
credential_id = models.TextField(unique=True, db_index=True)
credential_public_key = models.TextField()
sign_count = models.PositiveIntegerField(default=0)
# Metadata
name = models.CharField(max_length=255, help_text="User-friendly name (e.g., 'iPhone 15', 'YubiKey')")
aaguid = models.CharField(max_length=36, blank=True)
transports = models.JSONField(default=list, help_text="Supported transports: ['usb', 'nfc', 'ble', 'internal']")
# Attestation
attestation_object = models.TextField(blank=True)
attestation_client_data = models.TextField(blank=True)
# Tracking
created_at = models.DateTimeField(auto_now_add=True)
last_used_at = models.DateTimeField(null=True, blank=True)
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'users_passkey_credentials'
ordering = ['-created_at']
def __str__(self):
return f"{self.user.email} - {self.name}"
1.2: Service Layer
File: django/apps/users/services/passkey_service.py
from webauthn import (
generate_registration_options,
verify_registration_response,
generate_authentication_options,
verify_authentication_response,
options_to_json,
)
from webauthn.helpers.structs import (
AuthenticatorSelectionCriteria,
UserVerificationRequirement,
AuthenticatorAttachment,
ResidentKeyRequirement,
)
class PasskeyService:
"""Service for handling WebAuthn/Passkey operations."""
RP_ID = settings.PASSKEY_RP_ID # e.g., "thrillwiki.com"
RP_NAME = "ThrillWiki"
ORIGIN = settings.PASSKEY_ORIGIN # e.g., "https://thrillwiki.com"
@staticmethod
def generate_registration_options(user: User) -> dict:
"""Generate options for passkey registration."""
@staticmethod
def verify_registration(user: User, credential_data: dict, name: str) -> PasskeyCredential:
"""Verify and store a new passkey credential."""
@staticmethod
def generate_authentication_options(user: User = None) -> dict:
"""Generate options for passkey authentication."""
@staticmethod
def verify_authentication(credential_data: dict) -> User:
"""Verify passkey authentication and return user."""
@staticmethod
def list_credentials(user: User) -> List[PasskeyCredential]:
"""List all passkey credentials for a user."""
@staticmethod
def remove_credential(user: User, credential_id: str) -> bool:
"""Remove a passkey credential."""
1.3: API Endpoints
File: django/api/v1/endpoints/auth.py (additions)
# Passkey Registration
@router.post("/passkey/register/options", auth=jwt_auth, response={200: dict})