AdonisJS v5: Explicit Auth Guards Prevent Silent API Bypass
Attaching auth middleware in AdonisJS v5 is not enough; you must bind an explicit guard to each route group or unauthenticated requests can reach your controllers.
10 Oct 2026, 03:33 UTC

You added auth middleware, but the request still got through
You attach the auth middleware to an AdonisJS v5 route group, restart the server, and send a request without a token. The controller still runs and writes to the database. That is usually not a controller bug. It is a configuration gap: in AdonisJS v5, attaching the middleware by name does not by itself enforce authentication. The route group must also know which guard—session, API token, or another strategy—is responsible for vetting the request. Without an explicit guard, the middleware can pass the request straight through to your Lucid ORM queries.
The practical takeaway is to bind an explicit guard to every authenticated route group and run schema validation before any write operation. Invalid or unauthenticated requests should return standardized JSON errors instead of reaching the database.
How the v5 pipeline orders auth and validation
AdonisJS v5 executes global middleware, then named middleware, and finally the route handler. The auth middleware is a named middleware that asks a guard to identify the current user. A guard is the strategy used to extract credentials from a request; for example, the api guard typically inspects a bearer token, while the web guard reads a session. You declare guards in config/auth.ts and set a default guard that applies when a route does not specify one.
Validation happens after middleware. The AdonisJS validator sanitizes input and returns standardized JSON error responses for invalid requests, but it normally runs inside the controller. This ordering means unauthenticated requests should be rejected by the middleware before validation or database code ever runs.
Worked example: secure a posts endpoint
First, confirm the default guard in config/auth.ts. This example uses api for a token-based REST API:
// config/auth.ts
const authConfig = {
guard: 'api',
guards: {
api: {
driver: 'oat',
tokenSecret: Env.get('APP_KEY'),
// additional token options omitted for brevity
},
},
}
Next, apply the auth named middleware to the route group in start/routes.ts. Verify the exact middleware string against the AdonisJS v5 docs for your installed version:
// start/routes.ts
import Route from '@ioc:Adonis/Core/Route'
Route.group(() => {
Route.post('posts', 'PostsController.store')
}).middleware(['auth'])
Then validate the payload inside the controller before touching the database:
// app/Controllers/Http/PostsController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Post from 'App/Models/Post'
import { schema, rules } from '@ioc:Adonis/Core/Validator'
export default class PostsController {
public async store({ request, response, auth }: HttpContextContract) {
const postSchema = schema.create({
title: schema.string({}, [rules.maxLength(255)]),
body: schema.string({}, []),
})
const payload = await request.validate({ schema: postSchema })
const post = await Post.create({
userId: auth.user!.id,
...payload,
})
return response.created(post)
}
}
Here the auth middleware runs first. If it cannot identify a user, the request never reaches request.validate or Post.create. If authentication succeeds but the body is invalid, the validator returns a standardized error before any row is inserted.
Verify the behavior with curl
Run the development server from the project directory. No special permissions are required:
node ace serve
Then test three cases:
- No token:
curl -i -X POST http://localhost:3333/posts -H "Content-Type: application/json" -d '{"title":"x","body":"y"}'. Expect401 Unauthorizedif the guard is bound. - Valid token, invalid body: expect
422 Unprocessable Entityfrom the validator. - Valid token, valid body: expect
201 Created.
If the first request returns 201 Created or a server error instead of 401, the guard is not configured for that route group. Check config/auth.ts and the middleware line in start/routes.ts.
Trade-offs and limitations
- Explicit guard configuration is safer than implicit defaults, but it is easy to forget when duplicating route groups.
- Validator schemas require explicit imports and may not catch every runtime type mismatch without TypeScript strict mode.
- AdonisJS v5 introduced breaking changes from v4, especially in authentication provider registration and middleware signatures, so older tutorials may not apply unchanged.
- Authentication confirms identity; it does not confirm authorization. You still need to check whether the authenticated user owns the resource they are trying to modify.
Closing checklist
- Set the default guard in
config/auth.tsto the strategy your API uses. - Add
.middleware(['auth'])to every route group that mutates data. - Run schema validation inside the controller before any
Post.createorPost.updatecall. - Test with an unauthenticated request and confirm a
401response before deploying.
This draft reflects AdonisJS v5 behavior as described in public documentation. Verify exact middleware strings, config keys, and driver names against docs.adonisjs.com for your installed version before changing production routes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.