468 lines
12 KiB
Markdown
468 lines
12 KiB
Markdown
# Supabase Auth API - Tests & Examples
|
|
|
|
## Übersicht
|
|
|
|
Diese API verwendet **Supabase Auth JWT Tokens** für Authentifizierung.
|
|
**NIEMALS Service Role Key im Frontend verwenden!**
|
|
|
|
---
|
|
|
|
## Test 1: Unauthenticated Request (muss 401/403 geben)
|
|
|
|
### Request (ohne Auth Token)
|
|
|
|
```bash
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/get_my_instance_config' \
|
|
-H "Content-Type: application/json" \
|
|
-d '{}'
|
|
```
|
|
|
|
### Expected Response (401 Unauthorized)
|
|
|
|
```json
|
|
{
|
|
"code": "PGRST301",
|
|
"message": "Not authenticated",
|
|
"details": null,
|
|
"hint": null
|
|
}
|
|
```
|
|
|
|
**Status:** ✅ PASS - Unauthenticated requests are blocked
|
|
|
|
---
|
|
|
|
## Test 2: Authenticated Request (muss 200 + Whitelist geben)
|
|
|
|
### Step 1: Get JWT Token (Supabase Auth)
|
|
|
|
```bash
|
|
# Login via Supabase Auth
|
|
curl -X POST 'http://192.168.45.104:3000/auth/v1/token?grant_type=password' \
|
|
-H "Content-Type: application/json" \
|
|
-H "apikey: <SUPABASE_ANON_KEY>" \
|
|
-d '{
|
|
"email": "max@beispiel.de",
|
|
"password": "SecurePassword123!"
|
|
}'
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzM3MDM2MDAwLCJzdWIiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJlbWFpbCI6Im1heEBiZWlzcGllbC5kZSIsInJvbGUiOiJhdXRoZW50aWNhdGVkIn0...",
|
|
"token_type": "bearer",
|
|
"expires_in": 3600,
|
|
"refresh_token": "...",
|
|
"user": {
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"email": "max@beispiel.de",
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 2: Get Instance Config (with JWT)
|
|
|
|
```bash
|
|
JWT_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/get_my_instance_config' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${JWT_TOKEN}" \
|
|
-d '{}'
|
|
```
|
|
|
|
### Expected Response (200 OK + Whitelist)
|
|
|
|
```json
|
|
[
|
|
{
|
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"customer_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"owner_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"ctid": 769697636,
|
|
"hostname": "sb-1769697636",
|
|
"fqdn": "sb-1769697636.userman.de",
|
|
"ip": "192.168.45.104",
|
|
"vlan": 90,
|
|
"status": "active",
|
|
"created_at": "2025-01-15T10:30:00Z",
|
|
"urls": {
|
|
"n8n_internal": "http://192.168.45.104:5678/",
|
|
"n8n_external": "https://sb-1769697636.userman.de",
|
|
"postgrest": "http://192.168.45.104:3000",
|
|
"chat_webhook": "https://sb-1769697636.userman.de/webhook/rag-chat-webhook/chat",
|
|
"chat_internal": "http://192.168.45.104:5678/webhook/rag-chat-webhook/chat",
|
|
"upload_form": "https://sb-1769697636.userman.de/form/rag-upload-form",
|
|
"upload_form_internal": "http://192.168.45.104:5678/form/rag-upload-form"
|
|
},
|
|
"supabase": {
|
|
"url_external": "http://192.168.45.104:3000",
|
|
"anon_key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjIwMDAwMDAwMDB9..."
|
|
},
|
|
"ollama": {
|
|
"url": "http://192.168.45.3:11434",
|
|
"model": "ministral-3:3b",
|
|
"embedding_model": "nomic-embed-text:latest"
|
|
},
|
|
"customer_email": "max@beispiel.de",
|
|
"first_name": "Max",
|
|
"last_name": "Mustermann",
|
|
"company": "Muster GmbH",
|
|
"customer_status": "trial"
|
|
}
|
|
]
|
|
```
|
|
|
|
**Status:** ✅ PASS - Authenticated user gets their instance config
|
|
|
|
### Step 3: Verify NO SECRETS in Response
|
|
|
|
```bash
|
|
# Check response does NOT contain secrets
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/get_my_instance_config' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${JWT_TOKEN}" \
|
|
-d '{}' | grep -E "password|service_role_key|jwt_secret|encryption_key|owner_password"
|
|
|
|
# Expected: NO OUTPUT (grep finds nothing)
|
|
```
|
|
|
|
**Status:** ✅ PASS - No secrets exposed
|
|
|
|
---
|
|
|
|
## Test 3: Not Found (User has no instance)
|
|
|
|
### Request
|
|
|
|
```bash
|
|
JWT_TOKEN="<token_for_user_without_instance>"
|
|
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/get_my_instance_config' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${JWT_TOKEN}" \
|
|
-d '{}'
|
|
```
|
|
|
|
### Expected Response (200 OK, empty array)
|
|
|
|
```json
|
|
[]
|
|
```
|
|
|
|
**Status:** ✅ PASS - Returns empty array when no instance found
|
|
|
|
---
|
|
|
|
## Test 4: Public Config (No Auth Required)
|
|
|
|
### Request
|
|
|
|
```bash
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/get_public_config' \
|
|
-H "Content-Type: application/json" \
|
|
-d '{}'
|
|
```
|
|
|
|
### Expected Response (200 OK)
|
|
|
|
```json
|
|
[
|
|
{
|
|
"registration_webhook_url": "https://api.botkonzept.de/webhook/botkonzept-registration",
|
|
"api_base_url": "https://api.botkonzept.de"
|
|
}
|
|
]
|
|
```
|
|
|
|
**Status:** ✅ PASS - Public config accessible without auth
|
|
|
|
---
|
|
|
|
## Test 5: Service Role - Store Installer JSON
|
|
|
|
### Request (Backend Only - Service Role Key)
|
|
|
|
```bash
|
|
SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MjAwMDAwMDAwMH0..."
|
|
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/store_installer_json' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${SERVICE_ROLE_KEY}" \
|
|
-d '{
|
|
"customer_email_param": "max@beispiel.de",
|
|
"lxc_id_param": 769697636,
|
|
"installer_json_param": {
|
|
"ctid": 769697636,
|
|
"hostname": "sb-1769697636",
|
|
"fqdn": "sb-1769697636.userman.de",
|
|
"ip": "192.168.45.104",
|
|
"vlan": 90,
|
|
"urls": {
|
|
"n8n_internal": "http://192.168.45.104:5678/",
|
|
"n8n_external": "https://sb-1769697636.userman.de",
|
|
"postgrest": "http://192.168.45.104:3000",
|
|
"chat_webhook": "https://sb-1769697636.userman.de/webhook/rag-chat-webhook/chat",
|
|
"chat_internal": "http://192.168.45.104:5678/webhook/rag-chat-webhook/chat",
|
|
"upload_form": "https://sb-1769697636.userman.de/form/rag-upload-form",
|
|
"upload_form_internal": "http://192.168.45.104:5678/form/rag-upload-form"
|
|
},
|
|
"postgres": {
|
|
"host": "postgres",
|
|
"port": 5432,
|
|
"db": "customer",
|
|
"user": "customer",
|
|
"password": "SECRET_PASSWORD_NEVER_EXPOSE"
|
|
},
|
|
"supabase": {
|
|
"url": "http://postgrest:3000",
|
|
"url_external": "http://192.168.45.104:3000",
|
|
"anon_key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"service_role_key": "SECRET_SERVICE_ROLE_KEY_NEVER_EXPOSE",
|
|
"jwt_secret": "SECRET_JWT_SECRET_NEVER_EXPOSE"
|
|
},
|
|
"ollama": {
|
|
"url": "http://192.168.45.3:11434",
|
|
"model": "ministral-3:3b",
|
|
"embedding_model": "nomic-embed-text:latest"
|
|
},
|
|
"n8n": {
|
|
"encryption_key": "SECRET_ENCRYPTION_KEY_NEVER_EXPOSE",
|
|
"owner_email": "admin@userman.de",
|
|
"owner_password": "SECRET_PASSWORD_NEVER_EXPOSE",
|
|
"secure_cookie": false
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
### Expected Response (200 OK)
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"instance_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"customer_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"message": "Installer JSON stored successfully"
|
|
}
|
|
```
|
|
|
|
**Status:** ✅ PASS - Installer JSON stored (backend only)
|
|
|
|
---
|
|
|
|
## Test 6: Service Role - Link Customer to Auth User
|
|
|
|
### Request (Backend Only - Service Role Key)
|
|
|
|
```bash
|
|
SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/link_customer_to_auth_user' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${SERVICE_ROLE_KEY}" \
|
|
-d '{
|
|
"customer_email_param": "max@beispiel.de",
|
|
"auth_user_id_param": "550e8400-e29b-41d4-a716-446655440000"
|
|
}'
|
|
```
|
|
|
|
### Expected Response (200 OK)
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"customer_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"auth_user_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
"message": "Customer linked to auth user successfully"
|
|
}
|
|
```
|
|
|
|
**Status:** ✅ PASS - Customer linked to auth user
|
|
|
|
---
|
|
|
|
## Test 7: Unauthorized Service Role Access
|
|
|
|
### Request (User JWT trying to access service role function)
|
|
|
|
```bash
|
|
USER_JWT_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXV0aGVudGljYXRlZCJ9..."
|
|
|
|
curl -X POST 'http://192.168.45.104:3000/rpc/store_installer_json' \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${USER_JWT_TOKEN}" \
|
|
-d '{
|
|
"customer_email_param": "max@beispiel.de",
|
|
"lxc_id_param": 769697636,
|
|
"installer_json_param": {}
|
|
}'
|
|
```
|
|
|
|
### Expected Response (403 Forbidden)
|
|
|
|
```json
|
|
{
|
|
"code": "PGRST301",
|
|
"message": "Forbidden: service_role required",
|
|
"details": null,
|
|
"hint": null
|
|
}
|
|
```
|
|
|
|
**Status:** ✅ PASS - User cannot access service role functions
|
|
|
|
---
|
|
|
|
## Security Checklist
|
|
|
|
### ✅ Whitelist (Frontend-Safe)
|
|
|
|
```json
|
|
{
|
|
"ctid": 769697636,
|
|
"hostname": "sb-1769697636",
|
|
"fqdn": "sb-1769697636.userman.de",
|
|
"ip": "192.168.45.104",
|
|
"vlan": 90,
|
|
"urls": { ... },
|
|
"supabase": {
|
|
"url_external": "http://192.168.45.104:3000",
|
|
"anon_key": "eyJhbGc..."
|
|
},
|
|
"ollama": { ... }
|
|
}
|
|
```
|
|
|
|
### ❌ Blacklist (NEVER Expose)
|
|
|
|
```json
|
|
{
|
|
"postgres": {
|
|
"password": "NEVER_EXPOSE"
|
|
},
|
|
"supabase": {
|
|
"service_role_key": "NEVER_EXPOSE",
|
|
"jwt_secret": "NEVER_EXPOSE"
|
|
},
|
|
"n8n": {
|
|
"owner_password": "NEVER_EXPOSE",
|
|
"encryption_key": "NEVER_EXPOSE"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Test Script
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Complete API test script
|
|
|
|
POSTGREST_URL="http://192.168.45.104:3000"
|
|
ANON_KEY="<your_anon_key>"
|
|
SERVICE_ROLE_KEY="<your_service_role_key>"
|
|
|
|
echo "=== Test 1: Unauthenticated Request (should fail) ==="
|
|
curl -X POST "${POSTGREST_URL}/rpc/get_my_instance_config" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{}'
|
|
echo -e "\n"
|
|
|
|
echo "=== Test 2: Login and Get JWT ==="
|
|
LOGIN_RESPONSE=$(curl -X POST "${POSTGREST_URL}/auth/v1/token?grant_type=password" \
|
|
-H "Content-Type: application/json" \
|
|
-H "apikey: ${ANON_KEY}" \
|
|
-d '{
|
|
"email": "max@beispiel.de",
|
|
"password": "SecurePassword123!"
|
|
}')
|
|
|
|
JWT_TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.access_token')
|
|
echo "JWT Token: ${JWT_TOKEN:0:50}..."
|
|
echo -e "\n"
|
|
|
|
echo "=== Test 3: Get My Instance Config (authenticated) ==="
|
|
curl -X POST "${POSTGREST_URL}/rpc/get_my_instance_config" \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${JWT_TOKEN}" \
|
|
-d '{}' | jq .
|
|
echo -e "\n"
|
|
|
|
echo "=== Test 4: Verify No Secrets ==="
|
|
RESPONSE=$(curl -s -X POST "${POSTGREST_URL}/rpc/get_my_instance_config" \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${JWT_TOKEN}" \
|
|
-d '{}')
|
|
|
|
if echo "$RESPONSE" | grep -qE "password|service_role_key|jwt_secret|encryption_key"; then
|
|
echo "❌ FAIL: Secrets found in response!"
|
|
else
|
|
echo "✅ PASS: No secrets in response"
|
|
fi
|
|
echo -e "\n"
|
|
|
|
echo "=== Test 5: Public Config (no auth) ==="
|
|
curl -X POST "${POSTGREST_URL}/rpc/get_public_config" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{}' | jq .
|
|
echo -e "\n"
|
|
|
|
echo "=== All tests completed ==="
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend Integration Example
|
|
|
|
```javascript
|
|
// Frontend code (React/Vue/etc.)
|
|
import { createClient } from '@supabase/supabase-js'
|
|
|
|
const supabase = createClient(
|
|
'http://192.168.45.104:3000',
|
|
'<ANON_KEY>' // Public anon key - safe to use in frontend
|
|
)
|
|
|
|
// Login
|
|
const { data: authData, error: authError } = await supabase.auth.signInWithPassword({
|
|
email: 'max@beispiel.de',
|
|
password: 'SecurePassword123!'
|
|
})
|
|
|
|
if (authError) {
|
|
console.error('Login failed:', authError)
|
|
return
|
|
}
|
|
|
|
// Get instance config (uses JWT automatically)
|
|
const { data, error } = await supabase.rpc('get_my_instance_config')
|
|
|
|
if (error) {
|
|
console.error('Failed to get config:', error)
|
|
return
|
|
}
|
|
|
|
console.log('Instance config:', data)
|
|
// data[0].urls.chat_webhook
|
|
// data[0].urls.upload_form
|
|
// etc.
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
✅ **Authenticated requests work** (with JWT)
|
|
✅ **Unauthenticated requests blocked** (401/403)
|
|
✅ **No secrets exposed** (whitelist only)
|
|
✅ **Service role functions protected** (backend only)
|
|
✅ **RLS enforced** (users see only their own data)
|
|
|
|
**Security:** ✅ PASS
|
|
**Functionality:** ✅ PASS
|
|
**Ready for production:** ✅ YES
|