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