Obfuscation CodePen Zero Dependencies MIT v1.2.0

SaltyKeys.js

A zero-dependency library that ties an obfuscated API key to the specific CodePen pen it was generated for. Copy the encoded token elsewhere — it decodes to nothing.

The Problem

CodePen is a popular tool for sharing front-end demos. A common workflow: build a demo calling a third-party API (OpenAI, Google Maps, weather services). The problem — CodePen pens are public. Embed your real API key and anyone who views the pen can read and steal it.

The correct long-term answer is a server-side API proxy. But setting one up is overkill for a quick demo, a classroom exercise, or a proof-of-concept. SaltyKeys.js fills that gap by encoding the key in a token that only works on the pen it was built for.

Context-bound

The token embeds the pen's ID as a salt. A copied token can't decode anywhere else.

Zero dependencies

A single static class. No build step, no npm install, paste the source and go.

Honest about limits

Obfuscation only — not encryption. The docs say so clearly. Don't use this in production.

Pen ID Extraction

getPenId() parses window.location.href against the CodePen URL pattern. Inside an iframe embed, it falls back to the injected <link rel="canonical">, which always points to the canonical pen URL.

  • Result cached after the first call — subsequent lookups are free
  • Renders a visible warning if the pen is unsaved and ID can't be resolved
  • Overridable for non-CodePen environments via a custom URL pattern or function override

One-Time Setup

Key generation happens once, in the browser console on the saved pen. The raw key never appears in the pen's source.

  1. 1.Save the pen (unsaved pens have no ID)
  2. 2.Paste SaltyKeys.js into the JS panel or link via CDN
  3. 3.Run SaltyKeys.generateSaltedKey('your-api-key') in the console
  4. 4.Copy the output token into your pen's source as a constant
  5. 5.Retrieve at runtime with SaltyKeys.getApiKey(SALTED)

Token Encoding

Every token bakes in four components:

  • API key — your real secret
  • Pen ID — the salt that makes the token environment-specific
  • Timestamp — adds entropy so two calls with the same key produce different tokens
  • Nonce — random 8-character string for additional uniqueness

Combined, base64-encoded, then reversed. Cheap visual obfuscation on top of context-binding.

Context Validation

At runtime, getApiKey() reverses the token, base64-decodes it, splits off the trailing nonce, timestamp, and pen ID, then compares the embedded pen ID against getPenId().

  • Match → original API key is returned
  • No match → returns null, logs a warning

Keys containing : characters are handled correctly — only the last three segments (nonce, timestamp, pen ID) are popped; everything before is rejoined as the key.

How It Works

Encode once offline, decode at runtime on the correct pen.

Encoding (run once in the console)

apiKey + penId + timestamp + nonce
_safeEncode() — Unicode-safe btoa()
reverse string
obfuscated token ← store this

Decoding (runs at runtime in the pen)

obfuscated token
reverse string
_safeDecode() — Unicode-safe atob()
split → pop nonce, timestamp, penId
compare penId == getPenId()
match → return apiKey | no match → null

API Reference

All methods are static — no instantiation needed.

SaltyKeys.configure(options)

Call before any other method to override defaults.

Option Type Default Description
urlPattern RegExp CodePen regex Pattern to extract the page ID. Must include one capture group.
cacheEnabled boolean true Cache the extracted ID after the first call.
environment string 'codepen' Controls which warnings show when ID can't be resolved. Set to 'custom' to suppress CodePen-specific messaging.
// Adapt to a different environment
SaltyKeys.configure({
    urlPattern: /mysite\.com\/demos\/([^?#]+)/,
    environment: 'custom',
});
SaltyKeys.getPenId() → string | null

Extracts the page/pen ID from the current URL or canonical link.

const id = SaltyKeys.getPenId();
// → "abcXYZ" on https://codepen.io/username/pen/abcXYZ
SaltyKeys.generateSaltedKey(apiKey) → string | null

Encodes a real API key as a context-bound token. Run once from the console on the saved pen — do not put the raw key alongside this call in source.

// Run in the CodePen console, NOT in the pen source:
const token = SaltyKeys.generateSaltedKey('sk-my-real-api-key-abc123');
console.log(token);
// → long encoded string — copy this into your pen
SaltyKeys.getApiKey(saltedKey) → string | null

Decodes and returns the API key — but only if the current pen ID matches the one embedded in the token. Returns null otherwise.

// In your pen's JS source (the token is safe to be public):
const SALTED = 'XVlBzgbaiCMRAjWwhTHctcuAx...';

const apiKey = SaltyKeys.getApiKey(SALTED);
if (apiKey) {
    fetch(`https://api.example.com/data?key=${apiKey}`);
} else {
    console.warn('Could not retrieve API key.');
}

Non-CodePen Environments

SaltyKeys.js ships configured for CodePen but can be adapted to any environment where the page has a unique, consistent identifier. Override via configure() or replace getPenId() entirely:

Custom URL pattern

SaltyKeys.configure({
    urlPattern: /demos\/([^?#]+)/,
    environment: 'custom',
});

Function override

SaltyKeys.getPenId = function() {
    return document
        .querySelector('meta[name="demo-id"]')
        ?.content ?? null;
};

Security Model

Be honest about what this tool does and doesn't do.

Not suitable for production. A determined attacker with basic JavaScript skills can reverse the encoding. Use a server-side API proxy for anything sensitive.

Property Detail
Protection level Low — obfuscation only, not encryption
Attacker model Casual viewers who read source; not determined attackers
Reversibility Reversible with basic JavaScript knowledge
Server-side protection None — the key reaches the browser in decoded form when getApiKey() runs
Production suitability Not suitable. Use a server-side API proxy

The correct architecture for client-side apps that need real key protection:

Browser
Your API Proxy (server — stores the real key)
Third-party API

Troubleshooting

Symptom Likely cause Fix
Unable to extract Pen ID Pen not saved, or not on CodePen Save the pen first; or configure urlPattern for your environment
getApiKey() returns null Token generated on a different pen Regenerate the salted key on the correct pen
Key with : characters not recovered Token generated before v1.2.0 Regenerate with the current version
Warning banner in embed Pen viewed via embed iframe on a non-CodePen page Set environment: 'custom' if you control the embed page
atob is not defined Non-browser environment (e.g., Node.js) Library is browser-only by design; use test shims in the test suite

Browser Compatibility

Browser Minimum
Chrome 92+
Firefox 90+
Safari 15+
Edge 92+
Internet Explorer Not supported

Requires ES2022+ for private class fields (#), plus btoa/atob and standard DOM APIs.

Tests

30 test cases using Node.js's built-in node:test runner — no extra dependencies. Covers valid and invalid key generation, colon-containing keys, ID mismatch rejection, Unicode keys, caching behavior, and custom environment configuration.

# Run the full suite
npm test

# Or directly
node --test test/SaltyKeys.test.js

Get Started

A single file, no build step, 30 tests. Read the docs or browse the source on GitHub.