Api Key Exposure in Adonisjs with Basic Auth
Api Key Exposure in Adonisjs with Basic Auth — how this specific combination creates or exposes the vulnerability
AdonisJS applications that rely on HTTP Basic Authentication can inadvertently expose API keys when credentials are handled inconsistently across middleware, logging, and error handling. Basic Auth transmits credentials on each request using the Authorization: Basic base64(username:password) header. If the application treats the decoded credentials as an API key or uses the same secret for both authentication and key-based authorization, an attacker who intercepts or logs the header can obtain a long-lived key.
In AdonisJS, a common pattern uses the auth:basic provider to validate users against the database. If route guards or permission checks then reuse the authenticated user’s token or an embedded key from the database record as an API key, any misconfiguration in request logging or error reporting can leak that key. For example, if unauthenticated or improperly namespaced endpoints echo request headers for debugging, the Base64–encoded credentials may be captured in logs or browser developer tools. Because Base64 is reversible, the exposure of the header effectively exposes the credentials, which may function as an API key if the backend treats them as such.
Another vector arises when AdonisJS applications proxy requests to internal services and forward the incoming Authorization header without stripping or transforming it. If the downstream service interprets the Basic Auth credentials as an API key (for example, by mapping the username to a service account key), an unauthenticated attacker who reaches the proxy endpoint can enumerate valid credentials through rate-limited probing or SSRF-assisted scans. The risk is compounded when the same secret is used for both HTTP Basic Authentication and third-party service authentication, creating a path where exposure of one vector compromises multiple systems.
The OpenAPI/Swagger analysis performed by middleBrick highlights these risks by correlating spec definitions with runtime behavior. When an endpoint declares security schemes using httpBasic but also references bearer-like scopes or custom headers that carry key-like values, the scanner flags potential credential-to-key reuse. Cross-referencing spec definitions with runtime findings can reveal whether responses inadvertently include authorization headers in error payloads or logs, which would constitute data exposure under frameworks such as OWASP API Top 10 and GDPR.
middleBrick’s 12 security checks run in parallel to detect issues like Data Exposure, Unsafe Consumption, and SSRF in this context. For instance, it tests whether unauthenticated responses reveal whether Basic Auth validation succeeded or failed in ways that disclose user existence, and whether error messages contain encoded credentials. Because scans are black-box and require no credentials, they can safely probe publicly reachable endpoints to identify leakage paths without impacting production systems.
When findings are surfaced in the Web Dashboard or tracked over time via the Dashboard, teams receive prioritized findings with severity ratings and remediation guidance. The CLI tool (middlebrick scan <url>) can be integrated into scripts to automate detection of misconfigurations, while the GitHub Action can enforce a security score threshold in CI/CD pipelines to prevent deployment of vulnerable configurations. For continuous protection, the Pro plan enables scheduled scans and alerts, ensuring that changes to authentication logic are reviewed before they reach production.
Basic Auth-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on preventing the reuse of Basic Auth credentials as API keys, tightening logging, and ensuring that headers are not forwarded to downstream services. Below are concrete AdonisJS code examples that implement these controls.
1. Use dedicated authentication middleware that avoids exposing credentials as keys:
// start/hooks.ts
import { Exception } from '@poppinss/utils'
export const handleAuthErrors = (error: any) => {
if (error.name === 'E_INVALID_CREDENTIALS') {
return new Exception('Invalid credentials', 401, {
code: 'E_INVALID_CREDENTIALS',
})
}
return error
}
// app/Controllers/Http/auth.controller.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import User from 'App/Models/User'
import { Exception } from '@poppinss/utils'
export default class AuthController {
public async login({ request, auth, response }: HttpContextContract) {
const { username, password } = request.only(['username', 'password'])
const token = await auth.attempt(username, password)
// Do NOT expose token as Basic Auth credentials
return response.header('Authorization', '').json({ access_token: token })
}
}
2. Configure the auth provider to use a custom resolver that does not map credentials to API keys:
// config/auth.ts
import { AuthConfig } from '@ioc:Adonis/Add/Auth'
const authConfig: AuthConfig = {
defaults: {
guard: 'api',
usernameField: 'email',
},
guards: {
api: {
driver: 'jwt',
key: process.env.API_JWT_SECRET,
provider: 'users',
},
basic: {
driver: 'basic',
provider: 'users',
// Use a separate field for key-like tokens, not credentials
keyField: 'service_key',
},
},
providers: {
users: {
driver: 'lucid',
model: () => import('App/Models/User'),
},
},
}
export default authConfig
3. Strip sensitive headers before logging or forwarding requests:
// start/middleware/secure-headers.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default function secureHeadersMiddleware(ctx: HttpContextContract, next: () => Promise) {
// Remove Authorization header before logging
const safeHeaders = { ...ctx.request.headers() }
delete safeHeaders.authorization
ctx.logger.info('Incoming request', { headers: safeHeaders })
return next()
}
4. In gateway or proxy scenarios, do not forward Basic Auth headers to downstream services; instead, exchange credentials for a scoped token:
// app/Services/ProxyService.ts
import fetch from 'node-fetch'
export default class ProxyService {
async forward(url: string, userEmail: string, password: string) {
const credentials = Buffer.from(`${userEmail}:${password}`).toString('base64')
// Exchange credentials for an internal token, do not forward Authorization header
const response = await fetch(url, {
headers: {
'X-Internal-Token': process.env.INTERNAL_TOKEN,
'X-User-Email': userEmail,
},
})
return response
}
}
These steps reduce the likelihood that Basic Auth credentials are treated as or leaked as API keys. By isolating authentication from key-based authorization, sanitizing logs, and avoiding header propagation, you limit the attack surface even if credentials are exposed through other vectors.