Technical Specification Template
This is a template that can be used when writing technical specifications for development requirements. Keep in mind that this is a flexible template which can be adapted to the type of requirement. Here, an example has been used to illustrate how to fill in the template.
Request ID | 72.1 |
Title of the request | Security layer for IMIS API |
Relates to | RFC 72 |
Submitted on | 02/07/2018 |
Assessed on |
|
Approved on |
|
Status | Submitted Assessed Approved Rejected Processed Implemented |
Impact on costs (man hours, man days) |
|
Impact on schedule |
|
Category of change | Minor Major Principal |
Comments of vendor |
|
Version history | |||
Version | Date | Author | Revision |
1.0 | 02/07/2018 | DD (STPH) | First draft |
1.1 | 10/07/2018 | DD (STPH) | Added 2.3 JWT Payload information Added user password change impact |
1. Requirement
1.1 Goals/Scope
The scope of this RFC is to increase the security of the IMIS API when used by any third party applications and thus not allowing access to API calls to not authorized users.
1.2 Background
The IMIS Web Services can be called by any Mobile or Web Client without prior need of a registered user. This is due to the fact that any API call is creating a new session on the server side and, even that a Validate_credential function exists, it doesn’t allows remembering the user whose credentials are validated. This represents a security threat for the all IMIS information system.
1.3 Assumptions
Assumes that the IMIS API is used by a registered user
1.4 User stories
# | User Story | Importance | Notes |
1 | As an IMIS administrator, I want to prevent unauthorized access to the information. | Must have |
|
2 | As a user, I want to get my personal API access token using my login credentials. | Must have |
|
3 | As a user, I want to access the API functions using my personal access token. | Must have |
|
2. Technical Specification
This technical specification will deal with the implementation of the authentication and the authorisation of the user to execute API calls. In order to assure this, the LOGIN API call and the calls to all other functions must be updated.
2.1 Authentication protocol
2.1.1 JWT Authentication
The IMIS API should use a JSON Web Tokens authentication.
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA. [https://jwt.io ]
JWTs consist of three parts separated by dots (.), which are:
Header.Payload.Signature |
Therefore, a JWT typically looks like the following: xxxxx.yyyyy.zzzzz
Header
The header typically consists of two parts: the type of the token, which is JWT, and the hashing algorithm such as HMAC SHA256 or RSA.
In our case we will use:
{"alg": "HS256", "typ": "JWT"} |
Then, this JSON is Base64Url encoded to form the first part of the JWT.
Payload
The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional metadata. There are three types of claims: reserved, public, and private claims.
Reserved claims: These are a set of predefined claims, which are not mandatory but recommended, thought to provide a set of useful, interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), aud (audience), among others.
Notice that the claim names are only three characters long as JWT is meant to be compact.
Public claims: These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace.
Private claims: These are the custom claims created to share information between parties that agree on using them.
An example of payload we will use:
{"name": "[name of the user]", "role": "[role of the user]", "role": "[role of the user]"} |
Optional, we could add an expiration time to increase the security. We will add claims in the token for each role of the user within the IMIS system. These roles will be then used to define the authorisation access to the API calls.
The payload is then Base64Url encoded to form the second part of the JWT.
Signature
To create the signature part you have to take the encoded header, the encoded payload, a private_key, the algorithm specified in the header, and sign that.
As we choose to use HMAC SHA256 algorithm, the signature will be created in the following way.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), private_key) |
The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed in the way.
2.1.2 How to use the JWT
In authentication, when the user successfully logs in using his credentials, a JSON Web Token will be returned and must be saved locally (typically in local storage, but cookies can be also used), instead of the traditional approach of creating a session in the server and returning a cookie.
Whenever the user wants to access a protected route, it should send the JWT, typically in the Authorization header using the Bearer schema. Therefore the content of the header should look like the following.
Authorization: Bearer <token> |
This is a stateless authentication mechanism as the user state is never saved in the server memory. The server’s protected routes will check for a valid JWT in the Authorization header, and if there is, the user will be allowed.
2.1.3 Impact on the general architecture
There are some components that need to be updated in the general architecture (Figure 1) in order to implement the JWT authenticated API.
Figure 1. Composite structure for JWT authenticated API architecture
These components are:
· The database for storing new private keys
· The Web Application for creating and registering new private keys
· The API for creating and validating the JWT
· The different mobile or web clients to use the JWT
2.1.4 Impact on the database structure
In order to sign the token, we will generate a private_key for each registered User which will be saved on the server.
There are several possibilities:
· The private_key can be generated on the user registration and used for all logins. This possibility allows multiple logins (multiple devices at a time).
· The private_key can be regenerated on each login, but can be used only for the last login (only one device at a time).
· The private_key can be generated for each login (multiple devices at a time) and all private_keys will be saved on a separated table in the database. This solution is more secure but it requests more time when calling the API on the validation part.
For IMIS authentication, we will use the first solution. This will impact:
· the tblUsers table in the database by adding a column for the private_key
· the User.aspx.vb page by generating the private_key and assigning it to the user
When generating the private_key value, a minimum length of 20 characters must be considered in order to satisfy encryption requirements.
Every time the user change the password, a new private key must be generated in order to invalidate any already provided JWT.
2.2 Authorisation protocol
Net Core supports for several types of authorisation mechanisms (ex. Simple, Role-based, Claims-based, and Policy-based).
For implementing the authorisation protocol, we will use the role-based authorization, as described in section 2.1.1. This is based on the Role claims from the JWT token stored on token creation.
For each API controller and/or action we will need to add the Authorize attribute with the Roles parameter (list of roles that has access to the controller/action).
[Authorize(Roles = "IMISAdmin")] [Route("api/[controller]")] public class ValuesController : Controller { // GET api/values [Authorize(Roles = "EnrollmentOfficer")] [HttpGet] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } } |
2.3 JWT Payload information
In the authentication protocol we decided to use a JWT signed by user defined private key. For any API requests, the JWT have to be validated. This is only possible by knowing which user has requested this token, and to access his private key. We will use the “name” claim type to register the username in the token payload.
In the authorisation protocol, we chose to implement a Role-based authorisation protocol. Net core IdentityModel library provide out of the box authorisation mechanism based on the “role” claim type coming from the JWT. For each user’s role we will add a “role” claim with a string representing the role, the same strings used in the Authorisation protocol described in section 2.2.
For the expiration date we will provide a 5 (five) days limit. This value will be provided in the configuration file (appsettings.json) as an integer of number of days the token is valid (NOW + number of days).
Two additional claim types will be registered in the payload: the issuer and the audience. These values will be taken from the configuration file.
The JWT payload will have the following structure:
{ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": <username string>, "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [ <role string>, <role string>, … ], "exp": <expiration datetime>, "iss": <issuer string>, "aud": <audience string> } |
Example of a JWT payload for the Admin user:
{ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Admin", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [ "EnrollmentOfficer", "Manager", "Accountant", "Clerk", "MedicalOfficer", "SchemeAdmin", "IMISAdmin", "Receptionist", "ClaimAdmin", "ClaimContrib" ], "exp": 1533820230, "iss": "http://openimis.org ", "aud": "http://openimis.org " } |
2.4 Get API token
2.4.1 API /login diagram
Figure 2. Sequence diagram for API login call
2.4.2 API /login description
Feature | Specifications |
Title | Validate credentials and generate token |
Description | Creates and returns JWT token to use for the Authentication. |
URL | /login |
Method | POST |
Authentication | NONE |
Request Body | { "username": "[USERNAME]", "password": "[PASSWORD]" }
where: · USERNAME: string, required · PASSWORD: string, required |
Response Codes and Body | 200 - Success { "token": "[JWT_ACCESS_TOKEN]", "expires": "[Token_expiration_DateTime]" } 400 - Error in Request { "error": "The request body is invalid" } 401 - Unauthorized No body |
2.5 API call
As presented in 2.1.2, every call that needs authentication has to add to the request header the Authorization tag with the token. This way, on the server side, the token is validated and the access to the resource is granted.
The next diagram presents an example of API call with JWT authentication.
2.5.1 API call diagram
2.5.2 API call description
Example of the specification for an API method:
Feature | Specifications |
Title | Enter New Family |
Description | Enters a new family/group and its head. |
URL | /family |
Method | POST |
Authentication | JWT |
Request Body | { "village_code": "VILLAGE_CODE", // string, required "insurance_number_of_head": "INSURANCE_NUMBER_OF_HEAD", // string, required "other_name": "OTHER_NAME", // string, required "last_name": "LAST_NAME", // string, required "birth_date": "BIRTH_DATE", // date, required "gender": "GENDER", // {male, female, other}, required "poverty_status": "POVERTY_STATUS", // boolean, optional "confirmation_type": "CONFIRMATION_TYPE", // tblConfirmationType, optional "group_type": "GROUP_TYPE", // tblGroupType, optional "confirmation_no": "CONFIRMATION_NO", // string, optional "permanent_address_details": "PERMANENT_ADDRESS_DETAILS", // string, optional "marital_status": "MARITAL_STATUS", // tblMaritalStatus, optional "benefitiary_card": "BENEFITIARY_CARD", // boolean, optional "current_village_code": "CURRENT_VILLAGE_CODE", // string, optional "current_address_details": "CURRENT_ADDRESS_DETAILS", // string, optional "profession": "PROFESSION", // tblProfession, optional "education": "EDUCATION", // tblEducation, optional "phone_number": "PHONE_NUMBER", // string, optional "email": "EMAIL", // string, optional "identification_type": "IDENTIFICATION_TYPE", // tblIdentification, optional "identification_number": "IDENTIFICATION_NUMBER", // string, optional "FSP_code": "FSP_CODE", // string, optional } |
Response Codes and Body | 200 - Success { "familyID": "FAMILYID" } 400 - Error in Request {"error": "ERROR_MESSAGE"} 401 - Unauthorized No body |
Error messages (400 status code) | 1-Wrong format or missing insurance number of head |
2- Duplicated insurance number of head | |
3- Wrong or missing permanent village code | |
4-Wrong current village code | |
5-Wrong or missing gender | |
6-Wrong format or missing birth date | |
7-Missing last name | |
8-Missing other name | |
9-Wrong confirmation type | |
10-Wrong group type | |
11-Wrong marital status | |
12-Wrong education | |
13-Wrong profession | |
14-FSP code not found |
Did you encounter a problem or do you have a suggestion?
Please contact our Service Desk
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. https://creativecommons.org/licenses/by-sa/4.0/