Coverage for src/lexigram/auth/di/provider.py: 0%
38 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 12:26 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 12:26 +0800
1"""DI provider for lexigram-auth.
3This provider wires the auth subsystem with ClockProtocol, IdGeneratorProtocol,
4TracerProtocol, and MetricsCollectorProtocol for testability and hardening.
5"""
7from __future__ import annotations
9from typing import TYPE_CHECKING, Any
11from lexigram.auth.authn.password_hasher import Argon2idKeyDerivation
12from lexigram.auth.config import AuthConfig
13from lexigram.contracts.core import HealthCheckResult, HealthStatus, ProviderPriority
14from lexigram.contracts.core.health import HealthCheckCategory
15from lexigram.contracts.security.protocols import KeyDerivationProtocol
16from lexigram.di.provider import Provider
17from lexigram.logging import get_logger
19if TYPE_CHECKING:
20 from lexigram.contracts.core.di import (
21 ContainerRegistrarProtocol,
22 ContainerResolverProtocol,
23 )
25logger = get_logger(__name__)
27__all__ = ["AuthProvider"]
30class AuthProvider(Provider):
31 """Authentication provider for Lexigram Framework.
33 This provider wires:
34 - ClockProtocol for token/session timestamps
35 - IdGeneratorProtocol for session/token IDs
36 - TracerProtocol for observability
37 - MetricsCollectorProtocol for metrics
38 - KeyDerivationProtocol (Argon2id) for key derivation
39 """
41 name = "auth"
42 priority = ProviderPriority.SECURITY
44 config_key: str | None = "auth"
45 config_model: type | None = AuthConfig
47 def __init__(self, config: AuthConfig | None = None) -> None:
48 super().__init__()
49 self._config = config or AuthConfig()
51 @classmethod
52 def from_config(cls, config: AuthConfig, **context: Any) -> AuthProvider:
53 return cls(config=config)
55 async def register(self, container: ContainerRegistrarProtocol) -> None:
56 """Register auth services with the DI container."""
57 container.singleton(AuthConfig, instance=self._config)
59 container.singleton(
60 KeyDerivationProtocol,
61 factory=lambda _: Argon2idKeyDerivation(config=self._config.password),
62 )
64 if self._config.relay_verification:
65 from lexigram.auth.authn.apikeys import APIKeyManager
66 from lexigram.auth.authn.relay import RelayApiKeyVerifier
67 from lexigram.contracts.ai.relay import RelayAuthVerifierProtocol
68 from lexigram.contracts.auth import APIKeyRepositoryProtocol
70 if not container.has(APIKeyRepositoryProtocol):
71 logger.warning(
72 "relay_verifier_skipped",
73 reason="APIKeyRepositoryProtocol not bound",
74 )
75 return
76 container.singleton(
77 RelayAuthVerifierProtocol,
78 factory=lambda c: RelayApiKeyVerifier(
79 manager=APIKeyManager(repo=c.resolve(APIKeyRepositoryProtocol))
80 ),
81 )
83 async def boot(self, container: ContainerResolverProtocol) -> None:
84 """Boot the auth provider."""
86 async def shutdown(self) -> None:
87 """Shutdown the auth provider."""
89 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult:
90 """Return health status of the auth provider."""
91 return HealthCheckResult(
92 component=self.name,
93 status=HealthStatus.HEALTHY,
94 category=HealthCheckCategory.READINESS,
95 )