Skip to content

Authorization Model

The Voltimax IoT Platform uses Keycloak's resource-based authorization model. This page explains how resources, scopes, policies, and permissions are structured to control access.

Overview

Authorization decisions follow a layered model:

  1. A user is assigned one or more client roles
  2. Policies reference roles to define access rules
  3. Permissions bind a resource + scope to one or more policies
  4. When an API endpoint requires access to a resource with a specific scope, Keycloak evaluates the relevant permission and its associated policies

Resources

Resources represent domain entities in the platform. Each resource has a name, a URN-style type identifier, and a set of applicable scopes (e.g. read, write, delete). Not all resources support all scopes - for example, solar-forecast only supports read because forecast data is sourced externally.

Resources are defined in KeycloakRealmConfiguration.GetAuthorizationResources(). The current set of resources and their scopes can be inspected in that file or via the Keycloak admin console under the iot-platform-api client's Authorization tab.

Policies

Policies are the rules Keycloak evaluates to determine access. The platform uses role-based policies - each policy checks whether the authenticated user holds a specific client role (e.g. platform-admin-policy checks for the platform-admin role).

Each policy is of type role and uses UNANIMOUS decision strategy, meaning the role must be present for the policy to grant access. Policies are defined in KeycloakRealmConfiguration.GetAuthorizationPolicies().

Policy representation

In Keycloak's model, policies are configured with their role reference in the Config.AdditionalData["roles"] property as a JSON-serialized array:

json
[{ "id": "iot-platform-api/platform-admin", "required": true }]

The role ID format is {clientId}/{roleName}, and required: true means the role must be directly assigned (not inherited).

Permissions

Permissions link resources and scopes to policies. They are of type scope and use AFFIRMATIVE decision strategy - if any associated policy grants access, the permission is satisfied.

A permission is a resource:scope pair. In code, it's represented as:

csharp
new Permission("gateways", "read") // PolicyName = "gateways:read"

In Keycloak, this maps to a scope permission named gateways-read that binds the gateways resource with the read scope to one or more role-based policies.

All permission constants are defined in Permissions.cs and can be enumerated via Permissions.All. The complete set of permission-to-role mappings is visible in the Keycloak admin console or in KeycloakRealmConfiguration.

Decision Strategies

Two decision strategies are used:

  • UNANIMOUS (role policies): All conditions must pass. Since each role policy checks a single role, this simply means "the user must have this role."
  • AFFIRMATIVE (scope permissions): At least one associated policy must grant access. This means a user with any of the listed roles gets access.

Source Files

The authorization model is implemented across two projects:

  • Voltimax.Platform.Api/Core/Authorization/ - Permission type, Permissions constants, RequirePermission() extensions, and authorization setup
  • Voltimax.MigrationService/Keycloak/ - KeycloakRealmConfiguration (resource/scope/policy definitions) and KeycloakAuthorizationManager (Admin API import)

Adding New Resources

See Provisioning for the end-to-end workflow for adding a new protected resource and permission.