78 lines
3.1 KiB
Markdown
78 lines
3.1 KiB
Markdown
# Authentication Services — Design Document
|
|
|
|
> **For Claude:** This document captures design decisions for the authentication service layer.
|
|
> The implementation plan is at `docs/plans/2026-02-13-auth-services-plan.md`.
|
|
|
|
## Scope
|
|
|
|
Backend-only authentication services. No HTTP routes or templates in this phase.
|
|
|
|
Three independent services:
|
|
1. **PasswordService** — Argon2 hash/verify
|
|
2. **WebAuthnService** — FIDO2 registration/authentication via python-fido2
|
|
3. **MagicLinkService** — Invite token create/validate
|
|
|
|
## Design Decisions
|
|
|
|
### 1. PasswordService (stateless, pure crypto)
|
|
|
|
- Wraps `argon2-cffi` `PasswordHasher`
|
|
- Two methods: `hash(password) -> str`, `verify(hash, password) -> bool`
|
|
- `PasswordHasher` injected via constructor for testability
|
|
- No repository dependency — caller reads/writes credentials
|
|
- Uses argon2-cffi OWASP-recommended defaults
|
|
- `verify()` returns `bool` (catches `VerifyMismatchError` internally)
|
|
|
|
### 2. WebAuthnService (class wrapping Fido2Server)
|
|
|
|
- Constructor takes `rp_id`, `rp_name`, `origin` — creates `Fido2Server` internally
|
|
- Four methods: `begin_registration`, `complete_registration`, `begin_authentication`, `complete_authentication`
|
|
- `begin_*` methods return `(options, state)` — caller stores state (e.g., in HTTP session)
|
|
- `complete_*` methods accept state back from caller
|
|
- No repository dependency — caller handles credential CRUD
|
|
- Uses python-fido2 v2.1 API (`Fido2Server`, `PublicKeyCredentialRpEntity`, etc.)
|
|
|
|
### 3. MagicLinkService (token lifecycle)
|
|
|
|
- Depends on `MagicLinkRepository` (injected)
|
|
- `create(username, created_by?, note?) -> MagicLink` — generates token via `secrets.token_urlsafe(32)`
|
|
- `validate(token) -> MagicLink | None` — checks exists, not used, not expired
|
|
- `mark_used(token) -> bool` — delegates to repo
|
|
- `cleanup_expired() -> int` — delegates to repo
|
|
- TTL configurable via constructor (from `Settings.invite_ttl`)
|
|
|
|
### 4. Testing Strategy
|
|
|
|
- **Password:** Roundtrip hash/verify, invalid password returns False, fast hasher params for tests
|
|
- **WebAuthn:** Build registration/authentication responses manually using fido2's `create()` factory methods (`AttestedCredentialData.create`, `AuthenticatorData.create`, `CollectedClientData.create`, etc.) with real ES256 keys from `cryptography` library. No external test utility package needed.
|
|
- **MagicLink:** In-memory SQLite fixtures, test create/validate/expired/used states
|
|
|
|
### 5. Dependencies
|
|
|
|
All already in `pyproject.toml`:
|
|
- `fido2>=2.1` (python-fido2)
|
|
- `argon2-cffi>=25.1`
|
|
|
|
No new dependencies required.
|
|
|
|
## File Structure
|
|
|
|
```
|
|
src/fastapi_oidc_op/
|
|
├── authn/
|
|
│ ├── __init__.py (exists, empty)
|
|
│ ├── password.py (new)
|
|
│ └── webauthn.py (new)
|
|
├── invite/
|
|
│ ├── __init__.py (exists, empty)
|
|
│ └── service.py (new)
|
|
|
|
tests/
|
|
├── test_authn/
|
|
│ ├── __init__.py (new)
|
|
│ ├── test_password.py (new)
|
|
│ └── test_webauthn.py (new)
|
|
├── test_invite/
|
|
│ ├── __init__.py (new)
|
|
│ └── test_service.py (new)
|
|
```
|