How to Handle API Keys and Secrets
This guide shows how to securely manage sensitive data like API keys, passwords, and tokens in your Test Packages.
The Problem
You need to test with real API keys but can't commit them to your repository.
The Solution
QIT's secret management system that:
- Validates secrets exist before running
- Injects them as environment variables
- Automatically redacts them from logs
Step-by-Step Guide
1. Declare Required Secrets
In your package's qit-test.json:
{
"package": "my-plugin/payment-tests",
"requires": {
"secrets": [
"STRIPE_TEST_KEY",
"STRIPE_TEST_SECRET",
"WEBHOOK_SIGNING_SECRET"
]
}
}
2. Use Secrets in Tests
Access secrets via environment variables:
test('process payment with Stripe', async ({ page }) => {
// Secrets are available as env vars
const stripeKey = process.env.STRIPE_TEST_KEY;
const stripeSecret = process.env.STRIPE_TEST_SECRET;
// Use in your test
await page.evaluate((key) => {
window.Stripe = Stripe(key);
}, stripeKey);
// Make API calls
const response = await fetch('https://api.stripe.com/v1/charges', {
headers: {
'Authorization': `Bearer ${stripeSecret}`
}
});
});
3. Provide Secrets When Running
Method 1: Environment Variables
export STRIPE_TEST_KEY="pk_test_..."
export STRIPE_TEST_SECRET="sk_test_..."
export WEBHOOK_SIGNING_SECRET="whsec_..."
qit run:e2e your-extension-slug --test-package=./payment-tests
Method 2: .env File
Create .env file:
STRIPE_TEST_KEY=pk_test_...
STRIPE_TEST_SECRET=sk_test_...
WEBHOOK_SIGNING_SECRET=whsec_...
Load and run:
source .env && qit run:e2e your-extension-slug --test-package=./payment-tests
Method 3: Inline
STRIPE_TEST_KEY="pk_test_..." \
STRIPE_TEST_SECRET="sk_test_..." \
qit run:e2e your-extension-slug --test-package=./payment-tests
Secret Validation
QIT validates before running:
$ qit run:e2e your-extension-slug --test-package=./payment-tests
Error: Missing required secrets for package 'my-plugin/payment-tests':
- STRIPE_TEST_KEY (not set)
- STRIPE_TEST_SECRET (not set)
Please set these environment variables and try again.
Automatic Redaction
Secrets are hidden in logs:
Running test: process payment
API Key: pk_test_[REDACTED]
Making request to Stripe...
Response: { id: "ch_[REDACTED]", amount: 1000 }
✓ Test passed
CI/CD Integration
GitHub Actions
name: Test with Secrets
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests with secrets
env:
STRIPE_TEST_KEY: ${{ secrets.STRIPE_TEST_KEY }}
STRIPE_TEST_SECRET: ${{ secrets.STRIPE_TEST_SECRET }}
run: |
qit run:e2e your-extension-slug --test-package=./payment-tests
GitLab CI
test:
script:
- qit run:e2e your-extension-slug --test-package=./payment-tests
variables:
STRIPE_TEST_KEY: $STRIPE_TEST_KEY
STRIPE_TEST_SECRET: $STRIPE_TEST_SECRET
Best Practices
1. Use Test Keys Only
Never use production keys:
// ✅ Good: Test key
const key = process.env.STRIPE_TEST_KEY; // pk_test_...
// ❌ Bad: Production key
const key = process.env.STRIPE_LIVE_KEY; // pk_live_...
2. Document Required Secrets
In your package README:
## Required Secrets
This package requires:
- `STRIPE_TEST_KEY`: Stripe publishable test key
- `STRIPE_TEST_SECRET`: Stripe secret test key
- `WEBHOOK_SIGNING_SECRET`: Stripe webhook signing secret
Get these from: https://dashboard.stripe.com/test/apikeys
3. Provide Defaults for Non-Sensitive Config
// Secrets for sensitive data
const apiKey = process.env.STRIPE_TEST_KEY; // Required
// Regular config with defaults
const apiUrl = process.env.STRIPE_API_URL || 'https://api.stripe.com';
const timeout = process.env.API_TIMEOUT || '30000';
4. Validate Secret Format
test.beforeAll(() => {
const key = process.env.STRIPE_TEST_KEY;
if (!key?.startsWith('pk_test_')) {
throw new Error('STRIPE_TEST_KEY must be a test publishable key');
}
});
Common Patterns
Multiple Environment Secrets
For different environments:
{
"requires": {
"secrets": [
"STAGING_API_KEY",
"STAGING_API_SECRET",
"PRODUCTION_API_KEY",
"PRODUCTION_API_SECRET"
]
}
}
Optional Secrets
Handle optional features:
test('premium feature', async ({ page }) => {
const premiumKey = process.env.PREMIUM_API_KEY;
if (!premiumKey) {
test.skip();
return;
}
// Test premium features
});
Secret Rotation
Support multiple keys:
const keys = [
process.env.API_KEY_PRIMARY,
process.env.API_KEY_SECONDARY
].filter(Boolean);
const key = keys[0]; // Use primary, fallback to secondary
Troubleshooting
Secret Not Available in Test
Check the secret name matches exactly:
# Declared in manifest
"secrets": ["STRIPE_KEY"]
# Must set exactly
export STRIPE_KEY="..." # ✅
export stripe_key="..." # ❌ Wrong case
Secrets Visible in Logs
Ensure you're not manually logging:
// ❌ Bad: Manual logging
console.log(`Key: ${process.env.STRIPE_KEY}`);
// ✅ Good: Let QIT redact
// Secrets are automatically hidden
CI Secrets Not Working
Verify CI environment:
- name: Debug (remove after testing)
run: |
echo "Secrets configured:"
[ -n "$STRIPE_TEST_KEY" ] && echo "✓ STRIPE_TEST_KEY" || echo "✗ STRIPE_TEST_KEY"