home / skills / yoanbernabeu / supabase-pentest-skills / supabase-extract-anon-key
This skill extracts the Supabase anon key from client code to enable secure testing and RLS verification across projects.
npx playbooks add skill yoanbernabeu/supabase-pentest-skills --skill supabase-extract-anon-keyReview the files below or copy the command above to add this skill to your agents.
---
name: supabase-extract-anon-key
description: Extract the Supabase anon/public API key from client-side code. This key is expected in client apps but important for RLS testing.
---
# Supabase Anon Key Extraction
> π΄ **CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED**
>
> You MUST write to context files **AS YOU GO**, not just at the end.
> - Write to `.sb-pentest-context.json` **IMMEDIATELY after each discovery**
> - Log to `.sb-pentest-audit.log` **BEFORE 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 extracts the Supabase anonymous (public) API key from client-side code.
## When to Use This Skill
- After extracting the Supabase URL, to get the API key for testing
- To verify that only the anon key (not service key) is exposed
- Before running API audit skills that require authentication
## Prerequisites
- Supabase URL extracted (or will auto-invoke `supabase-extract-url`)
- Target application accessible
## Understanding Anon Keys
The **anon key** (also called public key) is:
- β
**Expected** to be in client-side code
- β
**Safe** when RLS (Row Level Security) is properly configured
- β οΈ **Risky** if RLS is missing or misconfigured
- β **Not the same** as the service_role key (which should NEVER be in client code)
### Key Format
Supabase anon keys are JWTs:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFiYzEyMyIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNjQwMDAwMDAwLCJleHAiOjE5NTUzNjAwMDB9.xxxx
```
Key characteristics:
- Starts with `eyJ` (base64 encoded `{"alg":`)
- Contains `"role":"anon"` in payload
- Project reference in `"ref"` claim
## Extraction Patterns
The skill searches for:
### 1. Direct Key Assignment
```javascript
const SUPABASE_KEY = 'eyJhbGci...'
const SUPABASE_ANON_KEY = 'eyJhbGci...'
```
### 2. Client Initialization
```javascript
createClient(url, 'eyJhbGci...')
createClient(url, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)
```
### 3. Environment Variable Patterns
```javascript
NEXT_PUBLIC_SUPABASE_ANON_KEY
VITE_SUPABASE_ANON_KEY
REACT_APP_SUPABASE_KEY
SUPABASE_KEY
```
## Usage
### Basic Extraction
```
Extract Supabase anon key from https://myapp.example.com
```
### If URL Already Known
```
Extract anon key for project abc123def
```
## Output Format
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ANON KEY EXTRACTED
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Type: anon (public)
Severity: βΉοΈ Expected (verify RLS configuration)
Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJz
dXBhYmFzZSIsInJlZiI6ImFiYzEyM2RlZiIsInJvbGUiOiJhbm
9uIiwiaWF0IjoxNjQwMDAwMDAwLCJleHAiOjE5NTUzNjAwMDB9
.xxxxxxxxxxxxx
Decoded Payload:
βββ iss: supabase
βββ ref: abc123def
βββ role: anon
βββ iat: 2021-12-20T00:00:00Z
βββ exp: 2031-12-20T00:00:00Z
Found in:
βββ /static/js/main.js (line 1253)
createClient('https://abc123def.supabase.co', 'eyJhbGci...')
Next Steps:
βββ Run supabase-audit-rls to test if RLS protects your data
βββ Run supabase-audit-tables-read to see what's accessible
βββ Run supabase-extract-service-key to check for critical leaks
Context updated: .sb-pentest-context.json
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
## Key Validation
The skill validates the extracted key:
```
Validation:
βββ Format: β
Valid JWT structure
βββ Decode: β
Payload readable
βββ Role: β
Confirmed "anon" role
βββ Project: β
Matches extracted URL (abc123def)
βββ Expiry: β
Not expired (expires 2031-12-20)
```
## Multiple Keys
If multiple keys are found:
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MULTIPLE KEYS FOUND
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β οΈ 2 potential Supabase keys detected
1. Anon Key (confirmed)
βββ Role: anon, Project: abc123def
2. Unknown Key
βββ Role: service_role β οΈ SEE supabase-extract-service-key
This may be a CRITICAL security issue!
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
## Context Output
Saved to `.sb-pentest-context.json`:
```json
{
"supabase": {
"anon_key": "eyJhbGci...",
"anon_key_decoded": {
"iss": "supabase",
"ref": "abc123def",
"role": "anon",
"iat": 1640000000,
"exp": 1955360000
},
"anon_key_sources": [
{
"file": "/static/js/main.js",
"line": 1253
}
]
}
}
```
## Security Assessment
| Finding | Severity | Description |
|---------|----------|-------------|
| Anon key in client | βΉοΈ Info | Expected, but test RLS |
| Anon key expired | β οΈ P2 | Key should be rotated |
| Multiple anon keys | β οΈ P2 | May indicate key rotation issues |
| Role is not "anon" | π΄ P0 | Wrong key type exposed! |
## Common Issues
β **Problem:** Key found but won't decode
β
**Solution:** May be obfuscated or split. Try:
```
Extract anon key with deobfuscation from https://myapp.example.com
```
β **Problem:** Key doesn't match URL project
β
**Solution:** App may use multiple Supabase projects. Both keys are recorded.
β **Problem:** No key found but Supabase detected
β
**Solution:** Key may be fetched at runtime. Check network requests:
```
Monitor network for anon key on https://myapp.example.com
```
## Best Practices Reminder
For developers reading this report:
1. **Anon key in client is normal** β It's designed for this
2. **RLS is critical** β The anon key relies on RLS for security
3. **Never use service_role in client** β Use Edge Functions instead
4. **Rotate keys periodically** β Available in Supabase Dashboard
## 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:
1. **Before starting any action** β Log the action to `.sb-pentest-audit.log`
2. **After each discovery** β Immediately update `.sb-pentest-context.json`
3. **After each significant step** β Log completion to `.sb-pentest-audit.log`
This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
### Required Actions (Progressive)
1. **Update `.sb-pentest-context.json`** with extracted data:
```json
{
"supabase": {
"anon_key": "eyJhbGci...",
"anon_key_decoded": { ... },
"anon_key_sources": [ ... ]
}
}
```
2. **Log to `.sb-pentest-audit.log`**:
```
[TIMESTAMP] [supabase-extract-anon-key] [START] Beginning anon key extraction
[TIMESTAMP] [supabase-extract-anon-key] [SUCCESS] Anon key extracted
[TIMESTAMP] [supabase-extract-anon-key] [CONTEXT_UPDATED] .sb-pentest-context.json updated
```
3. **If files don't exist**, create them before writing.
**FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE.**
## MANDATORY: Evidence Collection
π **Evidence Directory:** `.sb-pentest-evidence/02-extraction/`
### Evidence Files to Create
| File | Content |
|------|---------|
| `extracted-anon-key.json` | Anon key with decoded JWT payload |
### Evidence Format
```json
{
"evidence_id": "EXT-ANON-001",
"timestamp": "2025-01-31T10:07:00Z",
"category": "extraction",
"type": "anon_key",
"severity": "info",
"key_data": {
"key_prefix": "eyJhbGciOiJIUzI1NiI...",
"key_suffix": "...xxxx",
"full_key_length": 256
},
"decoded_payload": {
"iss": "supabase",
"ref": "abc123def",
"role": "anon",
"iat": "2021-12-20T00:00:00Z",
"exp": "2031-12-20T00:00:00Z"
},
"source": {
"file": "/static/js/main.js",
"line": 1253,
"context": "createClient('https://abc123def.supabase.co', 'eyJhbGci...')"
},
"validation": {
"format_valid": true,
"role_confirmed": "anon",
"project_matches": true,
"expired": false
}
}
```
## Related Skills
- `supabase-extract-url` β Get URL first (auto-invoked if needed)
- `supabase-extract-service-key` β Check for critical service key leak
- `supabase-audit-rls` β Test if RLS protects your data
- `supabase-audit-tables-read` β See what data is accessible with this key
This skill extracts the Supabase anon (public) API key from client-side code and validates its format and claims. It records discovery context and evidence immediately so findings are preserved during interruption. Use the result to drive RLS testing and further API audits.
The skill scans client assets and build artifacts for common key patterns, createClient calls, and environment variable usages. It validates JWT structure, decodes the payload to confirm the role and project ref, and matches the key to any known Supabase URL. Every discovery is logged and saved progressively to context and evidence files.
Is an anon key dangerous if found in client code?
Noβanon keys are expected in client apps, but they rely on properly configured RLS; test RLS to assess actual risk.
What if the key won't decode?
It may be obfuscated or split. Try deobfuscation and reassembly, or monitor network requests at runtime.
What if multiple keys are found?
Record each key, decode payloads, and flag any non-anon roles (service_role) as critical.