Plumier has several security feature to be able to protect your API using JWT. Most part of the request and response can be secure and authorize to restrict access to some user or role.
Plumier security can be enabled by using
@plumier/jwt package and set
JwtAuthFacility into Plumier application
Above will enable Plumier security feature,
secret is your JWT secret key used to sign the JWT during login process. If no
JwtAuthFacility will check for environment variable named
PLUM_JWT_SECRET, if both not provided an error will be thrown.
By default after
JwtAuthFacility applied, all route is private (Authenticated), it means non authenticated user will not able to access API except specifically defined
Plumier supported authentication using bearer token and cookie, both using JWT token. Authentication begin by signing a JWT token during login process like below.
Above is example of login controller returned a JWT token contains JWT claims
role. Its also possible to set cookie for authentication by returning cookie like below.
Above will set cookie to the API client with default
SameSite:Lax to prevent XSS and CSRF attack. The default cookie name used for authentication is
Authorization, this behavior can be changed on
JwtAuthFacility by providing
cookie option like below.
By defining a custom cookie name you should set the cookie appropriately
After user authenticated either by using bearer token on
Authorization header or using cookie, the current login user (the JWT claim) can be accessed from the request context
ctx.user anywhere on the system.
To access current login user from the controller you can use
@bind.user() like below.
JwtClaims is a specialized interface represent the user JWT claims, you can augment the interface to add more properties for intellisense like below.
When accessing current login user from other framework components other than controller which parameter binding doesn't exists, you can access it from the request context like below.
Above is example how you can access current login user from custom middleware, mostly all framework component has accessible
When user authenticated you can restrict access to some API based on user role or based on more complex condition. Plumier provide an authorization policy to define the authorization logic than it can be applied to secure access to the route, to secure setting to request part such as query or request body property, or to remove unauthorized response properties.
To create an authorization policy start by using
AuthorizationPolicyBuilder or by using its shorthand
authPolicy(). For example we will create an authorization policy for several roles
SuperAdmin by checking if the current login user claim
role property has the appropriate value.
Above example created several authorization policies named
SuperAdmin by checking the
role claim. Authorization policy allowed to returned
Promise<boolean> for asynchronous authorization logic.
Plumier has two predefined auth policy that is ready to use
Public: Used to make resource accessible by public, this authorization callback is always return true.
Authenticated: Default auth policy, used to secure routes only for login user (role omitted).
Authorization policy registration can be put anywhere with file name ends with
entity, for example
This behavior can be change using configuration below.
authPolicies configuration receive file path, directory or file glob to specify the location of the auth policy.
After authorization policy created and configured properly you can apply it to secure your API. There are several decorator can be used to apply the auth policy.
|Protect route can be accessed by specific auth policy|
|Protect property only can be write by specific auth policy|
|Protect property only can be read by specific auth policy|
|Protect property only can be read and no other role can write it|
|Protect property only can be write and no other role can read it|
For example below is how to secure a route by applying the decorator on the controller action.
With above configuration the
GET /animals route only accessible by
SuperAdmin, other than those role will receive 401.
Authorization can be applied on the controller to authorize all actions contained in the controller like below.
With above configuration both
GET /animals and
POST /animals will only accessible by
Authorization can be applied globally to apply default authorization to all routes, to do that you apply the auth policy from the
JwtAuthFacility like below.
With above configuration all routes (except explicitly has auth policy defined) will only be accessible by
Authorization can be applied on parameter to protect some request part bound to the parameter accessible by specific user.
Using above configuration some users may access the
GET /users but only
SuperAdmin can provide query string
Authorization can be applied on request body specifically on the property to restrict access to some property of the request body, you do that by adding decorator on the model properties like below.
Using above configuration, only
SuperAdmin can set the
role property of request body.
Authorization can be applied on response body, unlike most authorization process, response authorization doesn't response 401, instead its filter property value based on auth policy. You do this by applying decorator on the response model.
Using above configuration
role property will be visible only to
SuperAdmin. The response vary based on user role.
Response authorization may cause scalability issue on large response result. Since its will check the response properties recursively, its important to review the speed of your API response when you have complex nested response with complex authorization ie. use complex database query. Here are some best practice you can do
- Use query cache on authorization policy that require database operation. Choose short query cache (1 - 3 seconds) to prevent further caching issue, the idea is to prevent the same query being executed multiple time in single request.
- Only select the necessary fields on your client side on get all
GET /path?select=name,dob,createdAtand get by id
GET /path/123?select=name,dob,createdAtto prevent unnecessary role evaluation being executed.
Authorization applied to global, controller or action evaluated with some priority. Authorization system separated into three category, which is Route Authorization, Parameter Authorization, Response Authorization.
- Route authorization (global, controller, action) has the most priority evaluation, when a user doesn't have access it means he doesn't have access to the Parameter or Response.
- Parameter and Response Authorization will be evaluated later after Route authorization.
Route authorization separated into three location, which is Global Authorization, Controller Authorization and Action Authorization.
- Action authorization has the most priority evaluated. If user allowed to access action then Controller Authorization and Global Authorization ignore.
- Controller authorizations are second evaluated after Action Authorization, its means if an Action Authorization applied then it will be ignored.
- Global Authorization evaluated last.
For example if we have authorization configuration like below
Using above configuration the authorization will be like below.
POST /userswill only accessible by
SuperAdmin(inherit the controller authorization)
GET /userswill be accessible by
Public(its override the controller authorization), but
GET /users?emailwill only accessible by
GET /dashboardwill be accessible by
DashboardControllerdoesn't has any authorization applied it inherit the global authorization.