Phase 3 Implementation: - Token service with secure token generation and validation - Token endpoint (POST /token) with OAuth 2.0 compliance - Database migration 003 for tokens table - Authorization code validation and single-use enforcement Phase 1 Updates: - Enhanced CodeStore to support dict values with JSON serialization - Maintains backward compatibility Phase 2 Updates: - Authorization codes now include PKCE fields, used flag, timestamps - Complete metadata structure for token exchange Security: - 256-bit cryptographically secure tokens (secrets.token_urlsafe) - SHA-256 hashed storage (no plaintext) - Constant-time comparison for validation - Single-use code enforcement with replay detection Testing: - 226 tests passing (100%) - 87.27% coverage (exceeds 80% requirement) - OAuth 2.0 compliance verified This completes the v1.0.0 MVP with full IndieAuth authorization code flow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
107 lines
2.7 KiB
Python
107 lines
2.7 KiB
Python
"""FastAPI dependency injection for services."""
|
|
from functools import lru_cache
|
|
|
|
from gondulf.config import Config
|
|
from gondulf.database.connection import Database
|
|
from gondulf.dns import DNSService
|
|
from gondulf.email import EmailService
|
|
from gondulf.services.domain_verification import DomainVerificationService
|
|
from gondulf.services.html_fetcher import HTMLFetcherService
|
|
from gondulf.services.rate_limiter import RateLimiter
|
|
from gondulf.services.relme_parser import RelMeParser
|
|
from gondulf.services.token_service import TokenService
|
|
from gondulf.storage import CodeStore
|
|
|
|
|
|
# Configuration
|
|
@lru_cache
|
|
def get_config() -> Config:
|
|
"""Get configuration instance."""
|
|
return Config
|
|
|
|
|
|
# Phase 1 Services
|
|
@lru_cache
|
|
def get_database() -> Database:
|
|
"""Get singleton database service."""
|
|
config = get_config()
|
|
db = Database(config.DATABASE_URL)
|
|
db.initialize()
|
|
return db
|
|
|
|
|
|
@lru_cache
|
|
def get_code_storage() -> CodeStore:
|
|
"""Get singleton code storage service."""
|
|
config = get_config()
|
|
return CodeStore(ttl_seconds=config.CODE_EXPIRY)
|
|
|
|
|
|
@lru_cache
|
|
def get_email_service() -> EmailService:
|
|
"""Get singleton email service."""
|
|
config = get_config()
|
|
return EmailService(
|
|
smtp_host=config.SMTP_HOST,
|
|
smtp_port=config.SMTP_PORT,
|
|
smtp_from=config.SMTP_FROM,
|
|
smtp_username=config.SMTP_USERNAME,
|
|
smtp_password=config.SMTP_PASSWORD,
|
|
smtp_use_tls=config.SMTP_USE_TLS
|
|
)
|
|
|
|
|
|
@lru_cache
|
|
def get_dns_service() -> DNSService:
|
|
"""Get singleton DNS service."""
|
|
return DNSService()
|
|
|
|
|
|
# Phase 2 Services
|
|
@lru_cache
|
|
def get_html_fetcher() -> HTMLFetcherService:
|
|
"""Get singleton HTML fetcher service."""
|
|
return HTMLFetcherService()
|
|
|
|
|
|
@lru_cache
|
|
def get_relme_parser() -> RelMeParser:
|
|
"""Get singleton rel=me parser service."""
|
|
return RelMeParser()
|
|
|
|
|
|
@lru_cache
|
|
def get_rate_limiter() -> RateLimiter:
|
|
"""Get singleton rate limiter service."""
|
|
return RateLimiter(max_attempts=3, window_hours=1)
|
|
|
|
|
|
@lru_cache
|
|
def get_verification_service() -> DomainVerificationService:
|
|
"""Get singleton domain verification service."""
|
|
return DomainVerificationService(
|
|
dns_service=get_dns_service(),
|
|
email_service=get_email_service(),
|
|
code_storage=get_code_storage(),
|
|
html_fetcher=get_html_fetcher(),
|
|
relme_parser=get_relme_parser()
|
|
)
|
|
|
|
|
|
# Phase 3 Services
|
|
@lru_cache
|
|
def get_token_service() -> TokenService:
|
|
"""
|
|
Get TokenService singleton.
|
|
|
|
Returns cached instance for dependency injection.
|
|
"""
|
|
database = get_database()
|
|
config = get_config()
|
|
|
|
return TokenService(
|
|
database=database,
|
|
token_length=32, # 256 bits
|
|
token_ttl=config.TOKEN_EXPIRY # From environment (default: 3600)
|
|
)
|