This repository provides a pattern for authentication and authorization implemented in NodeJs using Inversify and solves the problem of passing user information between service components by creating request scoped objects.
Previously, I was coding mostly in Java and I used very well Spring Framework that provided a couple of fancy solutions for authentication and authorization, just consider the @PreAuthorize() annotation with the possibility to check wether the user has the required role and authority. Spring Framework provides very comfortable ways to check user premissions through the isAuthenticated(), hasAuthority() and hasRole() methods. Spring Framework is able to easily separate user information by creating request scoped beans. In the background it start a new thread for each requests and binds the user information to it.
Unfortunately, it is not recommended to use threads in NodeJS, because it brakes the event-driven pattern of the NodeJs.
NodeJS is an event-driven nonblocking runtime environment for JavaScript, that uses events and event handlers instead of threads. Of course, it is possible to use threads in NodeJS, however it increases the resource consumption, it is difficult to use and it is getting more difficult if you are using TypeScript. Then, how to create global objects per requests which are destroyed as the request ended?
The solution is provided by using Inversify and Express together in form of InversifyExpressServer, that makes possible to create objects which are binded to requests. In this example we created an authentication provider that extracts JWT token from the request header then it binds to a symbol that will be injectable in services.
The main part of the solution are the followings:
@injectable()
class CustomAuthProvider implements interfaces.AuthProvider {
public async getUser(
req: express.Request,
__res: express.Response,
__next: express.NextFunction
): Promise<Principal> {
return new Principal({
token: req.headers["x-auth-token"]
});
}
}
@controller("/some-controller")
class SomeController extends BaseHttpController {
@inject(TYPES.Principal) private readonly principal: Principal;
@inject(TYPES.CustomService)
private readonly customService: CustomService;
@httpGet("/")
public async get(
__req: express.Request,
res: express.Response,
__next: express.NextFunction
) {
if (this.principal) {
...
res.status(200).json(this.principal);
}
}
...
}
const server = new InversifyExpressServer(
diContainer,
null,
null,
null,
CustomAuthProvider
);
server.setConfig(app => {
app.use(
bodyParser.urlencoded({
extended: true
})
);
app.use(bodyParser.json());
});
server.build().listen(3000);
For more details please check the repository.
$ yarn install
$ yarn build
$ yarn start
There is a postman collection file in the test directory that makes easy to test requests.
The main idea of the solution comes here: inversify/InversifyJS#754; Thanks Remo H. Jansen!