Excessive Data Exposure in Adonisjs with Jwt Tokens
Excessive Data Exposure in Adonisjs with Jwt Tokens
Excessive Data Exposure occurs when an API returns more information than necessary for a given operation, and the combination of AdonisJS and JWT tokens can inadvertently amplify this risk. AdonisJS, a Node.js framework, often uses JWT tokens for stateless authentication, but developers may serialize sensitive user data into the token payload or expose additional user fields in API responses without considering access context.
When JWT tokens are issued, they typically contain a payload with claims such as user identifier, roles, and permissions. If the token includes fields like email, phone number, internal IDs, or role hierarchies, and the API endpoint returns the full user object or token payload directly to the client, this constitutes excessive data exposure. For example, an endpoint like /api/users/me might return the complete user record, including password hashes, reset tokens, or internal metadata, whereas the client may only need the user ID and username.
In AdonisJS, this often happens when using the User model directly in a controller response without filtering fields. Consider a controller method that authenticates a user and returns the authenticated user’s data:
import User from '#models/user'
import { createJwt } from '#utils/jwt'
export default class AuthController {
public async login({ request, auth, response }) {
const { email, password } = request.all()
const user = await auth.authenticate()
const token = createJwt({ id: user.id, email: user.email, role: user.role })
return response.send({ user, token })
}
}
Here, the user object returned may contain sensitive fields such as password, email_verified_at, or other columns not intended for client consumption. Even though the JWT token itself might only carry minimal claims, the API response exposes the full model instance, creating a data overexposure path. Additionally, if the JWT token is used to gate access but the response still includes sensitive model attributes, an attacker who obtains the token can infer or access further data through other endpoints that reflect the same over-permissive patterns.
Another scenario involves introspection endpoints or token validation routes that return decoded JWT payloads without proper filtering. If an endpoint like /api/auth/introspect returns the entire decoded token, including all custom claims, this can expose internal business logic or sensitive metadata. Attackers can use this information to craft privilege escalation attempts or social engineering strategies.
AdonisJS’s reliance on Lucid models makes it easy to overlook which fields are serialized. By default, model serialization may include all database columns unless explicitly defined via serialize methods or hidden attributes. Combined with JWT tokens that may carry broad claims, this can lead to significant data exposure when responses are not carefully constrained.
Furthermore, if the JWT token is used for authorization decisions without validating the scope of data returned, the API might leak administrative flags, tenant identifiers, or operational details. For instance, a token with a role: admin claim might lead the client to request administrative endpoints, and if those endpoints return verbose error messages or full dataset previews, sensitive information can be disclosed unintentionally.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on ensuring that API responses do not include more data than necessary and that JWT token payloads remain minimal and non-sensitive. In AdonisJS, you can control serialization and response shape explicitly.
First, define a serialize method on your User model to specify which fields are exposed:
import { DateTime } from 'luxon'
import { BaseModel, beforeSave } from '@ioc:Adonis/Lucid/Orm'
import { column } from '@ioc:Adonis/Lucid/Columns'
export default class User extends BaseModel {
@column({ isPrimary: true })
public id: number
@column()
public username: string
@column()
public email: string
@column()
public role: string
@column.dateTime({ autoCreate: true })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
@column({ serialize: false })
public password: string
public serialize() {
return {
id: this.id,
username: this.username,
email: this.email,
role: this.role,
}
}
}
With this, even if the controller returns the full model, sensitive fields like password are omitted. Next, ensure your authentication controller returns only necessary data alongside the token:
import User from '#models/user'
import { createJwt } from '#utils/jwt'
export default class AuthController {
public async login({ request, auth, response }) {
await auth.authenticate()
const user = await User.findByOrFail('email', request.input('email'))
const token = createJwt({ id: user.id, email: user.email, role: user.role })
return response.send({
token,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
},
})
}
}
This pattern avoids returning the full model and constructs a safe object. Additionally, when validating or introspecting tokens, filter the decoded payload:
import { jwtVerify } from 'jose'
export async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET))
return {
id: payload.sub,
email: payload.email,
role: payload.role,
}
}
Ensure your JWT creation utility uses a minimal payload:
import { SignJWT } from 'jose'
export function createJwt(claims: { id: number; email: string; role: string }) {
return new SignJWT(claims)
.setProtectedHeader({ alg: 'HS256' })
.setIssuer('adonisjs-app')
.setAudience('api-client')
.setExpirationTime('2h')
.sign(new TextEncoder().encode(process.env.JWT_SECRET))
}
Finally, audit all endpoints that return user or token data and apply consistent serialization or projection to prevent accidental exposure of sensitive fields.
FAQ
- How can I detect if my AdonisJS API is exposing excessive data via JWT tokens?
Use middleBrick to scan your API endpoints. It checks whether responses include sensitive fields beyond what the JWT payload necessitates and flags endpoints that return full model instances or token introspection data without filtering. - Does middleBrick test JWT token content for sensitive data exposure?
Yes, middleBrick’s Data Exposure checks analyze API responses and, where applicable, correlate findings with JWT token claims to identify over-permissive data disclosure patterns.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |