HIGH privilege escalationadonisjsbasic auth

Privilege Escalation in Adonisjs with Basic Auth

Privilege Escalation in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability

In AdonisJS, privilege escalation can occur when Basic Authentication is used without enforcing strict role or scope checks after authentication. Basic Auth typically relies on a username and password transmitted via an Authorization header, and AdonisJS can validate credentials using built-in or custom authentication providers. However, if the application only verifies credentials and does not enforce authorization tied to user roles, an authenticated user may access endpoints intended for administrators or other privileged roles.

For example, consider an AdonisJS route guarded by a basic auth check but lacking role-based access control. A low-privilege user who successfully authenticates might still invoke admin-only actions if the route does not explicitly validate permissions. This becomes a BOLA/IDOR-like scenario when object-level ownership is not verified, or a BFLA/Privilege Escalation issue when horizontal access is not restricted. The risk is compounded if the application exposes administrative functionality through predictable URLs without scoping to the authenticated user's role.

AdonisJS provides authentication utilities such as auth middleware and guards, but developers must explicitly assign roles and check them in route handlers or controller methods. Without this, the combination of Basic Auth and missing authorization logic creates a pathway for privilege escalation, where a standard user can perform actions or access data reserved for higher-privilege accounts.

Basic Auth-Specific Remediation in Adonisjs — concrete code fixes

To mitigate privilege escalation in AdonisJS with Basic Auth, implement role-based access control (RBAC) after authentication and validate permissions on every request to sensitive endpoints. Below are concrete code examples demonstrating secure practices.

1. Define a basic auth guard with a custom user provider

Configure an auth guard that uses a database provider to load user details, including roles.

// start/auth.ts
import { defineConfig } from '@ioc:Adonis/Addons/Auth'

export default defineConfig({
  guards: {
    basic: {
      driver: 'basic',
      provider: {
        driver: 'lucid',
        model: () => import('#models/user'),
      },
    },
  },
})

2. User model with role attribute

Ensure your User model includes a role field that determines access levels.

// app/Models/User.ts
import { DateTime } from 'luxon'
import {
  BaseModel,
  column,
  beforeSave,
} from '@ioc:Adonis/Lucid/Orm'
import hash from '@ioc:Adonis/Addons/Hashing'

export default class User extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public username: string

  @column()
  public password: string

  @column()
  public role: 'user' | 'admin'

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @beforeSave()
  public static async hashPassword(user: User) {
    if (user.$dirty.password) {
      user.password = await hash.make(user.password)
    }
  }
}

3. Authenticate and authorize in a route or controller

Use the auth middleware to authenticate, then check the user's role before proceeding.

// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
import { schema } from '@ioc:Adonis/Core/Validator'

Route.get('/admin/settings', async ({ auth, response }) => {
  await auth.authenticate('basic', { guard: 'basic' })
  const user = auth.getUser()

  if (!user || user.role !== 'admin') {
    return response.unauthorized()
  }

  // Proceed with admin-only logic
  return { message: 'Admin settings' }
})

// Example with ownership check (BOLA mitigation)
Route.get('/users/:id/profile', async ({ params, auth, response }) => {
  await auth.authenticate('basic', { guard: 'basic' })
  const user = auth.getUser()

  if (!user) {
 return response.unauthorized()
  }

  // Ensure users can only access their own profile unless admin
  if (user.id !== Number(params.id) && user.role !== 'admin') {
    return response.forbidden()
  }

  // Fetch and return profile
  return { userId: params.id, role: user.role }
})

4. Centralize authorization with middleware

Create custom middleware to enforce role checks across routes, reducing repetitive logic.

// start/kernel.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { middleware, name } from '@ioc:Adonis/Core/Event'

class RoleMiddleware {
  public async handle({ auth, response, request }: HttpContextContract, next: () => Promise, role: 'user' | 'admin') {
    await auth.authenticate('basic', { guard: 'basic' })
    const user = auth.getUser()

    if (!user || user.role !== role) {
      return response.unauthorized()
    }

    await next()
  }
}

export const middleware = {
  role: RoleMiddleware,
}

export const routeNames = {
  role: name('role'),
}

// In routes.ts
Route.get('/admin/users', () => {})
  .middleware('role:admin')
  .as('admin.users')

5. Validate ownership before sensitive operations

For endpoints that act on specific resources, verify ownership or required privileges to prevent BOLA/IDOR and privilege escalation.

// Example: updating a user record
Route.put('/users/:id', async ({ params, auth, request, response }) => {
  await auth.authenticate('basic', { guard: 'basic' })
  const user = auth.getUser()

  if (!user) {
    return response.unauthorized()
  }

  // Admins can update any user; others can update only themselves
  const targetUser = await User.findOrFail(params.id)
  if (user.role !== 'admin' && user.id !== targetUser.id) {
    return response.forbidden()
  }

  const payload = request.only(['username', 'role'])
  targetUser.username = payload.username
  if (payload.role) {
    if (user.role !== 'admin') {
      return response.forbidden()
    }
    targetUser.role = payload.role
  }
  await targetUser.save()

  return targetUser
})

Frequently Asked Questions

How does middleBrick detect privilege escalation risks in AdonisJS APIs using Basic Auth?
middleBrick runs 12 parallel security checks, including BOLA/IDOR and BFLA/Privilege Escalation, comparing OpenAPI/Swagger specs with runtime behavior. For Basic Auth flows, it verifies whether authentication is followed by proper role and ownership checks, flagging endpoints where authenticated users can access admin-only resources without authorization.
Can middleBrick integrate into CI/CD to prevent privilege escalation regressions in AdonisJS projects using Basic Auth?
Yes, with the Pro plan you can use the GitHub Action to add API security checks to your CI/CD pipeline. It can fail builds if a scan detects missing role-based checks on authenticated routes, helping prevent privilege escalation regressions before deployment.