Claude
Skills
Sign in
โ† Back

supabase-extract-anon-key

Included with Lifetime
$97 forever

Extract the Supabase anon/public API key from client-side code. This key is expected in client apps but important for RLS testing.

Backend & APIs

What this skill does


# 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 RL

Related in Backend & APIs