Api Key Exposure in Adonisjs with Jwt Tokens
Api Key Exposure in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
When an AdonisJS application uses JSON Web Tokens (JWT) for authentication but also relies on API keys for service-to-service or third-party integrations, improper handling can lead to Api Key Exposure. This occurs when API keys are embedded in JWT payloads or stored alongside token configuration in a way that they can be extracted by an attacker who gains access to token data. Because JWTs are often transmitted in HTTP headers (e.g., Authorization: Bearer <token>), if these tokens are logged, stored in browser local storage, or transmitted over non-TLS connections, any embedded or related API keys can be leaked.
In AdonisJS, developers sometimes place API keys inside the JWT payload to propagate credentials to downstream services. If the token is not properly protected (e.g., missing token encryption, using weak signing algorithms, or failing to validate token audiences), an attacker who steals the JWT can extract the API key and misuse it. Additionally, if AdonisJS middleware or route handlers log the full JWT or include it in error messages, the API key can be inadvertently exposed through logs or client-side JavaScript. This is particularly risky when tokens have long lifetimes or when refresh tokens are stored insecurely, allowing an attacker to repeatedly use a captured token to harvest embedded keys.
The risk is compounded when JWT secrets or public keys are not properly rotated or when environment variables holding signing keys are exposed through misconfigured .env files or insecure CI/CD pipelines. AdonisJS applications that generate JWTs without setting appropriate token expiration (exp), not validating issuers (iss), or failing to verify scopes can inadvertently expose API keys that were included as custom claims. Attackers can then use these keys to access external services, escalate privileges, or exfiltrate sensitive data, leading to account compromise or data breaches.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
To mitigate Api Key Exposure when using JWT tokens in AdonisJS, ensure that API keys are never embedded directly in JWT payloads. Instead, store API keys securely on the server side and reference them via opaque identifiers within the token. Use strong signing algorithms like RS256 and enforce short token lifetimes. Below are concrete code examples demonstrating secure practices.
Secure JWT generation without embedding API keys
const jwt = use('jwt')
const crypto = require('crypto')
class AuthController {
async login ({ request, auth }) {
const user = await User.findBy('email', request.input('email'))
if (!user || !(await verifyPassword(request.input('password'), user.password))) {
throw new Error('Invalid credentials')
}
// Do NOT include API keys in the payload
const token = jwt.sign({
sub: user.id,
email: user.email,
iat: Date.now(),
exp: Math.floor(Date.now() / 1000) + (60 * 15), // 15 minutes
iss: 'adonisjs-app',
aud: 'api.example.com'
}, Env.get('JWT_SECRET'), 'RS256')
return { token }
}
}
module.exports = AuthController
Validating JWTs and securely using API keys server-side
const jwt = use('jwt')
const axios = require('axios')
class ExternalServiceController {
async callExternal ({ request }) {
const authHeader = request.header('authorization')
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new Error('Unauthorized')
}
const token = authHeader.split(' ')[1]
let payload
try {
payload = jwt.verify(token, Env.get('JWT_PUBLIC_KEY'), { algorithms: ['RS256'] })
} catch (error) {
throw new Error('Invalid token')
}
// Retrieve API key securely from environment or secure vault using payload.sub as a lookup key
const apiKey = process.env[`API_KEY_${payload.sub.toUpperCase()}`]
if (!apiKey) {
throw new Error('Service credentials not found')
}
const response = await axios.get('https://external-api.example.com/data', {
headers: { Authorization: `Bearer ${apiKey}` }
})
return response.data
}
}
module.exports = ExternalServiceController
Environment-based API key management
// .env.example
JWT_SECRET=your_jwt_signing_secret
API_KEY_USER1=sk_live_abc123
API_KEY_USER2=sk_live_xyz789
// config/jwt.js
module.exports = {
secret: Env.getOrFail('JWT_SECRET'),
ttl: '15m'
}
// config/api-keys.js (never commit real keys)
const Env = use('Env')
const apiKeys = {
[Env.get('DB_USER_ID')]: Env.getOrFail('API_KEY_DB'),
[Env.get('PAYMENT_SERVICE_ID')]: Env.getOrFail('API_KEY_PAYMENT')
}
module.exports = apiKeys
Middleware to prevent token leakage in logs
const Logger = use('Logger')
class SanitizeMiddleware {
async handle ({ request, response }, next) {
request.cleanHeaders = () => {
const headers = request.headers()
delete headers.authorization
return headers
}
await next()
}
}
module.exports = SanitizeMiddleware