mdb-engine 0.1.6__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.
- mdb_engine/README.md +144 -0
- mdb_engine/__init__.py +37 -0
- mdb_engine/auth/README.md +631 -0
- mdb_engine/auth/__init__.py +128 -0
- mdb_engine/auth/casbin_factory.py +199 -0
- mdb_engine/auth/casbin_models.py +46 -0
- mdb_engine/auth/config_defaults.py +71 -0
- mdb_engine/auth/config_helpers.py +213 -0
- mdb_engine/auth/cookie_utils.py +158 -0
- mdb_engine/auth/decorators.py +350 -0
- mdb_engine/auth/dependencies.py +747 -0
- mdb_engine/auth/helpers.py +64 -0
- mdb_engine/auth/integration.py +578 -0
- mdb_engine/auth/jwt.py +225 -0
- mdb_engine/auth/middleware.py +241 -0
- mdb_engine/auth/oso_factory.py +323 -0
- mdb_engine/auth/provider.py +570 -0
- mdb_engine/auth/restrictions.py +271 -0
- mdb_engine/auth/session_manager.py +477 -0
- mdb_engine/auth/token_lifecycle.py +213 -0
- mdb_engine/auth/token_store.py +289 -0
- mdb_engine/auth/users.py +1516 -0
- mdb_engine/auth/utils.py +614 -0
- mdb_engine/cli/__init__.py +13 -0
- mdb_engine/cli/commands/__init__.py +7 -0
- mdb_engine/cli/commands/generate.py +105 -0
- mdb_engine/cli/commands/migrate.py +83 -0
- mdb_engine/cli/commands/show.py +70 -0
- mdb_engine/cli/commands/validate.py +63 -0
- mdb_engine/cli/main.py +41 -0
- mdb_engine/cli/utils.py +92 -0
- mdb_engine/config.py +217 -0
- mdb_engine/constants.py +160 -0
- mdb_engine/core/README.md +542 -0
- mdb_engine/core/__init__.py +42 -0
- mdb_engine/core/app_registration.py +392 -0
- mdb_engine/core/connection.py +243 -0
- mdb_engine/core/engine.py +749 -0
- mdb_engine/core/index_management.py +162 -0
- mdb_engine/core/manifest.py +2793 -0
- mdb_engine/core/seeding.py +179 -0
- mdb_engine/core/service_initialization.py +355 -0
- mdb_engine/core/types.py +413 -0
- mdb_engine/database/README.md +522 -0
- mdb_engine/database/__init__.py +31 -0
- mdb_engine/database/abstraction.py +635 -0
- mdb_engine/database/connection.py +387 -0
- mdb_engine/database/scoped_wrapper.py +1721 -0
- mdb_engine/embeddings/README.md +184 -0
- mdb_engine/embeddings/__init__.py +62 -0
- mdb_engine/embeddings/dependencies.py +193 -0
- mdb_engine/embeddings/service.py +759 -0
- mdb_engine/exceptions.py +167 -0
- mdb_engine/indexes/README.md +651 -0
- mdb_engine/indexes/__init__.py +21 -0
- mdb_engine/indexes/helpers.py +145 -0
- mdb_engine/indexes/manager.py +895 -0
- mdb_engine/memory/README.md +451 -0
- mdb_engine/memory/__init__.py +30 -0
- mdb_engine/memory/service.py +1285 -0
- mdb_engine/observability/README.md +515 -0
- mdb_engine/observability/__init__.py +42 -0
- mdb_engine/observability/health.py +296 -0
- mdb_engine/observability/logging.py +161 -0
- mdb_engine/observability/metrics.py +297 -0
- mdb_engine/routing/README.md +462 -0
- mdb_engine/routing/__init__.py +73 -0
- mdb_engine/routing/websockets.py +813 -0
- mdb_engine/utils/__init__.py +7 -0
- mdb_engine-0.1.6.dist-info/METADATA +213 -0
- mdb_engine-0.1.6.dist-info/RECORD +75 -0
- mdb_engine-0.1.6.dist-info/WHEEL +5 -0
- mdb_engine-0.1.6.dist-info/entry_points.txt +2 -0
- mdb_engine-0.1.6.dist-info/licenses/LICENSE +661 -0
- mdb_engine-0.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
# Authentication & Authorization Module
|
|
2
|
+
|
|
3
|
+
MongoDB-backed authentication and authorization conveniences for MDB_ENGINE applications. The engine provides building blocks (JWT tokens, pluggable authorization providers, session management) without imposing specific authentication flows. Apps implement their own authentication (including OAuth) while leveraging the engine's MongoDB-backed conveniences.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
The engine provides **MongoDB-backed conveniences** without imposing solutions:
|
|
8
|
+
|
|
9
|
+
- **Building blocks, not complete solutions**: The engine provides JWT token management, authorization providers (Casbin/OSO), and session management - but apps implement their own authentication flows
|
|
10
|
+
- **MongoDB-first**: All auth data (policies, sessions, tokens) is stored in MongoDB, leveraging the engine's scoping and isolation features
|
|
11
|
+
- **Pluggable authorization**: Choose Casbin (MongoDB-backed RBAC) or OSO (Cloud or library) - both auto-configured from manifest
|
|
12
|
+
- **App-level flexibility**: Apps can implement OAuth, custom auth flows, or use the provided app-level user management utilities
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **JWT Token Management**: Access and refresh token pairs with automatic lifecycle management
|
|
17
|
+
- **Pluggable Authorization**: Support for Casbin (MongoDB-backed) and OSO (Cloud or library) authorization providers
|
|
18
|
+
- **FastAPI Integration**: Ready-to-use dependencies for authentication and authorization
|
|
19
|
+
- **Session Management**: Multi-device session tracking with activity monitoring
|
|
20
|
+
- **Token Blacklisting**: Secure token revocation and expiration handling
|
|
21
|
+
- **App-Level User Management**: Utilities for app-specific user accounts and anonymous sessions
|
|
22
|
+
- **Security Middleware**: Built-in security headers and CSRF protection
|
|
23
|
+
- **Cookie Management**: Secure cookie handling for web applications
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
The auth module is part of MDB_ENGINE. No additional installation required, but you may need:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install pyjwt casbin # For JWT and Casbin support
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
### Environment Variables
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Required: Secret key for JWT signing (must be set, engine will fail to start if missing)
|
|
39
|
+
# Generate a secure key: python -c 'import secrets; print(secrets.token_urlsafe(32))'
|
|
40
|
+
export FLASK_SECRET_KEY="your-secret-key-here"
|
|
41
|
+
|
|
42
|
+
# Optional: Token TTL (seconds)
|
|
43
|
+
export ACCESS_TOKEN_TTL=3600 # Default: 1 hour
|
|
44
|
+
export REFRESH_TOKEN_TTL=2592000 # Default: 30 days
|
|
45
|
+
|
|
46
|
+
# Optional: Session limits
|
|
47
|
+
export MAX_SESSIONS_PER_USER=5
|
|
48
|
+
export SESSION_INACTIVITY_TIMEOUT=86400 # 24 hours
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Manifest Configuration
|
|
52
|
+
|
|
53
|
+
Configure authentication and authorization in your `manifest.json`. The system automatically creates a Casbin provider with MongoDB-backed policies:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"slug": "my_app",
|
|
58
|
+
"auth_required": true,
|
|
59
|
+
"auth": {
|
|
60
|
+
"policy": {
|
|
61
|
+
"provider": "casbin",
|
|
62
|
+
"required": true,
|
|
63
|
+
"allow_anonymous": false,
|
|
64
|
+
"authorization": {
|
|
65
|
+
"model": "rbac",
|
|
66
|
+
"policies_collection": "casbin_policies",
|
|
67
|
+
"link_users_roles": true,
|
|
68
|
+
"default_roles": ["user", "admin"]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"users": {
|
|
72
|
+
"enabled": true,
|
|
73
|
+
"strategy": "app_users"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Key Features:**
|
|
80
|
+
- **Auto-created provider**: Casbin provider is automatically created from manifest (default)
|
|
81
|
+
- **MongoDB-backed**: Policies stored in MongoDB collection (default: `casbin_policies`)
|
|
82
|
+
- **App-level user management**: App-level users automatically get Casbin roles assigned
|
|
83
|
+
- **Zero boilerplate**: Just configure in manifest, everything works automatically
|
|
84
|
+
|
|
85
|
+
**OSO Support**: The engine also supports OSO Cloud and OSO library. Configure `provider: "oso"` in your manifest with OSO Cloud credentials, and the engine will auto-create an OSO adapter just like Casbin.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
### 1. Unified Auth Setup (Recommended)
|
|
90
|
+
|
|
91
|
+
With the unified auth system, just configure your manifest and call `setup_auth_from_manifest()` - everything is auto-created:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from mdb_engine.auth import setup_auth_from_manifest
|
|
95
|
+
from fastapi import FastAPI
|
|
96
|
+
|
|
97
|
+
app = FastAPI()
|
|
98
|
+
|
|
99
|
+
@app.on_event("startup")
|
|
100
|
+
async def startup():
|
|
101
|
+
# This automatically:
|
|
102
|
+
# - Creates authorization provider (Casbin or OSO) with MongoDB adapter
|
|
103
|
+
# - Sets up token management
|
|
104
|
+
# - Links app-level users to authorization roles
|
|
105
|
+
# - Configures security middleware
|
|
106
|
+
await setup_auth_from_manifest(app, engine, "my_app")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The authorization provider is automatically available via `get_authz_provider` dependency:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from mdb_engine.auth import get_authz_provider, get_current_user
|
|
113
|
+
from fastapi import Depends
|
|
114
|
+
|
|
115
|
+
@app.get("/protected")
|
|
116
|
+
async def protected_route(
|
|
117
|
+
user: dict = Depends(get_current_user),
|
|
118
|
+
authz: AuthorizationProvider = Depends(get_authz_provider)
|
|
119
|
+
):
|
|
120
|
+
# Check permission using auto-created authorization provider (Casbin or OSO)
|
|
121
|
+
has_access = await authz.check(
|
|
122
|
+
subject=user.get("email", "anonymous"),
|
|
123
|
+
resource="my_app",
|
|
124
|
+
action="access"
|
|
125
|
+
)
|
|
126
|
+
if not has_access:
|
|
127
|
+
raise HTTPException(status_code=403, detail="Access denied")
|
|
128
|
+
return {"user_id": user["user_id"]}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 2. JWT Token Management
|
|
132
|
+
|
|
133
|
+
#### Generate Token Pairs
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from mdb_engine.auth import generate_token_pair, encode_jwt_token
|
|
137
|
+
|
|
138
|
+
# Generate access + refresh token pair
|
|
139
|
+
access_token, refresh_token = generate_token_pair(
|
|
140
|
+
user_id="user123",
|
|
141
|
+
user_email="user@example.com",
|
|
142
|
+
secret_key=SECRET_KEY
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Or encode custom token
|
|
146
|
+
token = encode_jwt_token(
|
|
147
|
+
payload={"user_id": "user123", "role": "admin"},
|
|
148
|
+
secret_key=SECRET_KEY,
|
|
149
|
+
expires_in=3600
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### Decode and Validate Tokens
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from mdb_engine.auth import decode_jwt_token, extract_token_metadata
|
|
157
|
+
|
|
158
|
+
# Decode token
|
|
159
|
+
payload = decode_jwt_token(token, SECRET_KEY)
|
|
160
|
+
|
|
161
|
+
# Extract metadata
|
|
162
|
+
metadata = extract_token_metadata(payload)
|
|
163
|
+
user_id = metadata.get("user_id")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 3. FastAPI Dependencies
|
|
167
|
+
|
|
168
|
+
#### Get Current User
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from mdb_engine.auth import get_current_user
|
|
172
|
+
from fastapi import Depends
|
|
173
|
+
|
|
174
|
+
@app.get("/profile")
|
|
175
|
+
async def get_profile(
|
|
176
|
+
user: dict = Depends(get_current_user)
|
|
177
|
+
):
|
|
178
|
+
if not user:
|
|
179
|
+
raise HTTPException(status_code=401)
|
|
180
|
+
return {"email": user["user_email"]}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Require Authentication
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from mdb_engine.auth import require_auth
|
|
187
|
+
|
|
188
|
+
@app.post("/data")
|
|
189
|
+
@require_auth
|
|
190
|
+
async def create_data(
|
|
191
|
+
user: dict = Depends(get_current_user),
|
|
192
|
+
data: dict = None
|
|
193
|
+
):
|
|
194
|
+
# User is guaranteed to be authenticated here
|
|
195
|
+
return {"created_by": user["user_id"]}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Require Admin
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from mdb_engine.auth import require_admin
|
|
202
|
+
|
|
203
|
+
@app.delete("/users/{user_id}")
|
|
204
|
+
async def delete_user(
|
|
205
|
+
user_id: str,
|
|
206
|
+
admin: dict = Depends(require_admin)
|
|
207
|
+
):
|
|
208
|
+
# Only admins can access this route
|
|
209
|
+
await delete_user_from_db(user_id)
|
|
210
|
+
return {"deleted": user_id}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Require Permission
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from mdb_engine.auth import require_permission, get_authz_provider
|
|
217
|
+
from fastapi import Depends
|
|
218
|
+
|
|
219
|
+
@app.post("/documents")
|
|
220
|
+
async def create_document(
|
|
221
|
+
document: dict,
|
|
222
|
+
user: dict = Depends(get_current_user),
|
|
223
|
+
authz: AuthorizationProvider = Depends(get_authz_provider)
|
|
224
|
+
):
|
|
225
|
+
# Check permission
|
|
226
|
+
has_permission = await require_permission(
|
|
227
|
+
user["user_id"],
|
|
228
|
+
"documents",
|
|
229
|
+
"create",
|
|
230
|
+
authz_provider=authz
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if not has_permission:
|
|
234
|
+
raise HTTPException(status_code=403, detail="Permission denied")
|
|
235
|
+
|
|
236
|
+
return await create_document_in_db(document)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 4. Authorization Providers
|
|
240
|
+
|
|
241
|
+
The engine supports two authorization providers, both auto-configured from manifest:
|
|
242
|
+
|
|
243
|
+
#### Casbin Adapter (Auto-Created from Manifest)
|
|
244
|
+
|
|
245
|
+
**Recommended**: Configure in manifest and let the system auto-create:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"auth": {
|
|
250
|
+
"policy": {
|
|
251
|
+
"provider": "casbin",
|
|
252
|
+
"authorization": {
|
|
253
|
+
"model": "rbac",
|
|
254
|
+
"policies_collection": "casbin_policies"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The provider is automatically created and available via `get_authz_provider` dependency.
|
|
262
|
+
|
|
263
|
+
**Manual Setup** (if needed):
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from mdb_engine.auth import CasbinAdapter, create_casbin_enforcer
|
|
267
|
+
|
|
268
|
+
# Create enforcer with MongoDB adapter
|
|
269
|
+
enforcer = await create_casbin_enforcer(
|
|
270
|
+
db=engine.get_database(),
|
|
271
|
+
model="rbac",
|
|
272
|
+
policies_collection="casbin_policies"
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Create adapter
|
|
276
|
+
authz_provider = CasbinAdapter(enforcer)
|
|
277
|
+
|
|
278
|
+
# Set on app state
|
|
279
|
+
app.state.authz_provider = authz_provider
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### OSO Adapter (Auto-Created from Manifest)
|
|
283
|
+
|
|
284
|
+
**OSO Cloud** (Recommended): Configure in manifest with OSO Cloud credentials:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"auth": {
|
|
289
|
+
"policy": {
|
|
290
|
+
"provider": "oso",
|
|
291
|
+
"authorization": {
|
|
292
|
+
"api_key": "${OSO_AUTH}",
|
|
293
|
+
"url": "${OSO_URL}",
|
|
294
|
+
"initial_roles": [
|
|
295
|
+
{"user": "admin@example.com", "role": "admin", "resource": "documents"}
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The OSO Cloud provider is automatically created from manifest, just like Casbin.
|
|
304
|
+
|
|
305
|
+
**OSO Library** (Manual Setup):
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
from mdb_engine.auth import OsoAdapter
|
|
309
|
+
from oso import Oso
|
|
310
|
+
|
|
311
|
+
# Create OSO instance
|
|
312
|
+
oso = Oso()
|
|
313
|
+
oso.load_file("policy.polar")
|
|
314
|
+
|
|
315
|
+
# Create adapter
|
|
316
|
+
authz_provider = OsoAdapter(oso)
|
|
317
|
+
|
|
318
|
+
# Set on app state
|
|
319
|
+
app.state.authz_provider = authz_provider
|
|
320
|
+
|
|
321
|
+
# Check permission
|
|
322
|
+
allowed = await authz_provider.check(
|
|
323
|
+
subject="user123",
|
|
324
|
+
resource="documents",
|
|
325
|
+
action="read"
|
|
326
|
+
)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### 5. Session Management
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
from mdb_engine.auth import SessionManager
|
|
333
|
+
|
|
334
|
+
# Initialize session manager
|
|
335
|
+
session_mgr = SessionManager(db, collection_name="user_sessions")
|
|
336
|
+
await session_mgr.ensure_indexes()
|
|
337
|
+
|
|
338
|
+
# Create session
|
|
339
|
+
session = await session_mgr.create_session(
|
|
340
|
+
user_id="user123",
|
|
341
|
+
device_id="device456",
|
|
342
|
+
refresh_jti="refresh_token_jti",
|
|
343
|
+
device_info={"ip": "1.2.3.4", "user_agent": "Mozilla/5.0"}
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# Update session activity
|
|
347
|
+
await session_mgr.update_activity("session_id", "device_id")
|
|
348
|
+
|
|
349
|
+
# Get user sessions
|
|
350
|
+
sessions = await session_mgr.get_user_sessions("user123")
|
|
351
|
+
|
|
352
|
+
# Revoke session
|
|
353
|
+
await session_mgr.revoke_session("session_id")
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### 6. Token Blacklisting
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
from mdb_engine.auth import TokenBlacklist
|
|
360
|
+
|
|
361
|
+
# Initialize blacklist
|
|
362
|
+
blacklist = TokenBlacklist(db, collection_name="token_blacklist")
|
|
363
|
+
await blacklist.ensure_indexes()
|
|
364
|
+
|
|
365
|
+
# Blacklist token
|
|
366
|
+
await blacklist.add_token("token_jti", expires_at=datetime.utcnow() + timedelta(hours=1))
|
|
367
|
+
|
|
368
|
+
# Check if token is blacklisted
|
|
369
|
+
is_blacklisted = await blacklist.is_blacklisted("token_jti")
|
|
370
|
+
|
|
371
|
+
# Cleanup expired tokens
|
|
372
|
+
await blacklist.cleanup_expired()
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 7. Token Lifecycle Management
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
from mdb_engine.auth import (
|
|
379
|
+
get_token_expiry_time,
|
|
380
|
+
is_token_expiring_soon,
|
|
381
|
+
should_refresh_token,
|
|
382
|
+
refresh_access_token
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Check token expiry
|
|
386
|
+
expiry = get_token_expiry_time(token_payload)
|
|
387
|
+
is_expiring = is_token_expiring_soon(token_payload, threshold_seconds=300)
|
|
388
|
+
|
|
389
|
+
# Refresh token
|
|
390
|
+
if should_refresh_token(refresh_token_payload):
|
|
391
|
+
new_access_token = await refresh_access_token(
|
|
392
|
+
refresh_token=refresh_token,
|
|
393
|
+
secret_key=SECRET_KEY,
|
|
394
|
+
db=db
|
|
395
|
+
)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 8. Sub-Authentication (App-Level Users)
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
from mdb_engine.auth import (
|
|
402
|
+
create_app_user,
|
|
403
|
+
authenticate_app_user,
|
|
404
|
+
get_app_user,
|
|
405
|
+
get_or_create_anonymous_user
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Create app-specific user
|
|
409
|
+
app_user = await create_app_user(
|
|
410
|
+
db=db,
|
|
411
|
+
app_slug="my_app",
|
|
412
|
+
email="user@example.com",
|
|
413
|
+
password="secure_password"
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
# Authenticate app user
|
|
417
|
+
user = await authenticate_app_user(
|
|
418
|
+
db=db,
|
|
419
|
+
app_slug="my_app",
|
|
420
|
+
email="user@example.com",
|
|
421
|
+
password="secure_password"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Get or create anonymous user
|
|
425
|
+
anon_user = await get_or_create_anonymous_user(
|
|
426
|
+
db=db,
|
|
427
|
+
app_slug="my_app",
|
|
428
|
+
device_id="device123"
|
|
429
|
+
)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### 9. Security Middleware
|
|
433
|
+
|
|
434
|
+
Security middleware is automatically added when using `setup_auth_from_manifest()`. If you need to add it manually:
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
from mdb_engine.auth import SecurityMiddleware
|
|
438
|
+
|
|
439
|
+
app.add_middleware(SecurityMiddleware)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### 10. Cookie Management
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from mdb_engine.auth import (
|
|
446
|
+
set_auth_cookies,
|
|
447
|
+
clear_auth_cookies,
|
|
448
|
+
get_secure_cookie_settings
|
|
449
|
+
)
|
|
450
|
+
from fastapi.responses import Response
|
|
451
|
+
|
|
452
|
+
@app.post("/login")
|
|
453
|
+
async def login(response: Response, credentials: dict):
|
|
454
|
+
# Authenticate user
|
|
455
|
+
user = await authenticate_user(credentials)
|
|
456
|
+
|
|
457
|
+
# Generate tokens
|
|
458
|
+
access_token, refresh_token = generate_token_pair(
|
|
459
|
+
user_id=user["_id"],
|
|
460
|
+
user_email=user["email"],
|
|
461
|
+
secret_key=SECRET_KEY
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# Set secure cookies
|
|
465
|
+
set_auth_cookies(
|
|
466
|
+
response=response,
|
|
467
|
+
access_token=access_token,
|
|
468
|
+
refresh_token=refresh_token
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
return {"status": "logged_in"}
|
|
472
|
+
|
|
473
|
+
@app.post("/logout")
|
|
474
|
+
async def logout(response: Response):
|
|
475
|
+
clear_auth_cookies(response)
|
|
476
|
+
return {"status": "logged_out"}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## API Reference
|
|
480
|
+
|
|
481
|
+
### JWT Functions
|
|
482
|
+
|
|
483
|
+
- `encode_jwt_token(payload, secret_key, expires_in=None)` - Encode JWT token
|
|
484
|
+
- `decode_jwt_token(token, secret_key)` - Decode JWT token
|
|
485
|
+
- `generate_token_pair(user_id, user_email, secret_key)` - Generate access + refresh token pair
|
|
486
|
+
- `extract_token_metadata(payload)` - Extract metadata from token payload
|
|
487
|
+
|
|
488
|
+
### FastAPI Dependencies
|
|
489
|
+
|
|
490
|
+
- `get_current_user(request, token=None)` - Get current authenticated user
|
|
491
|
+
- `require_admin(request)` - Require admin role
|
|
492
|
+
- `require_admin_or_developer(request)` - Require admin or developer role
|
|
493
|
+
- `require_permission(subject, resource, action, authz_provider)` - Check permission
|
|
494
|
+
- `get_authz_provider(request)` - Get authorization provider
|
|
495
|
+
- `get_token_blacklist(request)` - Get token blacklist
|
|
496
|
+
- `get_session_manager(request)` - Get session manager
|
|
497
|
+
|
|
498
|
+
### Authorization Providers
|
|
499
|
+
|
|
500
|
+
- `AuthorizationProvider` - Protocol interface for authorization providers
|
|
501
|
+
- `CasbinAdapter(enforcer)` - Casbin authorization adapter
|
|
502
|
+
- `OsoAdapter(oso)` - OSO authorization adapter
|
|
503
|
+
|
|
504
|
+
### Session Management
|
|
505
|
+
|
|
506
|
+
- `SessionManager(db, collection_name)` - Session manager class
|
|
507
|
+
- `create_session(user_id, device_id, refresh_jti, device_info)` - Create new session
|
|
508
|
+
- `update_activity(session_id, device_id)` - Update session activity
|
|
509
|
+
- `get_user_sessions(user_id)` - Get all sessions for user
|
|
510
|
+
- `revoke_session(session_id)` - Revoke a session
|
|
511
|
+
- `revoke_all_user_sessions(user_id)` - Revoke all user sessions
|
|
512
|
+
|
|
513
|
+
### Token Management
|
|
514
|
+
|
|
515
|
+
- `TokenBlacklist(db, collection_name)` - Token blacklist class
|
|
516
|
+
- `add_token(jti, expires_at)` - Blacklist a token
|
|
517
|
+
- `is_blacklisted(jti)` - Check if token is blacklisted
|
|
518
|
+
- `cleanup_expired()` - Remove expired tokens
|
|
519
|
+
|
|
520
|
+
### Token Lifecycle
|
|
521
|
+
|
|
522
|
+
- `get_token_expiry_time(payload)` - Get token expiry time
|
|
523
|
+
- `is_token_expiring_soon(payload, threshold_seconds)` - Check if token expires soon
|
|
524
|
+
- `should_refresh_token(payload)` - Check if token should be refreshed
|
|
525
|
+
- `refresh_access_token(refresh_token, secret_key, db)` - Refresh access token
|
|
526
|
+
- `get_token_age(payload)` - Get token age in seconds
|
|
527
|
+
- `get_time_until_expiry(payload)` - Get time until expiry
|
|
528
|
+
|
|
529
|
+
### Sub-Authentication
|
|
530
|
+
|
|
531
|
+
- `create_app_user(db, app_slug, email, password, **kwargs)` - Create app user
|
|
532
|
+
- `authenticate_app_user(db, app_slug, email, password)` - Authenticate app user
|
|
533
|
+
- `get_app_user(db, app_slug, user_id)` - Get app-level user
|
|
534
|
+
- `get_or_create_anonymous_user(db, app_slug, device_id)` - Get/create anonymous user
|
|
535
|
+
- `get_or_create_demo_user(db, app_slug, device_id)` - Get/create demo user
|
|
536
|
+
|
|
537
|
+
### Utilities
|
|
538
|
+
|
|
539
|
+
- `login_user(db, email, password, response)` - Login user and set cookies
|
|
540
|
+
- `register_user(db, email, password, **kwargs)` - Register new user
|
|
541
|
+
- `logout_user(response)` - Logout user and clear cookies
|
|
542
|
+
- `validate_password_strength(password)` - Validate password strength
|
|
543
|
+
|
|
544
|
+
## Decorators
|
|
545
|
+
|
|
546
|
+
### `@require_auth`
|
|
547
|
+
|
|
548
|
+
Require authentication for a route:
|
|
549
|
+
|
|
550
|
+
```python
|
|
551
|
+
from mdb_engine.auth import require_auth
|
|
552
|
+
|
|
553
|
+
@app.post("/data")
|
|
554
|
+
@require_auth
|
|
555
|
+
async def create_data(user: dict = Depends(get_current_user)):
|
|
556
|
+
return {"created_by": user["user_id"]}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### `@token_security`
|
|
560
|
+
|
|
561
|
+
Add token security checks:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
from mdb_engine.auth import token_security
|
|
565
|
+
|
|
566
|
+
@app.post("/sensitive")
|
|
567
|
+
@token_security
|
|
568
|
+
async def sensitive_operation(user: dict = Depends(get_current_user)):
|
|
569
|
+
return {"data": "sensitive"}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### `@rate_limit_auth`
|
|
573
|
+
|
|
574
|
+
Rate limit authentication endpoints:
|
|
575
|
+
|
|
576
|
+
```python
|
|
577
|
+
from mdb_engine.auth import rate_limit_auth
|
|
578
|
+
|
|
579
|
+
@app.post("/login")
|
|
580
|
+
@rate_limit_auth(max_attempts=5, window_seconds=300)
|
|
581
|
+
async def login(credentials: dict):
|
|
582
|
+
return await authenticate(credentials)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Best Practices
|
|
586
|
+
|
|
587
|
+
1. **Always use HTTPS in production** - JWT tokens should only be transmitted over HTTPS
|
|
588
|
+
2. **Set strong SECRET_KEY (Required)** - The engine requires `FLASK_SECRET_KEY` to be set. Use a cryptographically secure random key (minimum 32 characters). The engine will fail to start if this is not configured.
|
|
589
|
+
3. **Use token blacklisting** - Implement token revocation for logout and security
|
|
590
|
+
4. **Monitor sessions** - Track active sessions and clean up inactive ones
|
|
591
|
+
5. **Implement refresh tokens** - Use refresh tokens for long-lived sessions
|
|
592
|
+
6. **Validate permissions** - Always check permissions before sensitive operations
|
|
593
|
+
7. **Use secure cookies** - Enable HttpOnly, Secure, and SameSite flags
|
|
594
|
+
8. **Handle token expiration** - Implement automatic token refresh
|
|
595
|
+
9. **Log authentication events** - Track login, logout, and permission failures
|
|
596
|
+
10. **Use sub-authentication** - Isolate app-level users for multi-tenant applications
|
|
597
|
+
|
|
598
|
+
## Security Considerations
|
|
599
|
+
|
|
600
|
+
- **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
|
|
608
|
+
|
|
609
|
+
## Integration with MongoDBEngine
|
|
610
|
+
|
|
611
|
+
The auth module integrates seamlessly with MongoDBEngine:
|
|
612
|
+
|
|
613
|
+
```python
|
|
614
|
+
from mdb_engine import MongoDBEngine
|
|
615
|
+
from mdb_engine.auth import setup_auth_from_manifest
|
|
616
|
+
|
|
617
|
+
engine = MongoDBEngine(mongo_uri="...", db_name="...")
|
|
618
|
+
await engine.initialize()
|
|
619
|
+
|
|
620
|
+
manifest = await engine.load_manifest("manifest.json")
|
|
621
|
+
await engine.register_app(manifest)
|
|
622
|
+
|
|
623
|
+
# Setup auth from manifest
|
|
624
|
+
await setup_auth_from_manifest(engine, manifest)
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## Related Modules
|
|
628
|
+
|
|
629
|
+
- **`core/`** - MongoDBEngine integration
|
|
630
|
+
- **`database/`** - Database access for user storage
|
|
631
|
+
- **`observability/`** - Logging and metrics for auth events
|