Cybersecurity-Projects/PROJECTS/advanced/encrypted-p2p-chat/backend/app/models/Credential.py

79 lines
2.1 KiB
Python

"""
ⒸAngelaMos | 2025
WebAuthn credential model for passkey storage
"""
from datetime import datetime
from typing import TYPE_CHECKING
from uuid import UUID
from sqlalchemy import DateTime
from sqlmodel import Field, Relationship
from app.config import (
AAGUID_MAX_LENGTH,
ATTESTATION_TYPE_MAX_LENGTH,
CREDENTIAL_ID_MAX_LENGTH,
DISPLAY_NAME_MAX_LENGTH,
PUBLIC_KEY_MAX_LENGTH,
TRANSPORT_MAX_LENGTH,
)
from app.models.Base import BaseDBModel
if TYPE_CHECKING:
from app.models.User import User
class Credential(BaseDBModel, table = True):
"""
WebAuthn/FIDO2 passkey credential
"""
__tablename__ = "credentials"
id: int = Field(default = None, primary_key = True)
credential_id: str = Field(
unique = True,
index = True,
nullable = False,
max_length = CREDENTIAL_ID_MAX_LENGTH
)
public_key: str = Field(nullable = False, max_length = PUBLIC_KEY_MAX_LENGTH)
sign_count: int = Field(default = 0, nullable = False)
aaguid: str | None = Field(default = None, max_length = AAGUID_MAX_LENGTH)
# WebAuthn Level 3 fields
backup_eligible: bool = Field(default = False, nullable = False)
backup_state: bool = Field(default = False, nullable = False)
attestation_type: str | None = Field(
default = None,
max_length = ATTESTATION_TYPE_MAX_LENGTH
)
transports: str | None = Field(
default = None,
max_length = TRANSPORT_MAX_LENGTH
)
# User relationship
user_id: UUID = Field(
foreign_key = "users.id",
nullable = False,
index = True
)
user: "User" = Relationship(back_populates = "credentials")
# Device metadata
device_name: str | None = Field(
default = None,
max_length = DISPLAY_NAME_MAX_LENGTH
)
last_used_at: datetime | None = Field(
default = None,
sa_type = DateTime(timezone = True),
)
def __repr__(self) -> str:
"""
String representation of Credential
"""
return f"<Credential {self.credential_id[:16]}...>"