3. Authentication and authorization
The World Modeler™ API can only be accessed by authorized users. Before determining whether a user is authorized to invoke the operation they are requesting, the World Modeler API requires that they authenticate their identity. Before proceeding further, let us define what we mean by these terms:
- Authorization:
- The process of determining whether or not a request to access a system resource, such as data or functionality, will be granted to the consumer making the request.
- Authentication:
- The process of validating the credentials presented to gain access to the system to ensure the requestor’s identity is verified.
Users are authorized to make World Modeler API calls only if:
- They have a valid user id that is authenticated by World Modeler
- A valid API license key is provided
- The user and/or the API key has been granted access the service they are requesting.
World Modeler Server uses JSON Web Tokens (JWT) and Bearer authorization to establish the user credentials of every request sent to the REST API. This requires that each HTTP request include an Authorization header whose value indicates that the Bearer authorization method is being used, and specifies the JWT that establishes the requestor’s credentials. This has the form:
Authorization: Bearer JWT
Most client-side application frameworks and test/diagnostic tools such as Postman and Swagger will automatically include this header when sending HTTP requests if they are set to use Bearer authorization and provided with the necessary token.
Login and Refresh
World Modeler’s authorization/authentication protocol uses a technique called Refresh Tokens to add an extra layer of security against possible interception of the access token during a session. The first thing that any client of the World Modeler API must do before invoking any other API functions is gain authorization to use the API. This is done via a POST call to auth/login, where the username, password, organization, agent are passed to World Modeler server for authentication. Note that users may belong to an organization, which can be used to set up data sharing, common licensing, etc., or they may be individuals in which case organization is null. In addition, certain applications may have been granted special rights, accessible by a GUID issued to the application vendor. In this case, the GUID is passed as the clientAgent argument, otherwise this is left empty.
The credentials to be authenticated are passed in the body of the request as JSON as shown below:
URL: https://host.domain/login
Method: POST
Body:
{
"username": "string", // user name for login
"password": "string", // password for login
"organization": "string", // organization’s GUID (or null/empty)
"clientAgent": "string", // registered GUID of client application (or null/empty)
"apiKey": "string" // API key required to access the REST API
}
Consider that if, upon authentication of the above credentials, a single access token is issued for the life of the session and a bad actor intercepts the token early in the session, then they may be able to impersonate a valid user until the token expires. To limit this risk, World Modeler will issue two tokens if the login request is successful. Note that these are returned as the value key of a RESTResponse JSON object as described in section 1.
Status: 200 (Success)
Body
{
"status": "Success",
"value": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ",
"refreshToken": "efJqbGciOiJIUzL1NiR1nR5cCI6IkpXVCJ9... ",
"accessTokenExp": "2023-12-05T22:10:25.5328618Z",
"refreshTokenExp": "2023-12-12T22:09:25.5329336Z"
},
"message": null,
"requestTime": "0001-01-01T00:00:00",
"duration": "00:00:00",
"url": "https://host.domain/login"
}
The first of these is the access token, which is used to authorize each HTTP request via the Authorization header, as described above. However, the access token’s lifetime is very short – typically one minute. The second token is called the refresh token. This has a much longer lifetime and is used to acquire a new access token when the current token expires. The session flow is shown in the diagram below for an application (the API Consumer) that will consume services from the World Modeler Server. Note that both new access and refresh tokens are issued when the refreshToken operation is invoked from the API.
The refreshToken operation is invoked as:
URL: https://host.domain/refreshToken
Method: POST
Body:
```json
{
"accessToken": "eyJhbGci... ", // previously issued access token (possibly expired)
"refreshToken": "ey1Jsgdc..." // previously issued refresh token matching access token
//(must be valid)
}
```
Note also that neither login nor refreshToken operations require an Authorization header, since this cannot be set until a valid access token has been obtained.
Authentication and authorization error codes
If an authentication request fails, the message body depends on the nature of the failure, and when it occurs during the HTTP request processing pipeline. If World Modeler fails to authenticate a user in the auth/login operation, or to renew tokens in an refreshToken operation due to invalid credentials being passed, the response will return with status 401 (Unauthorized), and its body will contain a RESTResponse JSON object, as shown below.
Status: 401 (Unauthorized)
Body:
{
"status": "Fail",
"value": null,
"message": "Invalid credentials",
"requestTime": "0001-01-01T00:00:00",
"duration": "00:00:00"
"url":"https://host.domain/refreshToken"
}
When this type of 401 error occurs, that is, during authentication, the remedy is to ensure that the credentials being passed to auth/login, or the tokens being passed to auth/refreshToken are valid.
However, if authorization fails when calling a general operation in the API that is not part of the auth module, this is most likely because the access token is absent, invalid, or has expired. In this case, the response will be limited to information provided in the www-authenticate header, and the body will be empty, as follows:
Status: 401 (Unauthorized)
Header:
content-length: 0
date: Thu,12 Oct 2023 10:27:21 GMT
server: Kestrel
www-authenticate: Bearer error="invalid_token",error_description="The token
expired at '10/12/2023 09:14:16'"
In this scenario, it is likely that current access token has expired, has become corrupted, or the Authorization Bearer header is absent or incorrect. To fix this, auth/refreshToken or auth/login should be called, as appropriate, to issue valid tokens and the request should be resubmitted.
Note that both the access and refresh tokens encode the URL of the World Modeler Server instance that issued them, and the IP address of the client that requested them. Therefore, the tokens are only valid for requests made from the specific client to the specific server instance.
The difference is due to the fact that authorization failures that occur when calling general API operations are handled by the ASP.NET Core framework on which World Modeler is based, while authentication failures during the auth/login and auth/refreshToken operations are handled by World Modeler itself (this difference may disappear in future versions of the World Modeler API).
Access control error codes
Another important aspect of security is access control, that is, ensuring that even if a user has a valid authorization token, they have been granted permission to access the requested resource at the requested level (e.g. create, read, update, delete, etc.). This is handled by World Modeler’s Access Control Manager. When an API call is made by a user who does not have permission to access the requested resource at the requested level, an HTTP 403 (Forbidden) error code is returned. This is shown in the example below.
Status: 403 (Forbidden)
Body:
{
"status": "Fail",
"value": null,
"message": "Access denied",
"requestTime": "0001-01-01T00:00:00",
"duration": "00:00:00"
}
Getting information about the current user
API users that have been granted privileges to access the user management functions have access to user records, including the one associated with the current session. However, for obvious security reasons, API users are rarely granted these privileges. However, there are still cases where an API consumer needs to know the details of their own user record. A specific API resource path is provided for this purpose:
user/getCurrent
Access to this operation can be assigned privileges that are distinct from the more general user management operations which are normally restricted. This allows access to user/getCurrent to be granted to most users, as it only reveals information about the user’s own account, while access to other user accounts is denied.
Status: 200 (Success)
Body:
{
"status": "Success",
"value": {
"id": "b8136fc7-2709-4a09-8e7e-2b791554942e",
"userName": "mj.user",
"name": "Jo User",
"email": "j.user@userdomain.com",
"userStatus": 0,
"roles": [
{
"id": "117a8fcf-a9b8-4083-8084-7f27910ea1b9",
"name": "Administrator",
"documentation": null
},
{
"id": "117a8fcf-a9b8-4083-8084-7f27910ea1b9",
"name": "User",
"documentation": null
}
]
},
"message": null,
"requestTime": "0001-01-01T00:00:00",
"duration": "00:00:00",
"url": "https://host.domain/user/getCurrent"
}