Skip to main content
Version: DeepHub 2024 R1 - 2.5.0

Security & Authorization

The DeepHub® uses the OpenID standard to ensure secure access to the API. This section describes how to apply permission settings to individual API endpoints, WebSocket topics, and OpenID users, and provides an example OpenID setup using Keycloak as the OpenID server. However, any standard OpenID server can be used. Refer to the Configuration section for security and authorization configuration options.

Enable Authorization

In order to enable authorization features, permissions, OpenID and server configurations need to be configured accordingly. The following section gives a quick overview of files and configuration options that need to be configured to enable authorization and security features.

Base Configuration

The base configuration can be done in the hub_config.yaml configuration file, via environment variables, or via command line arguments. See the "Configuration" section for a general overview of the different base configuration possibilities.

  • require_authorization must be set to true
  • openid_client_name must be set to verify audiences. This is mandatory for OpenID. Refer to the audience claim setup example below for more details.
  • verify_authserver set to true when running the server in production mode (requires authserver_public_key_path).
  • authserver_public_key_path should point to the public key of the OpenID server when running in production mode
  • force_https should be set to true in production mode to disable any insecure plain http or websocket communication
  • openid_config_url must be set to the standard OpenID configuration endpoint, e.g.
openid_config_url: http://127.0.0.1:8080/auth/realms/omlox/.well-known/openid-configuration

Server Certificates

When running the server in production mode, appropriate settings such as server certificates, public_key_path, private_key_path and dh_params_path must be set. Please refer to the "Configuration" section for details.

Authorization Flow Type

The DeepHub uses Bearer Authorization for the REST API. Clients connecting to the DeepHub will never send passwords directly to the DeepHub. Instead, clients login to the OpenID server and provide the bearer token with each request header to the DeepHub. The DeepHub is able to securely verify the token is valid and was issued by the configured OpenID server.

For service-to-service and client-to-service communication, we recommend using bearer authorization. For web clients we advise to use Authorization Code Flow for secure login.

Dedicated Permissions

The dedicated permissions can be configured within the permissions.yaml configuration file.

note

There is no other way to configure the permissions than via this file. Environment variables or command line arguments to configure these are not available.

This file contains the configuration of the authorization to access the REST API and the WebSocket API. It is explained in detail in the next section, directly below:

Applying Individual REST API Permissions

The DeepHub server uses a RBAC (Role Based Access Control) model to assign permissions to OpenID roles. The permissions are stored in a YAML based file in the data directory of the Docker container. The permissions are loaded once at startup.

General structure of the permissions file:

role-name:
description: Some role description
'/api/path':
- PERMISSION1
- PERMISSION2
'/api/path_2/*':
- PERMISSION1
'/api/path_3/:resource_id':
- PERMISSION1
...

