Supabase JWT 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 and analyzes JSON Web Tokens (JWTs) related to Supabase from client-side code.
When to Use This Skill
To find all JWT tokens exposed in client code
To analyze token claims and expiration
To detect hardcoded user tokens (security issue)
To understand the authentication flow
Prerequisites
Target application accessible
Supabase detection completed (auto-invokes if needed)
Types of JWTs in Supabase
Type
Purpose
Client Exposure
Anon Key
API authentication
โ
Expected
Service Role Key
Admin access
โ Never
Access Token
User session
โ ๏ธ Dynamic only
Refresh Token
Token renewal
โ ๏ธ Dynamic only
Detection Patterns
1. API Keys (Static)
// Supabase API keys are JWTs
const
SUPABASE_KEY
=
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
2. Hardcoded User Tokens (Problem)
// โ Should never be hardcoded
const
userToken
=
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0...'
3. Storage Key Patterns
// Code referencing where JWTs are stored
localStorage
.
getItem
(
'supabase.auth.token'
)
localStorage
.
getItem
(
'sb-abc123-auth-token'
)
sessionStorage
.
getItem
(
'supabase_session'
)
Usage
Basic Extraction
Extract JWTs from https://myapp.example.com
With Claim Analysis
Extract and analyze all JWTs from https://myapp.example.com
Output Format
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT EXTRACTION RESULTS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Found: 3 JWTs
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT #1: Supabase Anon Key
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Type: API Key (anon)
Status: โ
Expected in client code
Header:
โโโ alg: HS256
โโโ typ: JWT
Payload:
โโโ iss: supabase
โโโ ref: abc123def
โโโ role: anon
โโโ iat: 2021-12-20T00:00:00Z
โโโ exp: 2031-12-20T00:00:00Z
Location: /static/js/main.js:1247
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT #2: Hardcoded User Token โ ๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Type: User Access Token
Status: โ ๏ธ P1 - Should not be hardcoded
Header:
โโโ alg: HS256
โโโ typ: JWT
Payload:
โโโ sub: 12345678-1234-1234-1234-123456789012
โโโ email: developer@company.com
โโโ role: authenticated
โโโ iat: 2025-01-15T10:00:00Z
โโโ exp: 2025-01-15T11:00:00Z (EXPIRED)
Location: /static/js/debug.js:45
Risk: This token may belong to a real user account.
Even if expired, it reveals user information.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT #3: Storage Reference
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Type: Storage Key Pattern
Status: โน๏ธ Informational
Pattern: localStorage.getItem('sb-abc123def-auth-token')
Location: /static/js/auth.js:89
Note: This is the expected storage key for user sessions.
Actual token value is set at runtime.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
JWT Claim Analysis
The skill identifies key claims:
Standard Claims
Claim
Description
Security Impact
sub
User ID
Identifies specific user
email
User email
PII exposure if hardcoded
role
Permission level
service_role
is critical
exp
Expiration
Expired tokens less risky
iat
Issued at
Indicates when created
Supabase-Specific Claims
Claim
Description
ref
Project reference
iss
Should be "supabase"
aal
Authenticator assurance level
amr
Authentication methods used
Security Findings
P0 - Critical
๐ด Service role key exposed (role: service_role)
โ Immediate key rotation required
P1 - High
๐ User token hardcoded with PII (email, sub visible)
โ Remove from code, may need to notify user
P2 - Medium
๐ก Expired test token in code
โ Clean up, potential information disclosure
Context Output
Saved to
.sb-pentest-context.json
:
{
"jwts"
:
{
"found"
:
3
,
"api_keys"
:
[
{
"type"
:
"anon"
,
"project_ref"
:
"abc123def"
,
"location"
:
"/static/js/main.js:1247"
}
]
,
"user_tokens"
:
[
{
"type"
:
"access_token"
,
"hardcoded"
:
true
,
"severity"
:
"P1"
,
"claims"
:
{
"sub"
:
"12345678-1234-1234-1234-123456789012"
,
"email"
:
"developer@company.com"
,
"expired"
:
true
}
,
"location"
:
"/static/js/debug.js:45"
}
]
,
"storage_patterns"
:
[
{
"pattern"
:
"sb-abc123def-auth-token"
,
"storage"
:
"localStorage"
,
"location"
:
"/static/js/auth.js:89"
}
]
}
}
Common Issues
โ
Problem:
JWT appears truncated
โ
Solution:
May span multiple lines. The skill attempts to reassemble.
โ
Problem:
JWT won't decode
โ
Solution:
May be encrypted (JWE) or custom format. Noted as undecodable.
โ
Problem:
Many false positives
โ
Solution:
Base64 strings that look like JWTs. Skill validates structure.
Remediation for Hardcoded Tokens
Before (Wrong)
// โ Never hardcode user tokens
const
adminToken
=
'eyJhbGciOiJIUzI1NiI...'
fetch
(
'/api/admin'
,
{
headers
:
{
Authorization
:
Bearer
${
adminToken
}
}
}
)
After (Correct)
// โ
Get token from Supabase session
const
{
data
:
{
session
}
}
=
await
supabase
.
auth
.
getSession
(
)
fetch
(
'/api/admin'
,
{
headers
:
{
Authorization
:
Bearer
${
session
.
access_token
}
}
}
)
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 starting any action
โ Log the action to
.sb-pentest-audit.log
After each discovery
โ Immediately update
.sb-pentest-context.json
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)
Update
.sb-pentest-context.json
with extracted data:
{
"jwts"
:
{
"found"
:
3
,
"api_keys"
:
[
...
]
,
"user_tokens"
:
[
...
]
,
"storage_patterns"
:
[
...
]
}
}
Log to
.sb-pentest-audit.log
:
[TIMESTAMP] [supabase-extract-jwt] [START] Beginning JWT extraction
[TIMESTAMP] [supabase-extract-jwt] [SUCCESS] Found 3 JWTs
[TIMESTAMP] [supabase-extract-jwt] [CONTEXT_UPDATED] .sb-pentest-context.json updated
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-jwts.json
All JWTs found with analysis
Evidence Format
{
"evidence_id"
:
"EXT-JWT-001"
,
"timestamp"
:
"2025-01-31T10:08:00Z"
,
"category"
:
"extraction"
,
"type"
:
"jwt_extraction"
,
"jwts_found"
:
[
{
"type"
:
"anon_key"
,
"severity"
:
"info"
,
"location"
:
"/static/js/main.js:1247"
,
"decoded_payload"
:
{
"iss"
:
"supabase"
,
"ref"
:
"abc123def"
,
"role"
:
"anon"
}
}
,
{
"type"
:
"hardcoded_user_token"
,
"severity"
:
"P1"
,
"location"
:
"/static/js/debug.js:45"
,
"decoded_payload"
:
{
"sub"
:
"[REDACTED]"
,
"email"
:
"[REDACTED]@example.com"
,
"role"
:
"authenticated"
,
"exp"
:
"2025-01-15T11:00:00Z"
}
,
"expired"
:
true
,
"issue"
:
"Hardcoded user token with PII"
}
]
,
"storage_patterns_found"
:
[
{
"pattern"
:
"localStorage.getItem('sb-abc123def-auth-token')"
,
"location"
:
"/static/js/auth.js:89"
}
]
}
supabase-extract-jwt
ๅฎ่ฃ
npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-extract-jwt