AdonisJS provides a standard way to define a middleware and enable access to routes based on defined validation rules. This middleware is leveraged to allow role-based access to routes.
Your start | routes.js file will have something akin to below -
const Route = use("Route");
Route.get("/", () => {
return { greeting: "Hello world!" };
});
First, we will group routes for the different roles and introduce the middleware.
// start | Routes.js
Route.group(() => {
Route.post("/user-register", "UserController.register");
Route.post("/user-login", "UserController.login");
});
Route.group(() => {
Route.get("/todo", "TodoController.index");
Route.post("/todo", "TodoController.create");
}).middleware("auth");
Route.group(() => {
Route.delete("/todo", "TodoController.delete");
}).middleware(["auth", "admin"]);
registerandloginmethods are available to unauthenticated and authenticated users- To do
createandindexare available only to authenticated users.authis provided by AdonisJS - Deleting to do is allowed only for admin
Now, we write the actual middleware for admin.
Create a admin.js file in app| Middleware folder under root. Introduce the following code -
"use strict";
const InvalidAccessException = use("App/Exceptions/InvalidAccessException");
class Admin {
async handle({ request, auth }, next) {
const user = await auth.getUser();
if (!user.role_cd || user.role_cd != "admin")
throw new InvalidAccessException();
await next();
}
}
module.exports = Admin;
What happens when a user tries to access the delete route -
- Adonis executes the
authandadminmiddleware authpasses ok if user is authenticatedadminchecks if theuser.role_cd, a custom field, has the value ‘admin’. If this does not check out, an exception is thrownInvalidAccessExceptionis a custom exception that returns a404with a custom message (see [custom exceptions in Adonis)(/custom-exceptions-in-adonisjs/)
In a previous post we had a short discussion on implementing access rules in Adonis based on roles. Should you implement that vs. implementing the above in routes?
Well - it depends.
- If the entire route is cordoned off to roles, you have a cleaner approach using role-based validation at the route level.
- For everything else, let your controller take the lead for role-based authentication
In the real world, you would want to implement a separate service for role-based auth. This service can be called by your route middleware, or your controllers depending on your use case.