Description of the core elements:

  • role-name must match an actual OpenID role name provided as part of the JWT Access Token.

  • /api/path must match an actual DeepHub API endpoint (refer to the OpenAPI file for a list of available endpoints).

  • The * symbol is a wildcard, allowing permissions to be given to all subpaths of an API. For example, /api/path_2/* will match all subpaths, such as /api/path_2/a, /api/path_2/b etc.

  • A path for a given role should only be specified once. Duplicates will produce an error.

  • Permission can be any of the following (multiple entries possible): CREATE_ANY, READ_ANY, UPDATE_ANY, DELETE_ANY, CREATE_OWN, READ_OWN, UPDATE_OWN, DELETE_OWN. Note that the respective ANY and OWN permissions are mutually exclusive, i.e., for a given path a role can have either READ_ANY or READ_OWN, but not both. Refer to the description below to choose the right permission.

  • :resource_id is a placeholder variable, matching identifiers given as part of a request. For example, the permission URL /v1/providers/:provider_id/location will match the HTTP GET request /v1/providers/provider123/location, and map the placeholder variable provider_id to the value "provider123". When the permission for the URL is one of CREATE_OWN, READ_OWN, UPDATE_OWN, DELETE_OWN, then the client is required to provide ownership proof with its access token. The structure of the required access token is described below.
    Important: The placeholder variables must match the names defined in the OpenAPI specification, e.g. it's required to use :provider_id for the route /v1/providers/:provider_id. The placeholder variable's name is used to match named resource identifiers provided with the access token, thus a mismatch will result in failed authorization.

Permission Types

CREATE_ANY
The role is allowed to create (e.g. via POST request) a resource at the given API path.

READ_ANY
The role is allowed to read all resource objects at a given API path.

UPDATE_ANY
The role is allowed to update (e.g. via PUT request) resources at a given API path.

DELETE_ANY
The role is allowed to delete all resources for a given API path.

CREATE_OWN, READ_OWN, UPDATE_OWN, DELETE_OWN
The role is only allowed to read or mutate it's own resources. Resource ownership is defined via JWT AccessToken. A particular user which owns a resource must have a matching resource UUID in the Access Token.

Permissions Example

In the example below, a role named omlox-api-role is provided full admin access to all API endpoints using a wildcard match:

omlox-api-role:
description: Super user with all permissions
'/v1/*':
- CREATE_ANY
- READ_ANY
- UPDATE_ANY
- DELETE_ANY

In this example, a user is given access restricted to their own location data:

omlox-api-role:
description: A user with access to only their own location data
'/v1/providers/:provider_id/*':
- CREATE_OWN
- READ_OWN
- UPDATE_OWN
- DELETE_OWN

Applying Permissions to Websocket

Permissions to websocket topics follow the same rules as the permissions for the REST API. The url paths use the following scheme: /v1/ws/socket/{topic}/:resource_id

In the scheme above, {topic} is a placeholder for the actual websocket topic name, e.g. "location_updates", and :resource_id is a placeholder for the identifier name which must match the data for this topic using the same rules and naming scheme as stated for the REST API.

For example, a permission to only access location data owned by the user that matches a provider_id given as part of the access token:

read-only-own-resources-role:
'/v1/ws/socket/location_updates/:provider_id':
- READ_OWN

Wildcards can be applied in the same way as the REST API. For example, /v1/ws/socket/location_updates/* would match location updates for all location providers. To subscribe to a topic, either READ_ANY or READ_OWN permissions are required. The limitations for READ_OWN permissions are detailed in the following section.

Required Ownership for Topics

All topics require ownership for certain resources in order for subscriptions to work if the topic's permissions are READ_OWN. The following lists the topics and what ownerships are required to receive messages.

TopicOwnership Needed
location_updatesThe Provider as identified by the event's field provider_id
collision_eventsThe Trackable as identified by the event's field collision_id_1
fence_eventsThe Fence as identified by the event's field fence_id
trackable_motionsThe Trackable as identified by the event's field id
change_eventsThe topic specific Entity as identified by the event's field id
note

The ownership for additional resources (e.g. the second Trackable of a collision) are currently not checked for subscribed topics.

Sending Location Updates

To send location updates via Websocket no subscription is required. However, the permissions for the respective topic must be CREATE_ANY or UPDATE_ANY. Finer grained permissions based on ownership are currently not supported for sending location updates via websocket.

Access Token Extension for Ownership Claims

In order to allow access to ownership restricted URLs, the access token must contain an object member named https://deephub.io/owned_resources containing named lists to resource identifiers. The member names must adhere to the following scheme:

placeholder_variable + 's'

In other words, the name of the members is the plural of the placeholder variable name. As an example, the json content of an access token looks like this:

{
"https://deephub.io/owned_resources": {
"provider_ids": [
"provider123"
],
"trackable_ids": [
"trackable123"
],
"zone_ids": [
"zone123"
],
"fence_ids": [
"fence123"
]
},
...
}

Example OpenID setup with Keycloak

An OpenID server is required in order to enable authorization features in DeepHub. Below is an example using Keycloak, though the basic setup should work similar for other OpenID servers.

Prerequisites: A running Keycloak instance. You can start with a simple standalone instance for development and testing. See the Keycloak guide to get started. Alternatively, you can download and use our "deephub-advanced-setup" example from github.

Log in to the Keycloak Admin Console of your Keycloak instance and then follow this procedure:

At first we have to create a new realm; let's call it "omlox" in this example:

  • From the Master drop-down menu, click Add Realm.
  • Type omlox in the Name field and click Create.

Next we create the role "omlox-api-role" for DeepHub API access. To do so:

  • From the menu, click Roles to open the roles list.
  • Click on Add Role on the right side of the list
  • Enter omlox-api-role in the Role Name field. The role name must not be changed, because this role name is mapped to omlox permissions.

Next we create a user named "omlox-api-user". To create a new user in the omlox realm, complete the following steps:

  • Make sure the omlox Realm is the selected current Realm in the Master drop-down menu
  • From the menu, click Users to open the user list page.
  • On the right side of the user list, click Add User to open the add user page.
  • Enter the name omlox-api-user in the Username field. Flip the Email Verified switch from Off to On and click Save to save the data and open the management page for the new user.
  • Click the Credentials tab and enter a password for the new user. Flip the Temporary switch to Off to make the new password permanent. Click Set Password to save the changes.
  • Click the Role Mappings tab. In the Realm Roles list, select omlox-api-role and click the Add selected button.

Next we create a client named "omlox-api-client". To create a new user in the omlox realm, complete the following steps:

  • Make sure the omlox realm is the selected current realm in the Master drop-down menu
  • From the menu, click Clients to open the client list page.
  • On the right side of the client list, click Create to open the create client page.
  • Enter the name omlox-api-client in the Client ID field. Select openid-connect in the Client Protocol dropdown.
  • Select Public as Access Type
  • Toggle Service Accounts Enabled. This enables Client Credentials Grant workflow for the DeepHub to fetch access tokens for token verification of client requests.
  • Click Save.

Next, create another client to be used by the DeepHub service itself. Note: This client is mandatory even in case no other OpenID exists to communicate with the DeepHub, and the client name must match with the openid_client_name in the DeepHub configuration. All other OpenID clients which communicate with the DeepHub have to include this client in the JWT as audience, so that the DeepHub can verify audiences as mandated by OpenID.

  • Repeat the previous step to create a client.
  • Name deephub-service. Note: The name is case-sensitive and must match the one in the configuration file.
  • Set the Access Type to bearer-only.
  • Click Save.

For security reasons an OpenID enabled service has to verify audience claims in order to avoid token misuse. Follow these steps:

  • From the meu, click Client Scopes to open the client scopes list.
  • Click Create to create a new client scope.
  • Enter hub-service into the Name field.
  • Click the Save button to create the new client scope.
  • In the list of client scopes, select the newly created hub-service.
  • Select Mappers from the top menu.
  • Click the Create button on the right side.
  • Enter hub-service-audience into the Name field.
  • From the Mapper Type selection choose Audience.
  • In the Included Client Audience select deephub-service.
  • Click Save button to save the changes.
  • Now we create a mapping between the newly created scope and the client used for user logins. From the left menu panel, choose Clients and choose omlox-api-client.
  • From either the Default Client Scopes or Optional Client Scopes list select hub-service and click on Add selected. You can only add it to either default or optional. The difference is that when you add it to the default list the token will always contain the audience hub-service. When you set it to optional the user client (e.g. web application) must explicitly set the scope to hub-service when requesting the token, the token will then contain the audience deephub-service. It’s advised to use the optional mapper and limit scopes appropriately on the user client side.

Optional steps It’s possible to define fine-grained ownership permissions by adding custom attributes to a user. This allows for assigning read_own permissions to API paths which are restricted to users that own a particular resource. Follow these steps:

  • From the left menu, click Clients.
  • From the clients list, select omlox-api-client. From the top menu select Mappers.
  • Click on the Create button to create a new mapper.
  • Set a name like Assigned Location Providers.
  • In the Mapper Type selection, select User Attribute.
  • In the User Attribute field, set https://deephub.io/owned_resources/provider_ids (Important: This field must match with the one we set in the User attributes for the particular user.
  • In the Token Claim Name field, set https://deephub\.io/owned_resources.provider_ids (without quotes). It’s important to use this exact name, as the DeepHub will check if a user has access to particular resources using this name. Also note the \ and . in this name. This will actually create a json member named https://deephub.io/owned_resources with an object named provider_ids.
  • From the Claim JSON Type selection, select String.
  • Enable Multivalued.
  • Click Save

Repeat the above steps to create respective mappers for fences, trackables and zones as well, using the following values.

For fence ownership mapping:

  • Set User Attribute to: https://deephub.io/owned_resources/fence_ids
  • Set Token Claim Name to: https://deephub\.io/owned_resources.fence_ids

For trackable ownership mapping:

  • Set User Attribute to: https://deephub.io/owned_resources/trackable_ids
  • Set Token Claim Name to: https://deephub\.io/owned_resources.trackable_ids

For zone ownership mapping:

  • Set User Attribute to: https://deephub.io/owned_resources/zone_ids
  • Set Token Claim Name to: https://deephub\.io/owned_resources.zone_ids

For source ownership mapping:

  • Set User Attribute to: https://deephub.io/owned_resources/source_ids
  • Set Token Claim Name to: https://deephub\.io/owned_resources.source_ids

Now we can create actual user attributes which will then be mapped at runtime. To assign a Location Provider to a user, follow these steps:

  • In the left menu bar, click Users
  • Choose a user from the list to whom you want to assign a trackable
  • Click Attributes.
  • In the attributes key / value list, set key to https://deephub.io/owned_resources/provider_ids and the value to the ID of the Location Provider you want to assign to the user (e.g. 5896823C-3B3F-48BB-B74B-E43041ABD59C).

Repeat the above steps for fences, trackables, zones, and sources using the respective keys below:

  • https://deephub.io/owned_resources/fence_ids
  • https://deephub.io/owned_resources/trackable_ids
  • https://deephub.io/owned_resources/zone_ids
  • https://deephub.io/owned_resources/source_ids

Note: You can set multiple entries in the value field using "##" as delimiter between entries. For example:

providerid1##providerid2

This will show as two items in the provider_ids array.

Further note: source_ids refer to the source given in a location update. This source can reference a zone, but doesn't have to. As such, source_ids should contain all zone ids in addition to other source ids. At this point some double book keeping is required.