Access to application resources (entities) is controlled in four ways:
An internal service is created for each of the the application’s entities. Services can be marked as active or inactive in the service configuration, thereby allowing a single application to be generated, while at the same time allowing selective service deployment. For example, there may be cases where it is desirable to route certain services to a particular application instance and another set of services to the rest of the pool. In such a case, a reverse-proxy could be configured to route the end-points appropriately, and the deployed service configurations would be adjusted accordingly. Service activations are controlled via entries in the ‘service_activations’ block of each application instance’s configuration file.
The following configuration file excerpt shows that the selected instance of the application is servicing ‘Person’ entity requests, but is not presently servicing ‘Car’ entity requests. However, if a relationship exists between ‘Person’ and ‘Car’, requests rooted on the ‘Person’ entity but involving the ‘Car’ entity will be accepted. For example, the selected instance would not honor a GET request for ../car/1234, but would accept and process a request for ../person/1234/toCar. In practice, the reverse-proxy/load-balancer should be configured to route requests to the correct end-point.
...
...,
"service_activations": [
{
"service_name": "Person",
"service_active": true
},
{
"service_name": "Car",
"service_active": false
}
]
}
User authentication is conducted using bcrypt in such a manner that passwords are never stored in the application database. When a user is created, their user-id is stored in the database along with the salt/peppered bcrypt hash of their password. This ensures that in the event of a breach no plain-text passwords can be obtained.
Bcrypt was chosen for the following reasons:
When a user logs into the application the following steps occur:
In addition to fulfilling the authorization requirements, the JWT is also used as a CSRF equivalent. By default the generated JWT has a validity of one-hour, but this may be adjusted via the application configuration file. See the following Authorization and End-Point Security section for more details regarding the content and use of the JWT content/claims.
In addition to password authentication, generated applications provide the ability to manage access to their end-points via an authorization scheme. At a high-level:
-> User
|
--> Group 1
| |
| --> Auth_EndPoint_A
| --> Auth_EndPoint_B
| --> Auth_EndPoint_C
|
--> Group 2
|
--> Auth_EndPoint_K
--> Auth_EndPoint_M
Application access can be restricted at the end-point level. Each generated end-point is given a name based on its entity, http method and purpose. The gorilla mux provides an easy way to assign names to end-points in a route declaration, and these names are defined in the generated application as Authorizations or Auths.
Authorizations are created per end-point and are therefore known to the router, which in turn allows the route middleware of the generated application to determine which Authorization is needed in order to permit the request to proceed. Recall that an authenticated user is sent a signed base64-encoded JWT token that must be passed in the http header Authorization field of each request. The generated router middleware validates the signature, decodes the token, and then examines its Claims in order to determine whether the request should be allowed to proceed. This level of checking can be thought of as the Authentication verification; does the requesting party have a valid access token for the system in general?
Assuming that the requesting user has a valid access token, the next step is to determine whether the user has permission to access the requested end-point. Each User is assigned to one or more User Groups and these are included as a Groups Claim in the JWT token when the User logs into the application. As a result, the route middleware is able to examine the content of the Groups Claim in order to determine whether the User is permitted to access the requested end-point. The route authorization check unfolds as follows:
The last bullet point is interesting, as it means that end-point access of protected routes is denied by default. Unless access is specifically granted via Authorization -> User Group -> User assignment, the protected end-point is not accessible.