245 lines
7.4 KiB
Markdown
245 lines
7.4 KiB
Markdown
# Authentication Setup - Mem0 Memory Server
|
|
|
|
## ✅ Implementation Complete
|
|
|
|
A simple but effective API key-based authentication layer has been added to ensure users can only access their own memories.
|
|
|
|
## 🔐 How It Works
|
|
|
|
### 1. **API Key to User Mapping**
|
|
API keys are mapped to user IDs in the `.env` file:
|
|
|
|
```bash
|
|
API_KEYS='{"sk-alice-test-key-12345": "alice", "sk-bob-test-key-67890": "bob", "sk-carol-test-key-abcdef": "carol"}'
|
|
```
|
|
|
|
### 2. **Authentication Flow**
|
|
1. Client sends request with `X-API-Key` header
|
|
2. Backend validates API key against configured mapping
|
|
3. Backend extracts authenticated user_id
|
|
4. Backend verifies user can only access their own data
|
|
5. Request proceeds or 401/403 error returned
|
|
|
|
### 3. **Protected Endpoints**
|
|
All memory-related endpoints now require authentication:
|
|
|
|
| Endpoint | Method | Auth Required | User Isolation |
|
|
|----------|--------|---------------|----------------|
|
|
| `/chat` | POST | ✅ | ✅ User can only chat as themselves |
|
|
| `/memories` | POST | ✅ | ✅ User can only add to own memories |
|
|
| `/memories/search` | POST | ✅ | ✅ User can only search own memories |
|
|
| `/memories/{user_id}` | GET | ✅ | ✅ User can only view own memories |
|
|
| `/memories` | PUT | ✅ | ✅ User can only update own memories |
|
|
| `/memories/{memory_id}` | DELETE | ✅ | ✅ User can only delete own memories |
|
|
| `/memories/user/{user_id}` | DELETE | ✅ | ✅ User can only delete own memories |
|
|
| `/graph/relationships/{user_id}` | GET | ✅ | ✅ User can only view own relationships |
|
|
| `/stats/{user_id}` | GET | ✅ | ✅ User can only view own stats |
|
|
| `/health` | GET | ❌ | N/A (Public) |
|
|
| `/stats` | GET | ❌ | N/A (Global stats) |
|
|
| `/models` | GET | ❌ | N/A (Public) |
|
|
|
|
## 📝 Configuration
|
|
|
|
### Environment Setup
|
|
|
|
**1. Update `.env` file:**
|
|
```bash
|
|
# Add API key mapping
|
|
API_KEYS='{"api_key_1": "user1", "api_key_2": "user2"}'
|
|
```
|
|
|
|
**2. Generate Secure API Keys (Production):**
|
|
```bash
|
|
# Generate a random 32-character API key
|
|
openssl rand -hex 32
|
|
|
|
# Example output: sk-a1b2c3d4e5f6...
|
|
```
|
|
|
|
**3. Restart Services:**
|
|
```bash
|
|
docker-compose down
|
|
docker-compose up -d
|
|
```
|
|
|
|
## 🧪 Testing Authentication
|
|
|
|
### Test 1: No API Key (Should Fail - 403)
|
|
```bash
|
|
curl -X POST "http://localhost:8000/memories" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"messages":[{"role":"user","content":"Test"}],"user_id":"alice"}'
|
|
|
|
# Response: {"detail":"Not authenticated"}
|
|
```
|
|
|
|
### Test 2: Invalid API Key (Should Fail - 401)
|
|
```bash
|
|
curl -X POST "http://localhost:8000/memories" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: invalid-key" \
|
|
-d '{"messages":[{"role":"user","content":"Test"}],"user_id":"alice"}'
|
|
|
|
# Response: {"detail":"Invalid API key"}
|
|
```
|
|
|
|
### Test 3: Valid API Key (Should Succeed - 200)
|
|
```bash
|
|
curl -X POST "http://localhost:8000/memories" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: sk-alice-test-key-12345" \
|
|
-d '{"messages":[{"role":"user","content":"My name is Alice"}],"user_id":"alice"}'
|
|
|
|
# Response: {"added_memories":[...], "message":"Memories added successfully"}
|
|
```
|
|
|
|
### Test 4: Cross-User Access (Should Fail - 403)
|
|
```bash
|
|
# Alice trying to access Bob's data
|
|
curl -X POST "http://localhost:8000/memories" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: sk-alice-test-key-12345" \
|
|
-d '{"messages":[{"role":"user","content":"Test"}],"user_id":"bob"}'
|
|
|
|
# Response: {"detail":"Access denied: You can only add memories for yourself (authenticated as 'alice')"}
|
|
```
|
|
|
|
### Test 5: Authenticated Chat
|
|
```bash
|
|
curl -X POST "http://localhost:8000/chat" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: sk-alice-test-key-12345" \
|
|
-d '{"message":"What do you know about me?","user_id":"alice"}'
|
|
|
|
# Response: {"response":"Based on your memories...", "memories_used":5, ...}
|
|
```
|
|
|
|
### Test 6: Authenticated Search
|
|
```bash
|
|
curl -X POST "http://localhost:8000/memories/search" \
|
|
-H "Content-Type: application/json" \
|
|
-H "X-API-Key: sk-bob-test-key-67890" \
|
|
-d '{"query":"tennis","user_id":"bob"}'
|
|
|
|
# Response: {"memories":[...], "total_count":4}
|
|
```
|
|
|
|
## 🔧 Implementation Details
|
|
|
|
### Files Modified/Created:
|
|
|
|
1. **`backend/auth.py`** (NEW)
|
|
- `AuthService` class for API key validation
|
|
- `get_current_user()` FastAPI dependency
|
|
- `verify_user_access()` for cross-user protection
|
|
|
|
2. **`backend/config.py`** (UPDATED)
|
|
- Added `api_keys` field using proper Pydantic V2 syntax
|
|
- Added `api_key_mapping` property to parse JSON
|
|
- Uses `validation_alias` with `AliasChoices` for env var mapping
|
|
|
|
3. **`backend/main.py`** (UPDATED)
|
|
- All protected endpoints now use `authenticated_user = Depends(get_current_user)`
|
|
- Added user isolation checks in each endpoint
|
|
|
|
4. **`docker-compose.yml`** (UPDATED)
|
|
- Added `API_KEYS` environment variable mapping
|
|
|
|
5. **`.env`** (UPDATED)
|
|
- Added `API_KEYS` configuration
|
|
|
|
6. **`.env.example`** (UPDATED)
|
|
- Added `API_KEYS` template with security notes
|
|
|
|
## 🎯 Security Features Implemented
|
|
|
|
✅ **API Key Authentication**: Every protected request requires valid API key
|
|
✅ **User Isolation**: Users can only access their own data
|
|
✅ **Cross-User Protection**: Prevents user A from accessing user B's memories
|
|
✅ **Structured Logging**: All auth events logged with correlation IDs
|
|
✅ **Environment-Based Config**: API keys stored in `.env` (not hardcoded)
|
|
✅ **Clear Error Messages**: Informative 401/403 responses
|
|
|
|
## 🔒 Production Recommendations
|
|
|
|
### Current Setup (✅ Completed)
|
|
- ✅ API key authentication
|
|
- ✅ User isolation
|
|
- ✅ Environment-based configuration
|
|
- ✅ Cross-user access protection
|
|
|
|
### Future Enhancements (Optional)
|
|
- [ ] Move API keys to secrets manager (AWS Secrets Manager, HashiCorp Vault)
|
|
- [ ] Add API key rotation mechanism
|
|
- [ ] Add rate limiting per API key
|
|
- [ ] Add API key expiration
|
|
- [ ] Add audit logging for security events
|
|
- [ ] Add API key scopes/permissions
|
|
- [ ] Add HTTPS enforcement
|
|
- [ ] Add request signing for extra security
|
|
|
|
## 📊 Test Results
|
|
|
|
All authentication tests **PASSED** ✅
|
|
|
|
| Test | Result | HTTP Status |
|
|
|------|--------|-------------|
|
|
| No API key | ✅ Blocked | 403 |
|
|
| Invalid API key | ✅ Blocked | 401 |
|
|
| Valid API key | ✅ Allowed | 200 |
|
|
| Cross-user access | ✅ Blocked | 403 |
|
|
| Authenticated chat | ✅ Works | 200 |
|
|
| Authenticated search | ✅ Works | 200 |
|
|
| Stats isolation | ✅ Blocked | 403 |
|
|
|
|
## 💡 Usage Example
|
|
|
|
```python
|
|
import requests
|
|
|
|
# Configure API key
|
|
API_KEY = "sk-alice-test-key-12345"
|
|
BASE_URL = "http://localhost:8000"
|
|
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"X-API-Key": API_KEY
|
|
}
|
|
|
|
# Add memory
|
|
response = requests.post(
|
|
f"{BASE_URL}/memories",
|
|
headers=headers,
|
|
json={
|
|
"messages": [{"role": "user", "content": "I love Python programming"}],
|
|
"user_id": "alice"
|
|
}
|
|
)
|
|
|
|
print(response.json())
|
|
# {"added_memories": [...], "message": "Memories added successfully"}
|
|
|
|
# Chat with memory
|
|
response = requests.post(
|
|
f"{BASE_URL}/chat",
|
|
headers=headers,
|
|
json={
|
|
"message": "What do I love?",
|
|
"user_id": "alice"
|
|
}
|
|
)
|
|
|
|
print(response.json()["response"])
|
|
# "Based on your memories, you love Python programming"
|
|
```
|
|
|
|
## 🚀 Ready for Production
|
|
|
|
The authentication layer is now **production-ready** for basic deployment with:
|
|
- ✅ Simple API key authentication
|
|
- ✅ User data isolation
|
|
- ✅ Proper error handling
|
|
- ✅ Environment-based configuration
|
|
- ✅ Comprehensive testing
|
|
|
|
For enterprise deployment, consider implementing the optional enhancements listed above.
|