mdb-engine 0.1.6__py3-none-any.whl → 0.4.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. mdb_engine/__init__.py +116 -11
  2. mdb_engine/auth/ARCHITECTURE.md +112 -0
  3. mdb_engine/auth/README.md +654 -11
  4. mdb_engine/auth/__init__.py +136 -29
  5. mdb_engine/auth/audit.py +592 -0
  6. mdb_engine/auth/base.py +252 -0
  7. mdb_engine/auth/casbin_factory.py +265 -70
  8. mdb_engine/auth/config_defaults.py +5 -5
  9. mdb_engine/auth/config_helpers.py +19 -18
  10. mdb_engine/auth/cookie_utils.py +12 -16
  11. mdb_engine/auth/csrf.py +483 -0
  12. mdb_engine/auth/decorators.py +10 -16
  13. mdb_engine/auth/dependencies.py +69 -71
  14. mdb_engine/auth/helpers.py +3 -3
  15. mdb_engine/auth/integration.py +61 -88
  16. mdb_engine/auth/jwt.py +11 -15
  17. mdb_engine/auth/middleware.py +79 -35
  18. mdb_engine/auth/oso_factory.py +21 -41
  19. mdb_engine/auth/provider.py +270 -171
  20. mdb_engine/auth/rate_limiter.py +505 -0
  21. mdb_engine/auth/restrictions.py +21 -36
  22. mdb_engine/auth/session_manager.py +24 -41
  23. mdb_engine/auth/shared_middleware.py +977 -0
  24. mdb_engine/auth/shared_users.py +775 -0
  25. mdb_engine/auth/token_lifecycle.py +10 -12
  26. mdb_engine/auth/token_store.py +17 -32
  27. mdb_engine/auth/users.py +99 -159
  28. mdb_engine/auth/utils.py +236 -42
  29. mdb_engine/cli/commands/generate.py +546 -10
  30. mdb_engine/cli/commands/validate.py +3 -7
  31. mdb_engine/cli/utils.py +7 -7
  32. mdb_engine/config.py +13 -28
  33. mdb_engine/constants.py +65 -0
  34. mdb_engine/core/README.md +117 -6
  35. mdb_engine/core/__init__.py +39 -7
  36. mdb_engine/core/app_registration.py +31 -50
  37. mdb_engine/core/app_secrets.py +289 -0
  38. mdb_engine/core/connection.py +20 -12
  39. mdb_engine/core/encryption.py +222 -0
  40. mdb_engine/core/engine.py +2862 -115
  41. mdb_engine/core/index_management.py +12 -16
  42. mdb_engine/core/manifest.py +628 -204
  43. mdb_engine/core/ray_integration.py +436 -0
  44. mdb_engine/core/seeding.py +13 -21
  45. mdb_engine/core/service_initialization.py +20 -30
  46. mdb_engine/core/types.py +40 -43
  47. mdb_engine/database/README.md +140 -17
  48. mdb_engine/database/__init__.py +17 -6
  49. mdb_engine/database/abstraction.py +37 -50
  50. mdb_engine/database/connection.py +51 -30
  51. mdb_engine/database/query_validator.py +367 -0
  52. mdb_engine/database/resource_limiter.py +204 -0
  53. mdb_engine/database/scoped_wrapper.py +747 -237
  54. mdb_engine/dependencies.py +427 -0
  55. mdb_engine/di/__init__.py +34 -0
  56. mdb_engine/di/container.py +247 -0
  57. mdb_engine/di/providers.py +206 -0
  58. mdb_engine/di/scopes.py +139 -0
  59. mdb_engine/embeddings/README.md +54 -24
  60. mdb_engine/embeddings/__init__.py +31 -24
  61. mdb_engine/embeddings/dependencies.py +38 -155
  62. mdb_engine/embeddings/service.py +78 -75
  63. mdb_engine/exceptions.py +104 -12
  64. mdb_engine/indexes/README.md +30 -13
  65. mdb_engine/indexes/__init__.py +1 -0
  66. mdb_engine/indexes/helpers.py +11 -11
  67. mdb_engine/indexes/manager.py +59 -123
  68. mdb_engine/memory/README.md +95 -4
  69. mdb_engine/memory/__init__.py +1 -2
  70. mdb_engine/memory/service.py +363 -1168
  71. mdb_engine/observability/README.md +4 -2
  72. mdb_engine/observability/__init__.py +26 -9
  73. mdb_engine/observability/health.py +17 -17
  74. mdb_engine/observability/logging.py +10 -10
  75. mdb_engine/observability/metrics.py +40 -19
  76. mdb_engine/repositories/__init__.py +34 -0
  77. mdb_engine/repositories/base.py +325 -0
  78. mdb_engine/repositories/mongo.py +233 -0
  79. mdb_engine/repositories/unit_of_work.py +166 -0
  80. mdb_engine/routing/README.md +1 -1
  81. mdb_engine/routing/__init__.py +1 -3
  82. mdb_engine/routing/websockets.py +41 -75
  83. mdb_engine/utils/__init__.py +3 -1
  84. mdb_engine/utils/mongo.py +117 -0
  85. mdb_engine-0.4.12.dist-info/METADATA +492 -0
  86. mdb_engine-0.4.12.dist-info/RECORD +97 -0
  87. {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/WHEEL +1 -1
  88. mdb_engine-0.1.6.dist-info/METADATA +0 -213
  89. mdb_engine-0.1.6.dist-info/RECORD +0 -75
  90. {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/entry_points.txt +0 -0
  91. {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/licenses/LICENSE +0 -0
  92. {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/top_level.txt +0 -0
mdb_engine/auth/README.md CHANGED
@@ -10,6 +10,82 @@ The engine provides **MongoDB-backed conveniences** without imposing solutions:
10
10
  - **MongoDB-first**: All auth data (policies, sessions, tokens) is stored in MongoDB, leveraging the engine's scoping and isolation features
11
11
  - **Pluggable authorization**: Choose Casbin (MongoDB-backed RBAC) or OSO (Cloud or library) - both auto-configured from manifest
12
12
  - **App-level flexibility**: Apps can implement OAuth, custom auth flows, or use the provided app-level user management utilities
13
+ - **Two auth modes**: Choose between per-app isolation (`mode: "app"`) or shared user pool with SSO (`mode: "shared"`)
14
+
15
+ ## Auth Modes
16
+
17
+ MDB_ENGINE supports two authentication modes, configured in manifest.json:
18
+
19
+ ### Per-App Auth (`mode: "app"`) - Default
20
+
21
+ Each app has isolated authentication. Users, tokens, and sessions are specific to each app.
22
+
23
+ ```json
24
+ {
25
+ "auth": {
26
+ "mode": "app",
27
+ "token_required": true
28
+ }
29
+ }
30
+ ```
31
+
32
+ **When to use:**
33
+ - Apps are independent
34
+ - Each app manages its own users
35
+ - No need for SSO between apps
36
+
37
+ ### Shared Auth (`mode: "shared"`) - SSO
38
+
39
+ All apps share a central user pool. Users authenticate once and can access any app (subject to role requirements).
40
+
41
+ ```json
42
+ {
43
+ "auth": {
44
+ "mode": "shared",
45
+ "auth_hub_url": "http://localhost:8000",
46
+ "related_apps": {
47
+ "dashboard": "http://localhost:8001"
48
+ },
49
+ "roles": ["viewer", "editor", "admin"],
50
+ "default_role": "viewer",
51
+ "require_role": "viewer",
52
+ "public_routes": ["/health", "/api/public"]
53
+ }
54
+ }
55
+ ```
56
+
57
+ **When to use:**
58
+ - Building a platform with multiple related apps
59
+ - You want Single Sign-On (SSO)
60
+ - You need per-app role management
61
+ - Apps should share user identity
62
+
63
+ **Shared auth fields:**
64
+ | Field | Description |
65
+ |-------|-------------|
66
+ | `roles` | Available roles for this app |
67
+ | `auth_hub_url` | URL of the authentication hub for SSO apps. Used for redirecting unauthenticated users to login. Can be overridden via `AUTH_HUB_URL` environment variable |
68
+ | `related_apps` | Map of related app slugs to their URLs for cross-app navigation. Keys are app slugs, values are URLs. Can be overridden via `{APP_SLUG_UPPER}_URL` environment variables |
69
+ | `default_role` | Role assigned to new users |
70
+ | `require_role` | Minimum role required to access app |
71
+ | `public_routes` | Routes that don't require authentication |
72
+
73
+ **How it works:**
74
+ 1. Users are stored in `_mdb_engine_shared_users` collection
75
+ 2. JWT tokens work across all apps (SSO)
76
+ 3. `SharedAuthMiddleware` is auto-configured by `engine.create_app()`
77
+ 4. User info is available via `request.state.user`
78
+
79
+ ```python
80
+ # Accessing user in shared auth mode
81
+ @app.get("/protected")
82
+ async def protected(request: Request):
83
+ user = request.state.user # Populated by middleware
84
+ roles = request.state.user_roles
85
+ return {"email": user["email"], "roles": roles}
86
+ ```
87
+
88
+ See `examples/multi_app_shared/` for a complete SSO example.
13
89
 
14
90
  ## Features
15
91
 
@@ -265,11 +341,12 @@ The provider is automatically created and available via `get_authz_provider` dep
265
341
  ```python
266
342
  from mdb_engine.auth import CasbinAdapter, create_casbin_enforcer
267
343
 
268
- # Create enforcer with MongoDB adapter
344
+ # Create enforcer with MongoDB adapter (uses scoped database)
345
+ db = engine.get_scoped_db("my_app")
269
346
  enforcer = await create_casbin_enforcer(
270
- db=engine.get_database(),
347
+ db=db,
271
348
  model="rbac",
272
- policies_collection="casbin_policies"
349
+ policies_collection="casbin_policies" # Will be app-scoped
273
350
  )
274
351
 
275
352
  # Create adapter
@@ -534,12 +611,70 @@ async def logout(response: Response):
534
611
  - `get_or_create_anonymous_user(db, app_slug, device_id)` - Get/create anonymous user
535
612
  - `get_or_create_demo_user(db, app_slug, device_id)` - Get/create demo user
536
613
 
614
+ ### Shared Auth (SSO)
615
+
616
+ - `SharedUserPool(mongo_db, jwt_secret, jwt_public_key, jwt_algorithm, ...)` - Shared user pool for SSO
617
+ - `create_user(email, password, app_roles)` - Create user in shared pool
618
+ - `authenticate(email, password, ip_address, fingerprint, session_binding)` - Authenticate with session binding
619
+ - `validate_token(token)` - Validate JWT and get user (checks blacklist)
620
+ - `revoke_token(token, reason)` - Revoke token by adding JTI to blacklist
621
+ - `revoke_all_user_tokens(user_id, reason)` - Revoke all user tokens
622
+ - `update_user_roles(email, app_slug, roles)` - Update user's roles for an app
623
+ - `user_has_role(user, app_slug, role)` - Check if user has role
624
+ - `get_secure_cookie_config(request)` - Get secure cookie settings
625
+ - `jwt_algorithm` - Property: configured algorithm (HS256, RS256, ES256)
626
+ - `is_asymmetric` - Property: True if using asymmetric algorithm
627
+ - `JWTSecretError` - Raised when JWT secret is missing
628
+ - `JWTKeyError` - Raised when JWT key configuration is invalid
629
+ - `SharedAuthMiddleware` - ASGI middleware for shared auth (supports session binding)
630
+ - `create_shared_auth_middleware(pool, slug, manifest_auth)` - Factory for configured middleware
631
+ - `create_shared_auth_middleware_lazy(slug, manifest_auth)` - Lazy factory for engine integration
632
+
633
+ ### Rate Limiting
634
+
635
+ - `AuthRateLimitMiddleware(app, limits, store)` - ASGI rate limiting middleware
636
+ - `RateLimit(max_attempts, window_seconds)` - Rate limit configuration
637
+ - `InMemoryRateLimitStore` - In-memory storage for rate limiting
638
+ - `MongoDBRateLimitStore(db)` - MongoDB-backed distributed rate limiting
639
+ - `create_rate_limit_middleware(manifest_auth, store)` - Factory from manifest config
640
+ - `@rate_limit(max_attempts, window_seconds)` - Decorator for individual endpoints
641
+
642
+ ### Audit Logging
643
+
644
+ - `AuthAuditLog(mongo_db, retention_days=90)` - Audit logger for auth events
645
+ - `log_event(action, success, user_email, ip_address, details)` - Log any event
646
+ - `log_login_success(email, ip_address, ...)` - Log successful login
647
+ - `log_login_failed(email, reason, ip_address, ...)` - Log failed login
648
+ - `log_logout(email, ip_address, ...)` - Log logout
649
+ - `log_register(email, ip_address, ...)` - Log registration
650
+ - `log_role_change(email, app_slug, old_roles, new_roles, ...)` - Log role change
651
+ - `log_token_revoked(email, reason, ...)` - Log token revocation
652
+ - `get_recent_events(hours, action, success)` - Query recent events
653
+ - `get_failed_logins(email, ip_address, hours)` - Query failed logins
654
+ - `get_security_summary(hours)` - Get security statistics
655
+ - `AuthAction` - Enum of audit action types
656
+
657
+ ### CSRF Protection
658
+
659
+ - `CSRFMiddleware(app, secret, exempt_routes, ...)` - CSRF protection middleware
660
+ - `create_csrf_middleware(manifest_auth)` - Factory from manifest config
661
+ - `generate_csrf_token(secret)` - Generate CSRF token
662
+ - `validate_csrf_token(token, secret, max_age)` - Validate CSRF token
663
+ - `get_csrf_token(request)` - FastAPI dependency for getting CSRF token
664
+
665
+ ### Password Policy
666
+
667
+ - `validate_password_strength(password, config)` - Validate password strength
668
+ - `validate_password_strength_async(password, config, check_breaches)` - Async validation with breach check
669
+ - `calculate_password_entropy(password)` - Calculate entropy in bits
670
+ - `is_common_password(password)` - Check against common password list
671
+ - `check_password_breach(password)` - Check against HaveIBeenPwned (async)
672
+
537
673
  ### Utilities
538
674
 
539
675
  - `login_user(db, email, password, response)` - Login user and set cookies
540
676
  - `register_user(db, email, password, **kwargs)` - Register new user
541
677
  - `logout_user(response)` - Logout user and clear cookies
542
- - `validate_password_strength(password)` - Validate password strength
543
678
 
544
679
  ## Decorators
545
680
 
@@ -595,16 +730,524 @@ async def login(credentials: dict):
595
730
  9. **Log authentication events** - Track login, logout, and permission failures
596
731
  10. **Use sub-authentication** - Isolate app-level users for multi-tenant applications
597
732
 
733
+ ## Enterprise Security Features
734
+
735
+ MDB_ENGINE auth includes enterprise-grade security features for production deployments.
736
+
737
+ ### JWT Secret Validation (Fail-Fast)
738
+
739
+ The `SharedUserPool` now **requires** a JWT secret - it will fail at startup if not configured:
740
+
741
+ ```python
742
+ from mdb_engine.auth import SharedUserPool, JWTSecretError
743
+
744
+ # Production: Requires MDB_ENGINE_JWT_SECRET env var or explicit secret
745
+ try:
746
+ pool = SharedUserPool(db) # Will raise JWTSecretError if no secret
747
+ except JWTSecretError:
748
+ print("Set MDB_ENGINE_JWT_SECRET environment variable!")
749
+
750
+ # Development: Use allow_insecure_dev for local testing (NOT for production!)
751
+ pool = SharedUserPool(db, allow_insecure_dev=True) # Auto-generates ephemeral secret
752
+ ```
753
+
754
+ **Generate a secure secret:**
755
+ ```bash
756
+ python -c "import secrets; print(secrets.token_urlsafe(32))"
757
+ ```
758
+
759
+ ### Token Revocation (JTI)
760
+
761
+ Tokens now include a unique JWT ID (JTI) that enables server-side revocation:
762
+
763
+ ```python
764
+ # Revoke a specific token (e.g., on logout)
765
+ await user_pool.revoke_token(token, reason="logout")
766
+
767
+ # Token is now blacklisted and will fail validation
768
+ user = await user_pool.validate_token(token) # Returns None
769
+
770
+ # Revoke all user tokens (e.g., password change)
771
+ await user_pool.revoke_all_user_tokens(user_id, reason="password_change")
772
+ ```
773
+
774
+ **How it works:**
775
+ - Each token contains a `jti` (JWT ID) claim
776
+ - `revoke_token()` adds the JTI to a blacklist collection
777
+ - `validate_token()` checks the blacklist before accepting the token
778
+ - Blacklist entries auto-expire via MongoDB TTL index
779
+
780
+ ### Rate Limiting
781
+
782
+ Protect auth endpoints from brute-force attacks with built-in rate limiting:
783
+
784
+ **Manifest Configuration:**
785
+ ```json
786
+ {
787
+ "auth": {
788
+ "mode": "shared",
789
+ "rate_limits": {
790
+ "/login": {"max_attempts": 5, "window_seconds": 300},
791
+ "/register": {"max_attempts": 3, "window_seconds": 3600}
792
+ }
793
+ }
794
+ }
795
+ ```
796
+
797
+ **Programmatic Usage:**
798
+ ```python
799
+ from mdb_engine.auth import AuthRateLimitMiddleware, RateLimit, rate_limit
800
+
801
+ # Via middleware (auto-configured by engine.create_app())
802
+ app.add_middleware(
803
+ AuthRateLimitMiddleware,
804
+ limits={"/login": RateLimit(max_attempts=5, window_seconds=300)}
805
+ )
806
+
807
+ # Via decorator
808
+ @app.post("/login")
809
+ @rate_limit(max_attempts=5, window_seconds=300)
810
+ async def login(request: Request):
811
+ ...
812
+ ```
813
+
814
+ **Features:**
815
+ - IP + email tracking for granular rate limiting
816
+ - Sliding window algorithm
817
+ - In-memory (single instance) or MongoDB (distributed) storage
818
+ - 429 responses with `Retry-After` header
819
+ - Automatic reset on successful login
820
+
821
+ ### Audit Logging
822
+
823
+ Comprehensive audit trail for authentication events:
824
+
825
+ **Manifest Configuration:**
826
+ ```json
827
+ {
828
+ "auth": {
829
+ "audit": {
830
+ "enabled": true,
831
+ "retention_days": 90
832
+ }
833
+ }
834
+ }
835
+ ```
836
+
837
+ **Programmatic Usage:**
838
+ ```python
839
+ from mdb_engine.auth import AuthAuditLog, AuthAction
840
+
841
+ audit = AuthAuditLog(db, retention_days=90)
842
+ await audit.ensure_indexes()
843
+
844
+ # Log authentication events
845
+ await audit.log_login_success(email="user@example.com", ip_address="192.168.1.1")
846
+ await audit.log_login_failed(email="user@example.com", reason="invalid_password")
847
+ await audit.log_logout(email="user@example.com")
848
+ await audit.log_register(email="new@example.com")
849
+ await audit.log_role_change(email="user@example.com", app_slug="my_app",
850
+ old_roles=["viewer"], new_roles=["editor"])
851
+
852
+ # Query audit logs
853
+ failed_logins = await audit.get_failed_logins(email="user@example.com", hours=24)
854
+ user_activity = await audit.get_user_activity(email="user@example.com", hours=168)
855
+ summary = await audit.get_security_summary(hours=24)
856
+ ```
857
+
858
+ **Audit Actions:**
859
+ | Action | Description |
860
+ |--------|-------------|
861
+ | `login_success` | Successful login |
862
+ | `login_failed` | Failed login attempt |
863
+ | `logout` | User logout |
864
+ | `register` | New user registration |
865
+ | `token_revoked` | Token was revoked |
866
+ | `role_granted` | User received new role |
867
+ | `role_revoked` | User role was removed |
868
+ | `rate_limit_exceeded` | Rate limit was hit |
869
+
870
+ ### Secure Cookies
871
+
872
+ Auto-configured secure cookie settings based on environment:
873
+
874
+ ```python
875
+ # Get secure cookie config (auto-detects HTTPS/production)
876
+ cookie_config = user_pool.get_secure_cookie_config(request)
877
+ response.set_cookie(value=token, **cookie_config)
878
+ ```
879
+
880
+ **Cookie settings by environment:**
881
+ | Setting | Development | Production |
882
+ |---------|-------------|------------|
883
+ | `httponly` | True | True |
884
+ | `secure` | False | True |
885
+ | `samesite` | lax | strict |
886
+
887
+ ### Security Checklist
888
+
889
+ Before deploying to production:
890
+
891
+ - [ ] `MDB_ENGINE_JWT_SECRET` is set to a secure, unique value
892
+ - [ ] Rate limiting is configured for `/login` and `/register`
893
+ - [ ] Audit logging is enabled
894
+ - [ ] HTTPS is enforced (cookie `secure` flag)
895
+ - [ ] Token expiry is appropriately short (default: 24h)
896
+ - [ ] Logout endpoints call `revoke_token()`
897
+
898
+ ## Advanced Security Features
899
+
900
+ ### CSRF Protection
901
+
902
+ CSRF protection is **auto-enabled for shared auth mode**. The middleware uses the double-submit cookie pattern.
903
+
904
+ **Manifest Configuration:**
905
+ ```json
906
+ {
907
+ "auth": {
908
+ "mode": "shared",
909
+ "csrf_protection": true
910
+ }
911
+ }
912
+ ```
913
+
914
+ **Advanced Configuration:**
915
+ ```json
916
+ {
917
+ "auth": {
918
+ "csrf_protection": {
919
+ "enabled": true,
920
+ "exempt_routes": ["/api/*"],
921
+ "rotate_tokens": false,
922
+ "token_ttl": 3600
923
+ }
924
+ }
925
+ }
926
+ ```
927
+
928
+ **How it works:**
929
+ 1. GET requests receive a CSRF token in a cookie
930
+ 2. POST/PUT/DELETE must include the token in `X-CSRF-Token` header
931
+ 3. Token is validated using constant-time comparison
932
+ 4. SameSite=Lax cookies provide additional protection
933
+
934
+ **Frontend Integration:**
935
+
936
+ Helper function for reading cookies:
937
+
938
+ ```javascript
939
+ // Reusable cookie helper
940
+ function getCookie(name) {
941
+ const value = `; ${document.cookie}`;
942
+ const parts = value.split(`; ${name}=`);
943
+ if (parts.length === 2) return parts.pop().split(';').shift();
944
+ return null;
945
+ }
946
+ ```
947
+
948
+ Include in all state-changing requests:
949
+
950
+ ```javascript
951
+ // POST request with CSRF token
952
+ async function createItem(data) {
953
+ const response = await fetch('/api/items', {
954
+ method: 'POST',
955
+ headers: {
956
+ 'Content-Type': 'application/json',
957
+ 'X-CSRF-Token': getCookie('csrf_token')
958
+ },
959
+ credentials: 'same-origin',
960
+ body: JSON.stringify(data)
961
+ });
962
+ return response.json();
963
+ }
964
+
965
+ // DELETE request with CSRF token
966
+ async function deleteItem(id) {
967
+ const response = await fetch(`/api/items/${id}`, {
968
+ method: 'DELETE',
969
+ headers: {
970
+ 'X-CSRF-Token': getCookie('csrf_token')
971
+ },
972
+ credentials: 'same-origin'
973
+ });
974
+ return response.json();
975
+ }
976
+ ```
977
+
978
+ **Logout Must Be POST:**
979
+
980
+ For security, logout endpoints should use POST method, not GET:
981
+
982
+ ```javascript
983
+ // Correct: POST with CSRF token
984
+ async function logout() {
985
+ const response = await fetch('/logout', {
986
+ method: 'POST',
987
+ headers: {
988
+ 'X-CSRF-Token': getCookie('csrf_token')
989
+ },
990
+ credentials: 'same-origin'
991
+ });
992
+
993
+ const result = await response.json();
994
+ if (result.success) {
995
+ window.location.href = result.redirect || '/login';
996
+ }
997
+ }
998
+ ```
999
+
1000
+ Backend endpoint:
1001
+
1002
+ ```python
1003
+ @app.post("/logout")
1004
+ async def logout(request: Request):
1005
+ """Logout must be POST with CSRF token."""
1006
+ response = JSONResponse({"success": True, "redirect": "/login"})
1007
+ response = await logout_user(request, response)
1008
+ return response
1009
+ ```
1010
+
1011
+ **Login/Register JSON Pattern:**
1012
+
1013
+ Return JSON responses for AJAX forms:
1014
+
1015
+ ```python
1016
+ @app.post("/login")
1017
+ async def login(request: Request, email: str = Form(...), password: str = Form(...)):
1018
+ """Login returning JSON for JavaScript frontend."""
1019
+ result = await authenticate_user(email, password)
1020
+
1021
+ if result["success"]:
1022
+ json_response = JSONResponse({"success": True, "redirect": "/dashboard"})
1023
+ # Copy auth cookies from result
1024
+ for key, value in result["response"].headers.items():
1025
+ if key.lower() == "set-cookie":
1026
+ json_response.headers.append(key, value)
1027
+ return json_response
1028
+
1029
+ return JSONResponse(
1030
+ {"success": False, "detail": result.get("error", "Login failed")},
1031
+ status_code=401
1032
+ )
1033
+ ```
1034
+
1035
+ **Error Handling:**
1036
+
1037
+ Handle CSRF validation failures (403 status):
1038
+
1039
+ ```javascript
1040
+ async function secureRequest(url, options = {}) {
1041
+ const response = await fetch(url, {
1042
+ ...options,
1043
+ headers: {
1044
+ ...options.headers,
1045
+ 'X-CSRF-Token': getCookie('csrf_token')
1046
+ },
1047
+ credentials: 'same-origin'
1048
+ });
1049
+
1050
+ if (response.status === 403) {
1051
+ const data = await response.json();
1052
+ if (data.detail?.includes('CSRF')) {
1053
+ // Token expired - refresh the page
1054
+ window.location.reload();
1055
+ return null;
1056
+ }
1057
+ }
1058
+
1059
+ return response;
1060
+ }
1061
+ ```
1062
+
1063
+ ### HSTS (HTTP Strict Transport Security)
1064
+
1065
+ HSTS forces HTTPS connections in production, protecting against protocol downgrade attacks.
1066
+
1067
+ **Manifest Configuration:**
1068
+ ```json
1069
+ {
1070
+ "auth": {
1071
+ "security": {
1072
+ "hsts": {
1073
+ "enabled": true,
1074
+ "max_age": 31536000,
1075
+ "include_subdomains": true,
1076
+ "preload": false
1077
+ }
1078
+ }
1079
+ }
1080
+ }
1081
+ ```
1082
+
1083
+ **Header Output:**
1084
+ ```
1085
+ Strict-Transport-Security: max-age=31536000; includeSubDomains
1086
+ ```
1087
+
1088
+ **Note:** Only enable `preload` if you're ready for permanent HTTPS commitment.
1089
+
1090
+ ### JWT Algorithm Support
1091
+
1092
+ MDB_ENGINE supports multiple JWT signing algorithms for different security requirements.
1093
+
1094
+ | Algorithm | Type | Key | Use Case |
1095
+ |-----------|------|-----|----------|
1096
+ | HS256 | Symmetric | Shared secret | Default, simple deployments |
1097
+ | RS256 | Asymmetric | RSA key pair | Microservices, token verification by multiple parties |
1098
+ | ES256 | Asymmetric | ECDSA key pair | Modern alternative to RSA, smaller keys |
1099
+
1100
+ **Manifest Configuration:**
1101
+ ```json
1102
+ {
1103
+ "auth": {
1104
+ "jwt": {
1105
+ "algorithm": "RS256",
1106
+ "token_expiry_hours": 24
1107
+ }
1108
+ }
1109
+ }
1110
+ ```
1111
+
1112
+ **Environment Variables:**
1113
+ ```bash
1114
+ # For HS256 (symmetric)
1115
+ export MDB_ENGINE_JWT_SECRET="your-secret-key"
1116
+
1117
+ # For RS256/ES256 (asymmetric)
1118
+ export MDB_ENGINE_JWT_SECRET="-----BEGIN RSA PRIVATE KEY-----..."
1119
+ export MDB_ENGINE_JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..."
1120
+ ```
1121
+
1122
+ **Programmatic Usage:**
1123
+ ```python
1124
+ pool = SharedUserPool(
1125
+ db,
1126
+ jwt_secret=private_key,
1127
+ jwt_public_key=public_key,
1128
+ jwt_algorithm="RS256"
1129
+ )
1130
+
1131
+ # Check algorithm
1132
+ print(pool.jwt_algorithm) # "RS256"
1133
+ print(pool.is_asymmetric) # True
1134
+ ```
1135
+
1136
+ ### Password Policy
1137
+
1138
+ Configurable password strength requirements with entropy calculation and breach detection.
1139
+
1140
+ **Manifest Configuration:**
1141
+ ```json
1142
+ {
1143
+ "auth": {
1144
+ "password_policy": {
1145
+ "min_length": 12,
1146
+ "min_entropy_bits": 50,
1147
+ "require_uppercase": true,
1148
+ "require_lowercase": true,
1149
+ "require_numbers": true,
1150
+ "require_special": false,
1151
+ "check_common_passwords": true,
1152
+ "check_breaches": false
1153
+ }
1154
+ }
1155
+ }
1156
+ ```
1157
+
1158
+ **Programmatic Usage:**
1159
+ ```python
1160
+ from mdb_engine.auth import (
1161
+ validate_password_strength,
1162
+ validate_password_strength_async,
1163
+ calculate_password_entropy,
1164
+ is_common_password,
1165
+ )
1166
+
1167
+ # Synchronous validation
1168
+ is_valid, errors = validate_password_strength(
1169
+ "MyPassword123",
1170
+ config=manifest.get("auth", {}).get("password_policy", {})
1171
+ )
1172
+
1173
+ # Async validation with breach check
1174
+ is_valid, errors = await validate_password_strength_async(
1175
+ "MyPassword123",
1176
+ config=config,
1177
+ check_breaches=True # Queries HaveIBeenPwned
1178
+ )
1179
+
1180
+ # Calculate entropy
1181
+ entropy = calculate_password_entropy("MyP@ssw0rd!")
1182
+ print(f"Entropy: {entropy} bits") # ~65 bits
1183
+
1184
+ # Check common passwords
1185
+ if is_common_password("password123"):
1186
+ print("Password is too common!")
1187
+ ```
1188
+
1189
+ **Entropy Guidelines:**
1190
+ | Entropy (bits) | Strength | Example |
1191
+ |----------------|----------|---------|
1192
+ | < 28 | Very Weak | "password" |
1193
+ | 28-35 | Weak | "Password1" |
1194
+ | 36-59 | Fair | "P@ssw0rd" |
1195
+ | 60-127 | Strong | "MyS3cur3P@ss!" |
1196
+ | 128+ | Very Strong | Random 20+ char |
1197
+
1198
+ ### Session Binding
1199
+
1200
+ Tie sessions to client characteristics to prevent session hijacking.
1201
+
1202
+ **Manifest Configuration:**
1203
+ ```json
1204
+ {
1205
+ "auth": {
1206
+ "session_binding": {
1207
+ "bind_ip": false,
1208
+ "bind_fingerprint": true,
1209
+ "allow_ip_change_with_reauth": true
1210
+ }
1211
+ }
1212
+ }
1213
+ ```
1214
+
1215
+ | Setting | Default | Description |
1216
+ |---------|---------|-------------|
1217
+ | `bind_ip` | false | Strict: reject if client IP changes |
1218
+ | `bind_fingerprint` | true | Soft: log warning if fingerprint changes |
1219
+ | `allow_ip_change_with_reauth` | true | Allow IP change if user re-authenticates |
1220
+
1221
+ **How it works:**
1222
+ 1. On login, IP and/or fingerprint are embedded in JWT claims
1223
+ 2. Middleware validates these claims on each request
1224
+ 3. IP binding = strict (rejects on mismatch)
1225
+ 4. Fingerprint binding = soft (logs warning, useful for security monitoring)
1226
+
1227
+ **Token Claims:**
1228
+ ```json
1229
+ {
1230
+ "sub": "user123",
1231
+ "email": "user@example.com",
1232
+ "ip": "192.168.1.100",
1233
+ "fp": "sha256-of-browser-fingerprint",
1234
+ "jti": "unique-token-id",
1235
+ "exp": 1234567890
1236
+ }
1237
+ ```
1238
+
598
1239
  ## Security Considerations
599
1240
 
600
1241
  - **Token Storage**: Store tokens in secure, HttpOnly cookies (not localStorage)
601
- - **CSRF Protection**: Use SameSite cookies and CSRF tokens for state-changing operations
602
- - **Password Hashing**: Always hash passwords using bcrypt or similar
603
- - **Rate Limiting**: Implement rate limiting on authentication endpoints
604
- - **Token Expiration**: Use short-lived access tokens (1 hour) and longer refresh tokens (30 days)
605
- - **Token Blacklisting**: Implement token blacklisting for immediate revocation
606
- - **Session Management**: Limit concurrent sessions per user
607
- - **Device Tracking**: Track devices for security monitoring
1242
+ - **CSRF Protection**: Auto-enabled for shared auth mode with double-submit cookie pattern
1243
+ - **Password Hashing**: Always hash passwords using bcrypt (built-in)
1244
+ - **Password Policy**: Enforce entropy requirements and check common passwords
1245
+ - **Rate Limiting**: Configure via manifest for `/login` and `/register`
1246
+ - **Token Expiration**: Use short-lived access tokens (default: 24h)
1247
+ - **Token Blacklisting**: Revoke tokens on logout via JTI
1248
+ - **Session Binding**: Bind sessions to IP/fingerprint for hijacking protection
1249
+ - **HSTS**: Force HTTPS in production
1250
+ - **Audit Logging**: Track all auth events for forensics
608
1251
 
609
1252
  ## Integration with MongoDBEngine
610
1253