Back to skills
SkillHub ClubShip Full StackFull StackBackendTesting

supabase-extract-anon-key

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

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
31
Hot score
89
Updated
March 19, 2026
Overall rating
C2.0
Composite score
2.0
Best-practice grade
B84.0

Install command

npx @skill-hub/cli install yoanbernabeu-supabase-pentest-skills-supabase-extract-anon-key

Repository

yoanbernabeu/supabase-pentest-skills

Skill path: skills/extraction/supabase-extract-anon-key

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

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Backend, Testing.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: yoanbernabeu.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install supabase-extract-anon-key into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/yoanbernabeu/supabase-pentest-skills before adding supabase-extract-anon-key to shared team environments
  • Use supabase-extract-anon-key for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
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
supabase-extract-anon-key | SkillHub