Token Leakage in Adonisjs with Jwt Tokens
Token Leakage in Adonisjs with Jwt Tokens — how this combination creates or exposes the vulnerability
Token leakage in AdonisJS when using JWT tokens occurs when JSON Web Tokens are exposed beyond the intended execution context, such as through logs, error messages, browser storage, or network interception. AdonisJS applications commonly rely on the jwt provider to issue and verify tokens for authentication, but insecure handling patterns can inadvertently expose these tokens.
For example, logging the full token or user payload during request processing can expose tokens in centralized logging systems or console outputs that may be accessible to unauthorized parties. Consider this unsafe pattern:
import { BaseController } from '@ioc:Adonis/Core/Controller'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class AuthController extends BaseController {
public async login({ auth, request, logger }: HttpContextContract) {
const { email, password } = request.only(['email', 'password'])
const token = await auth.use('jwt').generate(email, { expiresIn: '7d' })
logger.info('User login successful', { email, token }) // Unsafe: token in logs
return { token }
}
}
In this snippet, the JWT token is included in structured logs. If log aggregation or debugging interfaces are improperly secured, the token can be read by individuals who should not have access. Similarly, exposing the token in HTTP response bodies without encryption or over non-TLS channels creates network exposure risks, enabling interception via man-in-the-middle attacks.
Another leakage vector involves error handling. AdonisJS may include token details in stack traces or validation error messages when development configurations are used in production or when error handling does not sanitize sensitive fields. For instance, an unhandled exception during token validation could reveal token structure or associated user identifiers in error reports:
try {
const payload = await Jwt.verify(token, encryptionKey)
} catch (error) {
logger.error('Token verification failed', { error, token })
}
Insecure storage in client-side contexts compounds the issue. If the frontend persists JWT tokens in local storage or session storage as part of an AdonisJS-integrated JavaScript application, these tokens become accessible via cross-site scripting (XSS) attacks. Even if the backend generates tokens securely, client-side storage decisions can nullify these protections.
SSRF-related exposures can also indirectly facilitate token leakage. If an AdonisJS application makes outbound HTTP requests using user-supplied URLs without proper validation, an attacker might coerce the application into interacting with internal metadata services. While this does not directly expose JWTs, it can reveal service-to-service communication patterns that, when combined with log or error data, assist in constructing further attacks involving token misuse.
Finally, weak token lifecycle practices, such as long expiration times without refresh token rotation or revocation mechanisms, increase the window of exposure. If a token is leaked, prolonged validity allows extended unauthorized access. Proper token invalidation and short-lived access tokens with secure refresh pathways reduce the impact of potential leaks.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
To mitigate token leakage in AdonisJS with JWT tokens, implement strict handling practices across logging, error management, transport, and storage. Begin by ensuring tokens are never logged or exposed in error outputs.
import { BaseController } from '@ioc:Adonis/Core/Controller'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class AuthController extends BaseController {
public async login({ auth, request, logger }: HttpContextContract) {
const { email, password } = request.only(['email', 'password'])
const token = await auth.use('jwt').generate(email, { expiresIn: '7d' })
logger.info('User login successful', { email }) // Safe: exclude token
return { token }
}
}
Use environment-based error handling to avoid leaking tokens in production. In start/handlers.ts, customize error reporting to strip sensitive fields:
import { ExceptionHandler } from '@ioc:Adonis/Core/ExceptionHandler'
export default class CustomExceptionHandler extends ExceptionHandler {
public async handle(error: any, ctx: HttpContextContract) {
if (error.message && error.name === 'TokenizationException') {
ctx.response.badRequest({ message: 'Invalid authentication token' })
} else {
// generic response in production
ctx.response.internalServerError({ message: 'Internal server error' })
}
if (app.env === 'dev') {
await this.report(error)
}
}
}
Enforce HTTPS for all API endpoints to protect tokens in transit. Configure AdonisJS to reject non-TLS requests in production by leveraging middleware that checks the x-forwarded-proto header or server configuration. In routes files, you can apply route-level security hints where supported by infrastructure, ensuring encrypted channels.
Store tokens securely on the client side by avoiding local storage. Use in-memory storage or secure, HTTP-only cookies for session management when integrating with frontend frameworks. If tokens must be stored client-side for API calls, prefer short lifetimes and implement silent refresh flows using secure refresh tokens stored in HttpOnly cookies.
// Example: Set token in memory (React/Vue/Angular context), not localStorage
let authToken: string | null = null
function setAuthToken(token: string) {
authToken = token
}
function clearAuthToken() {
authToken = null
}
Implement token revocation and short expiration windows to limit exposure. Use refresh token rotation and maintain a denylist for invalidated tokens. AdonisJS applications can integrate a lightweight cache to track revoked tokens efficiently:
import { DateTime } from 'luxon'
import { Cache } from '@ioc:Adonis/Core/Cache'
export async function revokeToken(token: string) {
const expiry = DateTime.now().plus({ hours: 1 }).toSeconds()
await Cache.put(`revoked_token:${token}`, true, expiry)
}
export async function isTokenRevoked(token: string) {
return await Cache.get(`revoked_token:${token}`, false)
}
Finally, conduct regular security scans using tools like the middleBrick CLI to identify accidental token exposure in API responses or configurations. The CLI tool allows you to scan from terminal with middlebrick scan <url>, integrating checks into development workflows without requiring agent setup.