Security
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.
#
Enable FunctionalityPlumier 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 secret
provide JwtAuthFacility
will check for environment variable named PLUM_JWT_SECRET
, if both not provided an error will be thrown.
note
By default after JwtAuthFacility
applied, all route is private (Authenticated), it means non authenticated user will not able to access API except specifically defined Public
#
AuthenticationPlumier 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 userId
and role
. Its also possible to set cookie for authentication by returning cookie like below.
Above will set cookie to the API client with default HttpOnly
and 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 .setCookie("Oreo", token)
.
#
Accessing Current Login UserAfter 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.
Note that 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 ctx
property.
#
AuthorizationWhen 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 User
, Admin
, SuperAdmin
by checking if the current login user claim role
property has the appropriate value.
Above example created several authorization policies named User
, Admin
, SuperAdmin
by checking the role
claim. Authorization policy allowed to returned boolean
or Promise<boolean>
for asynchronous authorization logic.
note
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 File RegistrationAuthorization policy registration can be put anywhere with file name ends with policy
, controller
or entity
, for example user-policy.ts
, user_policy.ts
, user-controller.ts
etc.
This behavior can be change using configuration below.
The authPolicies
configuration receive file path, directory or file glob to specify the location of the auth policy.
#
Applying Authorization PolicyAfter 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.
Decorator | Description |
---|---|
@authorize.route(AUTH_POLICY) | Protect route can be accessed by specific auth policy |
@authorize.write(AUTH_POLICY) | Protect property only can be write by specific auth policy |
@authorize.read(AUTH_POLICY) | Protect property only can be read by specific auth policy |
@authorize.readonly() | Protect property only can be read and no other role can write it |
@authorize.writeonly() | Protect property only can be write and no other role can read it |
#
Authorizing RouteFor 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 Admin
or 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 Admin
or SuperAdmin
.
#
Global Route AuthorizationAuthorization 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 Admin
or SuperAdmin
#
Query String AuthorizationAuthorization 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 Admin
or SuperAdmin
can provide query string email
other than that will returned 401.
#
Request Body AuthorizationAuthorization 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 Admin
or SuperAdmin
can set the role
property of request body.
#
Response AuthorizationAuthorization 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 email
and role
property will be visible only to Admin
and SuperAdmin
. The response vary based on user role.
#
Scalability Best PracticeResponse 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,createdAt
and get by idGET /path/123?select=name,dob,createdAt
to prevent unnecessary role evaluation being executed.
#
Authorization Evaluation OrderAuthorization 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 /users
will only accessible byAdmin
andSuperAdmin
(inherit the controller authorization)GET /users
will be accessible byPublic
(its override the controller authorization), butGET /users?email
will only accessible bySuperAdmin
.GET /dashboard
will be accessible byPublic
, sinceDashboardController
doesn't has any authorization applied it inherit the global authorization.