Skip to content

Security

Background

Open Data Capture is designed to be secure by design. Here, we provide a detailed overview of all major aspects of our security system, including authentication and authorization.

Assumptions

This security model is based on protecting the database from unauthorized users through network-based attacks. To this end, it is assumed that:

  1. The platform administrator is trusted
  2. The platform administrator understands this material
  3. The server is not compromised

Authentication

The API uses JSON Web Tokens (JWTs) to identify and authorize users. When a client sends a request to /auth/login, their password is compared to a hashed and salted version stored in our database. If the password is valid, a JWT is created with a payload that encodes various data about the user, including any relevant permissions. This payload is then signed using a private key stored in the environment variable SECRET_KEY and set to expire in 24 hours. The signed payload is returned to the client in the form of a JWT.

On requests to protected endpoints within the API, the client must present the JWT as proof of their identity. Following web standards, the access token is attached to each request header in the following format: Authorization: Bearer {TOKEN}. The server then verifies the integrity of the payload by checking the signature against the private key. If the signature is valid, the server can trust that the payload has not been tampered with and can use the data contained within it to determine whether the now authenticated user is authorized to perform certain actions. If the signature is invalid, or otherwise malformed, the server returns HTTP status 401.

Authorization

Once a user is authenticated, attribute-based access control is used to determine access to protected resources. In the source code, endpoints are assigned a given route access (either public or protected). If a route is protected, it is assigned an action (create, delete, manage, read, update) and subject, which corresponds to either a database model (e.g., User) or a special value all. For example, POST /v1/users may be associated with the action create and the subject User. If there is no route access explicitly defined for a given route, the default is manage and all.

When creating a user, a default set of permissions may be assigned based on the following roles:

  1. Admin
  2. Group Manager
  3. Standard User

If no role is specified, then the user is created with no permissions. In this case, an admin can apply additional, custom permissions to this user.

After validating the access token, the server checks whether the user has authorization to perform the selected action (create, read, update, or delete) on the subject (e.g., Group). These are specified on a per-route basis. If a route does not have any required permission set specified, the user must have permission to manage all resources to have access (i.e., the platform admin). If the user does not have adequate permissions, HTTP status 403 is returned.

User Roles

Admin

By default, an admin user has full access (i.e., create, read, update, and delete) to all resources. For security reasons, it is recommended to create only one admin, and elevate the privileges of group managers when required.

Group Manager

As a rule, group managers generally have permission to modify entities belonging to their group (e.g., users), and read data collected for their group (e.g., instrument records).

Standard User

By default, standard users are not able to access any data. They are only allowed to administer instruments, and download the record once complete (as it is stored in memory at that time).

Instrument Creation

Given that instruments can contain arbitrary JavaScript code, the security of the entire system relies on the assumption that only trusted users are able to create instruments. You should assume that anyone able to create instruments is able to execute arbitrary code on your server, with the permissions of the running process. If you are running Open Data Capture in a Docker container (as is recommended), this could lead to the compromise of your entire server (for details, read see this).