Bola Idor in Adonisjs with Jwt Tokens
Bola Idor in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) occurs when an API exposes object identifiers (IDs) and allows a subject to act on resources they do not own. In AdonisJS, using JWT tokens for authentication can inadvertently enable BOLA when authorization checks are incomplete or inconsistently applied. JWT tokens typically carry a subject (sub) claim identifying the user, but if route handlers or implicit model bindings do not enforce ownership, an attacker can manipulate IDs to access or modify other users' resources.
Consider an endpoint /api/users/:id/profile that uses JWT-based authentication. The token contains the user identifier (e.g., sub: "usr_abc123"). If the handler resolves the profile by ID without verifying that the profile belongs to the subject in the token, an attacker can change :id to another user’s identifier and access sensitive data. This is a classic BOLA scenario. AdonisJS does not automatically enforce ownership; it provides request handling and model binding, but developers must explicitly couple the authenticated subject from the JWT with the resource being accessed.
Common patterns that lead to BOLA with JWT in AdonisJS include:
- Using implicit model binding (e.g., via route model binding or implicit params) without scoping the query to the authenticated user.
- Relying on URL IDs alone to fetch records, without cross-referencing the user ID from the JWT payload.
- Assuming that JWT-based authentication alone is sufficient for authorization, rather than implementing per-request ownership checks.
Real-world attack chains often combine BOLA with other issues such as IDOR when predictable numeric IDs are used. For example, an attacker can iterate through user IDs (e.g., usr_abc124, usr_abc125) and access profiles, emails, or settings they should not see. Because JWT tokens can contain user roles, an attacker may also test for privilege escalation if role claims are not validated server-side on each request.
OWASP API Top 10 2023 A1: Broken Object Level Authorization is the relevant category. BOLA is not specific to any framework; however, in AdonisJS with JWT tokens, the framework’s flexible binding and middleware system means developers must intentionally scope data access. Without explicit checks, the API surface remains vulnerable even when JWT authentication is correctly implemented.
Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes
Remediation centers on ensuring every data access is scoped to the authenticated subject from the JWT token. Always resolve the subject from the token, then enforce ownership at the query or service layer. Below are concrete, realistic code examples for AdonisJS that demonstrate secure patterns.
1. Decode the JWT and attach user identity to the request
Use an authentication middleware to verify the token and attach the user payload to the request. This keeps the subject available downstream without trusting URL parameters.
// start/hooks/auth.js
const jwt = require('@adonisjs/fold').requireLater('Adonis/Addons/JWT');
exports.authHook = async (ctx, next) => {
const authHeader = ctx.request.header('authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
ctx.response.unauthorized('Missing bearer token');
return;
}
const token = authHeader.split(' ')[1];
try {
const payload = await jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });
ctx.authUser = { id: payload.sub, role: payload.role };
await next();
} catch (error) {
ctx.response.unauthorized('Invalid token');
}
};
2. Scoped profile retrieval with explicit ownership check
Avoid implicit model binding for sensitive resources. Instead, fetch by ID and assert ownership using the subject from the JWT.
// controllers/ProfileController.js
const Profile = use('App/Models/Profile');
class ProfileController {
async show({ request, authUser }) {
const requestedId = request.param('id');
// Enforce ownership: ensure the requested profile belongs to the authenticated user
const profile = await Profile.query()
.where('user_id', authUser.id)
.with('user')
.findOrFail(requestedId);
return profile;
}
}
3. Secure update with ownership guard
When updating, recompute the subject and scope the update to that subject. Do not rely on request body IDs.
// controllers/ProfileController.js
class ProfileController {
async update({ request, authUser }) {
const requestedId = request.param('id');
const data = request.only(['bio', 'avatarUrl']);
const profile = await Profile.query()
.where('user_id', authUser.id)
.where('id', requestedId)
.firstOrFail();
profile.merge(data);
await profile.save();
return profile;
}
}
4. Middleware-based scoping for routes
Use route-level middleware to enforce ownership for a group of routes, reducing repetitive checks.
// start/hooks/ownership.js
const Profile = use('App/Models/Profile');
exports.checkProfileOwnership = async (ctx, next) => {
const profileId = ctx.params.id;
const authUser = ctx.authUser;
const exists = await Profile.query()
.where('user_id', authUser.id)
.where('id', profileId)
.limit(1)
.fetch();
if (!exists.rows.length) {
ctx.response.status(403).send({ error: 'Forbidden: you do not own this resource' });
return;
}
await next();
};
5. Example route definitions
Wire the hooks and controllers so that JWT-subject is always used for scoping.
// start/routes.js
Route.get('/profiles/:id', 'ProfileController.show')
.middleware(['authHook', 'checkProfileOwnership']);
Route.put('/profiles/:id', 'ProfileController.update')
.middleware(['authHook', 'checkProfileOwnership']);
Key principles
- Never trust URL IDs for authorization; always cross-reference the JWT subject (sub).
- Use explicit query scopes (where) rather than relying on model binding alone.
- Keep sensitive operations close to the authentication middleware so the subject is available.
- Validate roles and scopes within the token if your authorization model requires them, but do not rely on roles alone for object-level checks.
By consistently scoping queries to the authenticated subject and validating ownership on every request, BOLA risks with JWT tokens in AdonisJS are effectively mitigated.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |