HIGH privilege escalationadonisjsjwt tokens

Privilege Escalation in Adonisjs with Jwt Tokens

Privilege Escalation in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Privilege escalation in AdonisJS when using JWT tokens typically arises from how roles/permissions are encoded in the token and how the application authorizes requests. JWTs are often used for stateless authentication; if the token carries user metadata such as a role or a list of permissions, an attacker who can tamper with the token or obtain a token for a lower-privilege account may attempt to elevate their privileges.

In AdonisJS, the framework does not inherently validate claims beyond signature verification unless you explicitly enforce role/permission checks on each route or in route-level middleware. If authorization relies solely on the token payload without re-verifying scope or role against a server-side store or policy, an attacker who can modify the token (e.g., changing role: "user" to role: "admin") can gain elevated access. This is commonly tied to BOLA/IDOR when the token identifies a resource by ID without ensuring the requesting user is allowed to act on that resource, and to BFLA/Privilege Escalation when endpoints expose logic that can be invoked with an elevated token.

A concrete scenario: an API endpoint POST /roles/assign that changes another user’s role may be protected only by checking whether the incoming JWT has a role claim of admin. If the client can forge a token with that claim (e.g., by manipulating a locally signed token with weak secrets or by reusing an old token that was issued with broader scopes), the authorization check passes incorrectly. Even when using asymmetric keys (RS256), if the public key is misconfigured or the algorithm is not strictly enforced, an attacker might supply an unsigned tokens or use a public key as secret, leading to privilege escalation.

JWT-specific risks also intersect with token scope and claims. For example, a token issued with scope: read might be altered to scope: write admin if the application does not validate scope per endpoint. AdonisJS route guards that only inspect the presence of a role claim and do not validate scope or context enable BFLA—where a lower-privilege function is invoked with elevated authority. Without per-action checks, an attacker can invoke admin-only actions by replaying or modifying requests, escalating their privileges within the application.

To detect this during scanning, middleBrick runs parallel checks including Authentication, BOLA/IDOR, BFLA/Privilege Escalation, and Unsafe Consumption. It analyzes OpenAPI specs and runtime behavior to identify endpoints that accept JWT tokens but lack strict role/scope validation, and it probes for token tampering and missing algorithm constraints. Findings include severity-ranked guidance on how to tighten authorization and token validation to prevent escalation paths.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation centers on strict validation of token claims, algorithm enforcement, and per-endpoint authorization checks. Never trust the payload alone; re-verify critical claims against policy, enforce expected algorithms, and scope checks to the action being performed.

1. Enforce token algorithm and validate issuer/audience

When verifying JWTs in AdonisJS, explicitly set the allowed algorithm and validate standard claims. Using jose (a modern choice), you can enforce RS256 and check iss and aud:

import { jwtVerify } from 'jose';

const publicKey = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END PUBLIC KEY-----';

async function verifyToken(token: string) {
  const { payload } = await jwtVerify(token, publicKey, {
    algorithms: ['RS256'],
    issuer: 'https://your-auth-server.example.com',
    audience: 'https://api.example.com',
  });
  return payload;
}

This prevents algorithm confusion attacks (e.g., expecting RS256 but accepting none) and ensures tokens are issued by a trusted source for the intended API audience.

2. Map roles/scopes to permissions and enforce per-route

Decode the token, map roles or scopes to granular permissions, and enforce checks in route handlers or middleware. Avoid relying on a simple role claim; use an allowlist of permissions for each route:

import { verify } from 'jsonwebtoken';

const allowedScopes = new Set(['role:admin', 'role:super-admin']);

function requireAdmin(token: string) {
  const decoded = verify(token, process.env.JWT_SECRET as string);
  if (!decoded.scopes || !decoded.scopes.some(s => allowedScopes.has(s))) {
    throw new Error('Insufficient scope');
  }
  return decoded;
}

// In a route handler
Route.post('/roles/assign', async ({ request, response }) => {
  const token = request.header('authorization')?.replace('Bearer ', '');
  if (!token) return response.unauthorized();
  const payload = requireAdmin(token);
  // proceed with admin action
});

3. Use middleware to centralize authorization and scope checks

Define a reusable middleware that validates the token and attaches user capabilities to the context. This keeps route handlers clean and ensures consistent enforcement:

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { verify } from 'jsonwebtoken';

export default class AuthMiddleware {
  public async handle(ctx: HttpContextContract, next: () => Promise) {
    const authHeader = ctx.request.header('authorization');
    if (!authHeader) {
      return ctx.response.unauthorized('Missing token');
    }
    const token = authHeader.replace('Bearer ', '');
    let decoded: any;
    try {
      decoded = verify(token, process.env.JWT_SECRET as string);
    } catch {
      return ctx.response.unauthorized('Invalid token');
    }
    // Attach capabilities
    ctx.auth = { user: decoded.sub, scopes: decoded.scopes || [] };
    await next();
  }
}

4. Avoid storing sensitive escalation actions behind simple role claims

For endpoints that change roles or scopes, require additional confirmation (e.g., MFA or admin approval workflows) and validate that the actor’s effective scopes permit the change. Do not allow token claims alone to determine whether a mutation is permitted; couple token validation with server-side policy checks.

middleBrick’s scans include checks for missing algorithm constraints and improper token validation. By following the above patterns—explicit algorithm enforcement, scope-based allowlists, and centralized middleware—you reduce the risk of privilege escalation when using JWT tokens in AdonisJS.

Frequently Asked Questions

What should I do if my JWT tokens include roles but I need per-action permissions?
Map roles to granular permissions in your application logic and enforce those permissions per endpoint rather than relying on a role claim alone. Use middleware to validate scopes against an allowlist for each route.
How can I prevent algorithm confusion attacks with JWTs in AdonisJS?
Always enforce the expected algorithm (e.g., RS256) during verification, validate issuer and audience claims, and avoid accepting unsigned tokens. Use a well-maintained library like jose that defaults to rejecting insecure algorithms.