Create a test user (with explicit permission) to audit what authenticated users can access vs anonymous users. Detects IDOR, cross-user access, and privilege escalation.
Install
npx skillscat add yoanbernabeu/supabase-pentest-skills/supabase-audit-authenticated Install via the SkillsCat registry.
Authenticated User Audit
๐ด CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED
You MUST write to context files AS YOU GO, not just at the end.
- Write to
.sb-pentest-context.jsonIMMEDIATELY after each test- Log to
.sb-pentest-audit.logBEFORE and AFTER each action- DO NOT wait until the skill completes to update files
- If the skill crashes or is interrupted, all prior findings must already be saved
This is not optional. Failure to write progressively is a critical error.
This skill creates a test user (with explicit permission) to compare authenticated vs anonymous access and detect IDOR vulnerabilities.
โ ๏ธ IMPORTANT: User Consent Required
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ USER CREATION CONSENT REQUIRED โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ โ
โ This skill will CREATE A TEST USER in your Supabase project. โ
โ โ
โ The user will be created with: โ
โ โข Email: pentest-[random]@security-audit.local โ
โ โข Password: Strong random password (32+ chars) โ
โ โข Purpose: Testing authenticated access vs anonymous โ
โ โ
โ At the end of the audit, you will be asked if you want to โ
โ DELETE the test user (recommended). โ
โ โ
โ Do you authorize the creation of a test user? โ
โ Type "yes, create test user" to proceed. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโDO NOT proceed without explicit user consent.
When to Use This Skill
- After completing anonymous access tests
- To detect IDOR (Insecure Direct Object Reference) vulnerabilities
- To test cross-user data access
- To verify RLS policies work for authenticated users
- To find privilege escalation issues
Prerequisites
- Signup must be open (or use invite flow)
- Anon key available
- Anonymous audit completed (recommended)
Why Authenticated Testing Matters
Many vulnerabilities only appear with authentication:
| Vulnerability | Anonymous | Authenticated |
|---|---|---|
| RLS bypass (no RLS) | โ Detectable | โ Detectable |
| IDOR | โ Not visible | โ Only visible |
| Cross-user access | โ Not visible | โ Only visible |
| Privilege escalation | โ Not visible | โ Only visible |
| Overly permissive RLS | Partial | โ Full detection |
Test User Creation
Email Format
pentest-[8-char-random]@security-audit.localExample: pentest-a7b3c9d2@security-audit.local
Password Generation
Strong password with:
- 32+ characters
- Uppercase, lowercase, numbers, symbols
- Cryptographically random
Example: Xk9$mP2#vL5@nQ8&jR4*wY7!hT3%bU6^
The password is displayed ONCE and saved to evidence.
Tests Performed
1. User Creation & Login
# Create user
curl -X POST "$SUPABASE_URL/auth/v1/signup" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "pentest-xxx@security-audit.local", "password": "[STRONG_PASSWORD]"}'
# Login and get JWT
curl -X POST "$SUPABASE_URL/auth/v1/token?grant_type=password" \
-H "apikey: $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "pentest-xxx@security-audit.local", "password": "[STRONG_PASSWORD]"}'2. Authenticated vs Anonymous Comparison
For each table:
| Test | Anonymous | Authenticated | Finding |
|---|---|---|---|
| SELECT | 0 rows | 1,247 rows | ๐ด Auth-only exposure |
| Own data | N/A | Only own row | โ RLS working |
| Other users' data | N/A | All rows | ๐ด Cross-user access |
3. IDOR Testing
# As test user, try to access other user's data
curl "$SUPABASE_URL/rest/v1/orders?user_id=eq.[OTHER_USER_ID]" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer [TEST_USER_JWT]"
# If returns data: IDOR vulnerability!4. Cross-User Access
# Get test user's ID from JWT
TEST_USER_ID=$(echo $JWT | jq -r '.sub')
# Try to access data belonging to a different user
curl "$SUPABASE_URL/rest/v1/profiles?id=neq.$TEST_USER_ID" \
-H "Authorization: Bearer [TEST_USER_JWT]"
# If returns other users' profiles: Cross-user access!5. Storage with Authentication
# Test authenticated storage access
curl "$SUPABASE_URL/storage/v1/object/list/documents" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer [TEST_USER_JWT]"
# Compare with anonymous results6. Realtime with Authentication
// Subscribe to table changes as authenticated user
const channel = supabase.channel('test')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'orders'
}, payload => console.log(payload))
.subscribe()
// Does it receive OTHER users' order changes?Output Format
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
AUTHENTICATED USER AUDIT
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Test User Creation
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Status: โ
User created successfully
Test User Details:
โโโ Email: pentest-a7b3c9d2@security-audit.local
โโโ User ID: 550e8400-e29b-41d4-a716-446655440099
โโโ Password: [Saved to evidence - shown once]
โโโ JWT obtained: โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Anonymous vs Authenticated Comparison
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Table: users
โโโ Anonymous access: 0 rows
โโโ Authenticated access: 1,247 rows โ ALL USERS!
โโโ Status: ๐ด P0 - Data hidden from anon but exposed to any auth user
Table: orders
โโโ Anonymous access: 0 rows (blocked)
โโโ Authenticated access: 1 row (own orders only)
โโโ Status: โ
RLS working correctly
Table: profiles
โโโ Anonymous access: 0 rows
โโโ Authenticated access: 1,247 rows โ ALL PROFILES!
โโโ Own profile only expected: โ NO
โโโ Status: ๐ด P0 - Cross-user profile access
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
IDOR Testing
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Test: Access other user's orders by ID
โโโ Request: GET /orders?user_id=eq.[other-user-id]
โโโ Auth: Test user JWT
โโโ Response: 200 OK - 15 orders returned
โโโ Status: ๐ด P0 - IDOR VULNERABILITY
Proof:
curl "$URL/rest/v1/orders?user_id=eq.other-user-uuid" \
-H "Authorization: Bearer [test-user-jwt]"
# Returns orders belonging to other-user-uuid!
Test: Access admin endpoints
โโโ Request: GET /functions/v1/admin-panel
โโโ Auth: Test user JWT (regular user)
โโโ Response: 200 OK - Admin data returned!
โโโ Status: ๐ด P0 - PRIVILEGE ESCALATION
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Storage with Authentication
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Bucket: documents
โโโ Anonymous: โ 0 files (blocked)
โโโ Authenticated: โ
523 files visible โ ALL USERS' FILES!
โโโ Status: ๐ด P1 - Auth users see all documents
Bucket: user-uploads
โโโ Anonymous: โ 0 files
โโโ Authenticated: 3 files (own files only)
โโโ Status: โ
RLS working correctly
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
New Findings (Auth-only):
โโโ ๐ด P0: users table - all users visible to any auth user
โโโ ๐ด P0: profiles table - cross-user access
โโโ ๐ด P0: IDOR in orders - can access any user's orders
โโโ ๐ด P0: Privilege escalation in admin-panel
โโโ ๐ P1: documents bucket - all files visible to auth users
Comparison:
โโโ Issues found (Anonymous): 3
โโโ Issues found (Authenticated): 8 โ 5 NEW ISSUES!
โโโ Auth-only vulnerabilities: 5
Recommendation:
These issues were NOT visible in anonymous testing!
Always test with authenticated users.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Cleanup
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๏ธ Test user still exists in database.
Do you want to delete the test user?
Email: pentest-a7b3c9d2@security-audit.local
[This requires service_role key or manual deletion]
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโContext Output
{
"authenticated_audit": {
"timestamp": "2025-01-31T12:00:00Z",
"test_user": {
"email": "pentest-a7b3c9d2@security-audit.local",
"user_id": "550e8400-e29b-41d4-a716-446655440099",
"created_at": "2025-01-31T12:00:00Z",
"deleted": false
},
"comparison": {
"tables": {
"users": {
"anon_access": 0,
"auth_access": 1247,
"expected_auth_access": "own_row_only",
"severity": "P0",
"finding": "All users visible to any authenticated user"
},
"orders": {
"anon_access": 0,
"auth_access": 1,
"expected_auth_access": "own_rows_only",
"severity": null,
"finding": "RLS working correctly"
}
},
"idor_tests": [
{
"test": "access_other_user_orders",
"vulnerable": true,
"severity": "P0",
"proof": "curl command..."
}
],
"privilege_escalation": [
{
"endpoint": "/functions/v1/admin-panel",
"vulnerable": true,
"severity": "P0"
}
]
},
"summary": {
"anon_issues": 3,
"auth_issues": 8,
"auth_only_issues": 5
}
}
}RLS Policy Examples
Correct: Users see only their own data
-- This RLS policy is correct
CREATE POLICY "Users see own data"
ON users FOR SELECT
USING (auth.uid() = id);
-- Result:
-- Anonymous: 0 rows
-- Authenticated: 1 row (own data)Incorrect: All authenticated users see everything
-- This RLS policy is WRONG
CREATE POLICY "Authenticated users see all"
ON users FOR SELECT
USING (auth.role() = 'authenticated'); -- โ Too permissive!
-- Result:
-- Anonymous: 0 rows
-- Authenticated: ALL rows โ VULNERABILITY!Correct fix:
-- Fix: Add user ownership check
CREATE POLICY "Users see own data"
ON users FOR SELECT
USING (auth.uid() = id); -- โ
Only own rowCleanup Options
Option 1: Manual deletion (Dashboard)
Supabase Dashboard โ Authentication โ Users โ Find test user โ DeleteOption 2: Via service_role key (if available)
curl -X DELETE "$SUPABASE_URL/auth/v1/admin/users/[USER_ID]" \
-H "apikey: $SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SERVICE_ROLE_KEY"Option 3: Leave for later
The test user uses a non-functional email domain (security-audit.local) and cannot be used maliciously.
MANDATORY: Evidence Collection
๐ Evidence Directory: .sb-pentest-evidence/05-auth-audit/authenticated-tests/
Evidence Files to Create
| File | Content |
|---|---|
test-user-created.json |
Test user details (password saved securely) |
anon-vs-auth-comparison.json |
Side-by-side comparison |
idor-tests/[table].json |
IDOR test results |
privilege-escalation.json |
Privilege escalation tests |
Evidence Format
{
"evidence_id": "AUTH-TEST-001",
"timestamp": "2025-01-31T12:00:00Z",
"category": "auth-audit",
"type": "authenticated_testing",
"test_user": {
"email": "pentest-a7b3c9d2@security-audit.local",
"user_id": "550e8400-...",
"password": "[STORED SECURELY - DO NOT COMMIT]"
},
"comparison_test": {
"table": "users",
"anonymous": {
"curl_command": "curl '$URL/rest/v1/users' -H 'apikey: $ANON_KEY'",
"response_status": 200,
"rows_returned": 0
},
"authenticated": {
"curl_command": "curl '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -H 'Authorization: Bearer $JWT'",
"response_status": 200,
"rows_returned": 1247
},
"finding": {
"severity": "P0",
"issue": "All users visible to any authenticated user",
"expected": "Only own row should be visible",
"impact": "Full user enumeration for any authenticated user"
}
}
}Add to curl-commands.sh
# === AUTHENTICATED TESTING ===
# NOTE: Replace [JWT] with test user's JWT
# Compare anonymous vs authenticated access
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" -H "apikey: $ANON_KEY"
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" -H "apikey: $ANON_KEY" -H "Authorization: Bearer [JWT]"
# IDOR test - access other user's data
curl -s "$SUPABASE_URL/rest/v1/orders?user_id=eq.[OTHER_USER_ID]" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer [JWT]"
# Cross-user profile access
curl -s "$SUPABASE_URL/rest/v1/profiles?id=neq.[TEST_USER_ID]" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer [JWT]"MANDATORY: Progressive Context File Updates
โ ๏ธ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.
Critical Rule: Write As You Go
DO NOT batch all writes at the end. Instead:
- Before user creation โ Log consent and action to
.sb-pentest-audit.log - After user created โ Immediately save user details to context and evidence
- After each comparison test โ Update
.sb-pentest-context.jsonwith results - After each IDOR test โ Save evidence immediately
This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
Required Actions (Progressive)
Log user creation:
[TIMESTAMP] [supabase-audit-authenticated] [CONSENT] User authorized test user creation [TIMESTAMP] [supabase-audit-authenticated] [CREATED] Test user pentest-xxx@security-audit.localSave test user to context immediately:
{ "authenticated_audit": { "test_user": { "email": "...", "user_id": "...", "created_at": "..." } } }Log each finding as discovered:
[TIMESTAMP] [supabase-audit-authenticated] [FINDING] P0: IDOR in orders table
FAILURE TO UPDATE CONTEXT FILES PROGRESSIVELY IS NOT ACCEPTABLE.
Related Skills
supabase-audit-auth-signupโ Test if signup is open firstsupabase-audit-tables-readโ Compare with anonymous resultssupabase-audit-rlsโ Deep dive into RLS policiessupabase-audit-functionsโ Test function access with authsupabase-reportโ Include auth-only findings in report