API Keys
API keys in Novee use asymmetric cryptography (public/private key pairs) to sign JSON Web Tokens (JWTs) for secure API authentication. This approach provides stronger security than traditional API keys or secrets.
Why Use JWT Signing?
Traditional API authentication methods have limitations:
| Method | Drawback |
|---|---|
| Static API Keys | Can be intercepted and reused indefinitely |
| Shared Secrets | Must be stored on both client and server |
| Basic Auth | Credentials sent with every request |
JWT signing with asymmetric keys solves these issues:
- Your private key never leaves your system — Only the public key is shared with Novee
- Short-lived tokens — JWTs expire quickly, limiting the window for misuse
- Non-repudiation — Requests can be cryptographically verified as coming from you
- No shared secrets — Even if our systems were compromised, your private key remains safe
Generate an EC Key Pair
We use ES256 (ECDSA with P-256 curve) for JWT signing. Generate your key pair using OpenSSL:
# Generate private key
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
# Generate public key from private key
openssl ec -in private.pem -pubout -out public.pemKeep your private.pem secure! Never share it, commit it to version control, or upload it anywhere. Only the public.pem should be uploaded when creating an API key in the dashboard.
Verify Your Keys
You can verify your keys were generated correctly:
# View private key details
openssl ec -in private.pem -text -noout
# View public key details
openssl ec -in public.pem -pubin -text -nooutCreate an API Key
- Navigate to Dashboard → API Keys
- Click Create API Key
- Fill in the details:
- Name: A descriptive name for the key
- Description: (Optional) What this key is used for
- Public Key: Paste the contents of your
public.pemfile - Key Type: Select the appropriate type
- Environment: Choose
testfor development orlivefor production
- Click Create
Your API key ID will be displayed. Save this ID — you’ll need it to identify your key when making requests.
Sign a JWT
Use your private key to sign JWTs for API authentication. Here are examples in common languages:
Node.js
import jwt from 'jsonwebtoken';
import fs from 'fs';
const privateKey = fs.readFileSync('private.pem', 'utf8');
const token = jwt.sign(
{
sub: 'your-api-key-id',
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 300, // 5 minutes
},
privateKey,
{ algorithm: 'ES256' }
);
// Use the token in your API requests
fetch('https://api.novee.io/v1/resource', {
headers: {
'Authorization': `Bearer ${token}`
}
});Python
import jwt
import time
with open('private.pem', 'r') as f:
private_key = f.read()
token = jwt.encode(
{
'sub': 'your-api-key-id',
'iat': int(time.time()),
'exp': int(time.time()) + 300, # 5 minutes
},
private_key,
algorithm='ES256'
)
# Use the token in your API requests
import requests
response = requests.get(
'https://api.novee.io/v1/resource',
headers={'Authorization': f'Bearer {token}'}
)Go
package main
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
func signJWT(apiKeyID string) (string, error) {
keyData, _ := os.ReadFile("private.pem")
block, _ := pem.Decode(keyData)
privateKey, _ := x509.ParseECPrivateKey(block.Bytes)
now := time.Now()
token := jwt.NewWithClaims(jwt.SigningMethodES256, jwt.MapClaims{
"sub": apiKeyID,
"iat": now.Unix(),
"exp": now.Add(5 * time.Minute).Unix(),
})
return token.SignedString(privateKey)
}JWT Claims
When signing your JWT, include these claims:
| Claim | Description | Required |
|---|---|---|
sub | Your API key ID | Yes |
iat | Issued at timestamp (Unix seconds) | Yes |
exp | Expiration timestamp (Unix seconds) | Yes |
jti | Unique token ID (for replay protection) | Recommended |
Keep token lifetimes short (5-15 minutes). Generate a fresh token for each request or batch of requests.
Best Practices
Key Security
- Store private keys in secure key management systems (AWS KMS, HashiCorp Vault, etc.)
- Use environment variables or secrets managers, never hardcode keys
- Rotate keys periodically and immediately if compromised
- Use different keys for test and production environments
Token Handling
- Generate tokens just before use, not in advance
- Never log or expose tokens in error messages
- Implement token refresh logic for long-running processes
- Include a unique
jticlaim for critical operations
Environment Separation
- Test keys: Use for development and staging environments
- Live keys: Use only in production with restricted access
- Never use test keys in production or vice versa
Troubleshooting
”Invalid signature” error
- Ensure you’re using the correct private key matching the public key uploaded
- Verify you’re using the
ES256algorithm - Check that the key is in valid PEM format
”Token expired” error
- Check that your system clock is synchronized (use NTP)
- Ensure the
expclaim is set correctly in Unix seconds - Generate a fresh token and retry
”Invalid key format” error
- Ensure the public key includes the full PEM header and footer:
-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY----- - Check for any extra whitespace or newlines
- Verify the key was generated with the P-256 curve
Next Steps
- Webhooks — Set up webhook endpoints for real-time events