Insecure Design in Adonisjs with Jwt Tokens
Insecure Design in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Insecure design in an AdonisJS application using JWT tokens often arises from a mismatch between the framework’s flexible configuration options and secure-by-default practices. When designing token-based authentication, developers must consider token issuance, storage, validation, and revocation as an integrated security strategy rather than treating JWT as a plug-and-play solution.
One common design flaw is issuing tokens with overly broad scopes or long expiration times, which increases the window for replay or token theft abuse. If access and refresh tokens are not differentiated, a stolen access token can expose sensitive endpoints until expiry. AdonisJS does not enforce short-lived tokens by default; without explicit configuration, applications may emit tokens that persist for hours or days.
Another design risk is how tokens are stored and transmitted. If the application relies on cookies without the HttpOnly, Secure, and SameSite=Strict attributes, tokens become accessible to JavaScript, enabling cross-site scripting (XSS) exfiltration. Alternatively, storing tokens in local storage and manually attaching them via headers simplifies client-side logic but increases exposure to XSS. The insecure design choice is often prioritizing developer convenience over defense-in-depth, especially when CORS is misconfigured to allow broad origins, enabling malicious sites to make authenticated requests on behalf of users.
Validation and signature verification design also matter. If the application fails to explicitly specify the expected algorithm (e.g., not enforcing HS256 or RS256) and does not validate the token issuer (iss) and audience (aud), it may accept tokens signed with a weak or unexpected key. AdonisJS’s JWT utilities allow customization of these parameters; omitting strict checks can lead to algorithm confusion attacks or acceptance of unsigned tokens in development modes mistakenly left enabled in production.
Refresh token handling is another area where insecure design manifests. Without a robust rotation and revocation mechanism, compromised refresh tokens can lead to long-term access. AdonisJS applications that store refresh tokens in a database must ensure that each use is invalidated and that reuse triggers account review. Designing without rate limiting on token endpoints also enables credential stuffing or brute-force attempts against the authentication route.
Finally, insufficient audit logging and anomaly detection in the authorization layer means insecure designs may go unnoticed. Even if tokens are validated correctly, a lack of monitoring for abnormal token usage patterns (e.g., multiple geographic logins within minutes) can delay breach detection. MiddleBrick’s LLM/AI Security checks can specifically probe for system prompt leakage and unauthorized tool usage patterns; when integrated via the MCP Server, teams can detect design weaknesses earlier in development cycles.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
To remediate insecure design with JWT tokens in AdonisJS, apply strict configuration and validation controls across issuance, transmission, and verification.
1. Configure token lifetimes and scopes explicitly
Set short-lived access tokens and use refresh tokens with strict revocation. Use environment variables to control expiry rather than hardcoding values.
// config/jwt.ts
import { type JwtConfig } from '@ioc/Adonis/Addons/Jwt'
const jwtConfig: JwtConfig = {
secret: process.env.JWT_SECRET!,
token: {
access: {
expiresIn: '15m',
},
refresh: {
expiresIn: '7d',
},
},
audience: 'https://api.yourapp.com',
issuer: 'yourapp-auth-service',
algorithms: ['HS256'],
}
export default jwtConfig
2. Enforce secure cookie attributes when using cookie-based storage
When storing tokens in cookies, ensure attributes mitigate XSS and CSRF risks. Combine with CORS restrictions.
// start/hooks.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export const handleAuthenticationCookie = (ctx: HttpContextContract, token: string) => {
ctx.response.cookie('access_token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
path: '/',
maxAge: 900000, // 15 minutes
})
}
3. Validate issuer, audience, and algorithm on each request
Do not rely on default validation. Explicitly verify claims to prevent token confusion.
// middleware/ensure_auth.ts
import { Exception } from '@poppinss/utils'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { createJwtVerify } from 'jose/jwt/verify'
export default async function ensureAuth({ request, response }: HttpContextContract) {
const token = request.header('authorization')?.replace('Bearer ', '')
if (!token) {
throw new Exception('Unauthorized', 401)
}
try {
const { payload } = await createJwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET!), {
issuer: 'yourapp-auth-service',
audience: 'https://api.yourapp.com',
algorithms: ['HS256'],
})
request.authUser = payload
} catch (error) {
throw new Exception('Invalid token', 401)
}
}
4. Implement refresh token rotation and reuse detection
Store refresh tokens with a one-time-use policy and bind them to device fingerprints where feasible.
// controllers/AuthController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Token from 'App/Models/Token'
export class AuthController {
public async refresh({ request, response, auth }: HttpContextContract) {
const { refreshToken } = request.body()
const stored = await Token.findBy('refresh_token', refreshToken)
if (!stored || stored.revoked || stored.used) {
throw new Error('Invalid refresh token')
}
// Invalidate used token and issue new pair
stored.used = true
await stored.save()
const accessToken = jwt.sign({ sub: stored.userId }, process.env.JWT_SECRET!, { expiresIn: '15m' })
const newRefreshToken = jwt.sign({ sub: stored.userId }, process.env.JWT_SECRET!, { expiresIn: '7d' })
response.cookie('access_token', accessToken, { httpOnly: true, secure: true, sameSite: 'strict' })
response.cookie('refresh_token', newRefreshToken, { httpOnly: true, secure: true, sameSite: 'strict' })
return { accessToken, refreshToken: newRefreshToken }
}
}
5. Apply rate limiting and anomaly detection on token endpoints
Use AdonisJS middleware or an external layer to throttle login and refresh requests, and log suspicious patterns for review.