mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 03:51:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
127
django-backend/PASSKEY_WEBAUTHN_IMPLEMENTATION_PLAN.md
Normal file
127
django-backend/PASSKEY_WEBAUTHN_IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 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`
|
||||
|
||||
```python
|
||||
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`
|
||||
|
||||
```python
|
||||
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)
|
||||
|
||||
```python
|
||||
# Passkey Registration
|
||||
@router.post("/passkey/register/options", auth=jwt_auth, response={200: dict})
|
||||
Reference in New Issue
Block a user