HIGH brute force attackadonisjsjwt tokens

Brute Force Attack in Adonisjs with Jwt Tokens

Brute Force Attack in Adonisjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

A brute force attack against an API using JWT tokens in AdonisJS typically targets the token validation or authentication endpoints rather than attempting to crack the token signature itself. Because JWTs are cryptographically signed, attackers cannot feasibly guess or forge a valid token. Instead, they use high-volume automated requests to guess valid user identities (e.g., numeric IDs or known usernames/emails) or use credential stuffing techniques to discover accounts with weak passwords. In AdonisJS, if authentication routes do not enforce strict rate limiting, each request consumes server-side resources for token verification, signature validation, and database lookups. This can lead to resource exhaustion, increased latency for legitimate users, and potential account enumeration when responses differ between existing and non-existing accounts.

The vulnerability is amplified when applications expose token introspection or validation endpoints without proper controls. For example, an endpoint that returns detailed error messages like “Invalid signature” versus “User not found” can allow an attacker to distinguish between valid and invalid identifiers. Additionally, if JWT tokens have long expiration times or are not bound to a per-request nonce or IP context, an attacker who intercepts a token might replay it to probe account permissions or infer account behavior. The attack surface also includes unauthenticated endpoints used for login or token refresh, where weak account lockout or captcha mechanisms can enable rapid, automated attempts.

To detect this pattern, middleBrick runs authentication and rate limiting checks alongside account enumeration probes. In an OpenAPI/Swagger spec, the tool cross-references defined authentication flows (e.g., securitySchemes using type: http and scheme: bearer) with runtime behavior, validating that error responses remain consistent and that endpoints enforcing JWT verification do not leak user existence. The scanner does not rely on internal architecture; it observes inputs and outputs to identify whether brute force risks exist in the API design and implementation.

Jwt Tokens-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on rate limiting, consistent error handling, and secure token usage. Below are AdonisJS code examples that demonstrate these controls.

1. Apply rate limiting to authentication routes

Use AdonisJS middleware to limit login and token validation requests. This example uses the built-in Throttle middleware to cap attempts per IP.

// start/handlers/throttle-login.ts
import { Throttle } from '@ioc:Adonis/Addons/Throttle'

export const config = {
  identifier: 'login-throttle',
  maxAttempts: 5,
  windowInSeconds: 60,
  timeoutInSeconds: 300,
}

// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
import throttleLogin from 'App/Handlers/throttle-login'

Route.post('auth/login', 'AuthController.login').middleware(['throttle:login-throttle', 'validateLogin'])
Route.post('auth/token/refresh', 'AuthController.refresh').middleware(['throttle:login-throttle'])

2. Use consistent error responses to prevent account enumeration

Ensure that authentication responses do not reveal whether an account exists. Always perform token validation and database lookup before returning a standardized error payload.

// app/Controllers/Http/AuthController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { schema } from '@ioc:Adonis/Core/Validator'
import User from 'App/Models/User'
import { DateTime } from 'luxon'

export default class AuthController {
  public async login({ request, auth, response }: HttpContextContract) {
    const email = request.input('email')
    const password = request.input('password')

    // Validate input first to avoid unnecessary work
    const loginSchema = schema.create({
      email: schema.string.optional([
        ...email.isEmail ? [] : ['email'],
      ]),
      password: schema.string.optional([
        ...password ? [] : ['required'],
      ]),
    })

    const payload = await request.validate({ schema: loginSchema })

    // Always perform token validation if a token is provided
    let user: User | null = null
    if (payload.email) {
      user = await User.findBy('email', payload.email)
    }

    // Standardized response regardless of user existence
    if (!user || !(await user.verifyPassword(password))) {
      return response.unauthorized({
        error: {
          code: 'auth_invalid_credentials',
          message: 'Invalid credentials',
          documentation: 'https://docs.example.com/auth/errors#auth_invalid_credentials',
        },
      })
    }

    const token = await auth.use('api').generate(user)
    return response.ok({
      token: token.toJSON(),
      expiresIn: DateTime.local().plus({ hours: 1 }).toISO(),
    })
  }
}

3. Configure JWT options to limit token lifetime and scope

Keep token lifetimes short and avoid exposing sensitive claims. Configure the JWT provider to use appropriate options.

// config/jwt.ts
export const jwt = {
  enabled: true,
  secret: process.env.JWT_SECRET as string,
  token: {
    expiresIn: '15m',
    notBefore: '0s',
    issuer: 'api.example.com',
    audience: 'web-app',
  },
  refreshToken: {
    enabled: true,
    expiresIn: '7d',
  },
}

4. Validate token binding where applicable

Bind tokens to contextual properties such as IP or user-agent when your threat model requires it. This example stores the IP at issuance and validates it on sensitive operations.

// app/Models/User.ts
import { DateTime } from 'luxon'
import { column, beforeSave } from '@ioc:Adonis/Lucid/Orm'
import { Hash } from '@ioc:Adonis/Core/Hash'
import type { LucidModel } from '@ioc:Adonis/Lucid/Model'
import { v4 as uuidv4 } from 'uuid'

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

  @column()
  public email: string

  @column()
  public password: string

  @column()
  public ipAtTokenIssuance?: string

  @beforeSave()
  public static async hashPassword(user: User) {
    if ($exposing(event, 'saving') && user.$dirty.password) {
      user.password = await Hash.make(user.password)
    }
  }
}

// In a controller or middleware handling sensitive routes
public async verifyTokenContext({ auth, request, response }: HttpContextContract) {
  const user = auth.user
  const token = request.headers().authorization?.replace('Bearer ', '')
  if (!token || !user) {
    return response.unauthorized({ error: { code: 'auth_token_missing', message: 'Unauthorized' } })
  }

  // Example: check that the current request IP matches the issuance context
  if (user.ipAtTokenIssuance && user.ipAtTokenIssuance !== request.ip()) {
    return response.forbidden({ error: { code: 'token_context_mismatch', message: 'Token context invalid' } })
  }
}

5. Secure OpenAPI/Swagger spec references for JWT

Ensure your spec defines bearer security schemes and that responses do not leak information. Use x-internal or x-unauthenticated tags for public routes and require security definitions for sensitive operations.

# openapi.yaml (excerpt)
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        password:
          tokenUrl: /auth/login
          scopes: {}
security:
  - bearerAuth: []

Frequently Asked Questions

How does middleBrick detect brute force risks related to JWT authentication in AdonisJS?
middleBrick runs authentication and rate limiting checks alongside account enumeration probes. It cross-references your OpenAPI/Swagger definitions (securitySchemes with type: http and scheme: bearer) with runtime behavior to verify that error responses remain consistent and that endpoints enforcing JWT verification do not leak user existence.
Can middleBrick fix brute force vulnerabilities automatically?
middleBrick detects and reports findings with severity and remediation guidance; it does not fix, patch, block, or remediate. Apply rate limiting, consistent error handling, short JWT lifetimes, and token binding using the code examples provided to reduce risk.