Overview

Support for identity provider principals in the Empirum REST API does not work out of the box for on-premises customers. In addition to managing identity provider principals in the Admin UI, as described in Empirum REST API Technical Reference and Implementation, the configuration steps below are required.

If any of these steps is missing, an otherwise valid token is rejected with 403 Forbidden.

 

Required Configuration

Step What to do
1. Azure app registration Register an API application and a client application in Microsoft Entra ID. On the API application, configure Expose an API.
2. identity-providers.json Add a matching entry for the identity provider to identity-providers.json on the REST API side.
3. Role assignment Assign roles to the identity provider principal separately in the Permission Management web app.

Roles are not assigned through Entra App Roles. Role assignments configured in Entra ID are not evaluated by the Empirum REST API.

 

Step 1: Azure app registration

Two Azure (Entra) app registrations are involved:

  • One for the API itself: registered once, represents the Empirum REST API as a protected resource ("audience") in your tenant.
  • One per client application: registered once per application or script that will call the API. Each client is either a Service Client (unattended, uses a client secret) or a Public Client (interactive, a human signs in).

Information to collect

Value Where to find it Used for
Directory (tenant) ID App registration → Overview identity-providers.json → authority
API app registration's Application (client) ID API app registration → Overview identity-providers.json → audience
Client app registration's Application (client) ID Each client app registration → Overview Token request (client_id)
Client secret (Service Client only) Client app registration → Certificates & secrets Token request (client_secret)
Object ID of the calling identity Service principal (Service Client) or user (Public Client) → Overview Registered in Empirum as a Client/User principal

Register the API

Do this once per Empirum instance.

  1. In the Azure Portal, go to Microsoft Entra ID → App registrations → New registration.
  2. Name it, e.g. Matrix42 Empirum REST API. Leave the redirect URI empty. Click Register.
  3. On the Overview page, copy:
    • Application (client) ID: this becomes the audience for your instance.
    • Directory (tenant) ID: this becomes part of the authority URL.
  4. Go to Expose an API → click Add next to "Application ID URI" → accept the default (api://<Application-ID>) or set a custom URI → Save.

The Expose an API step is required. Without it, client token requests using scope=<api-client-id>/.default fail, because the API is not yet a valid audience in your tenant.

 

Register a client application

Repeat this once per application or script that should call the API. Pick the variant that matches how the application runs:

  Service Client Public Client
Flow Client Credentials Device Code / Authorization Code
Who authenticates The application itself A human user, interactively
Credentials Client ID + Client Secret None (browser sign-in / device code)
Typical use case Scheduled scripts, services, CI/CD Admin tools, interactive PowerShell sessions

Service Client (unattended scripts and services)

  1. Go to App registrations → New registration → name it, e.g. Matrix42 Empirum PowerShell Client → Register.
  2. Copy its Application (client) ID.
  3. Go to Certificates & secrets → New client secret → copy the generated value immediately.
  4. On the Overview page, note the Object ID of the application's service principal (listed under "Managed application in local directory"). You register this value in Empirum in the role assignment step.

The client secret value is shown only once. Store it in a password manager or your application's secret store before leaving the page.

 

No API permission needs to be granted to this app registration in the Azure Portal. The client simply requests scope=<api-client-id>/.default, and Empirum resolves what it is allowed to do from its own role assignment, not from Entra.

 

Public Client (interactive sign-in)

  1. Go to App registrations → New registration → name it, e.g. Matrix42 Empirum Interactive Client → Register.
  2. Copy its Application (client) ID.
  3. Go to Authentication → Add a platform:
    • For Device Code: choose Mobile and desktop applications, select http://localhost as redirect URI, and enable Allow public client flows.
    • For Authorization Code: choose Web and set the redirect URI to http://localhost.

No client secret is needed. Each user who signs in authenticates with their own Entra account. Their Object ID (visible under their user profile in Entra, or captured automatically on first interactive sign-in) is what gets registered in Empirum.

Step 2: identity-providers.json

Make this change in the DBUtil installation directory, then (re-)install the REST API service through DBUtil. Do not edit the files directly in the running service's installation folder.

Installing or updating the REST API service through DBUtil replaces the entire service directory. Any change to appsettings.json or Data/identity-providers.json made directly in the installed service folder is lost the next time DBUtil (re-)installs the service. Always make the change in the DBUtil installation directory first, then let DBUtil (re-)install the service so the change is copied into place correctly.

 
  1. On the Empirum server, open the folder that DBUtil uses to install the REST API service (next to Setup.inf, alongside the REST API's appsettings.json and Data\identity-providers.json files).
  2. Copy identity-providers.local.json.example to identity-providers.local.json in the same folder and fill in the placeholders (see the example and field table below).
  3. Optional: if you also want a Sign in with Entra ID button on the Permission Management login page (/admin), copy appsettings.local.json.example to appsettings.local.json in the same folder and fill in the matching placeholders there as well.
  4. Run the REST API service (re-)installation through DBUtil so the updated configuration is picked up.
  5. Confirm the identity provider is active: sign in to /admin (if you enabled the Entra sign-in button in step 3), or request a token as shown in Obtaining a token and calling the API.

Example identity-providers.local.json:

{
  "providers": [
    {
      "name": "contoso-entra",
      "authority": "https://login.microsoftonline.com/<tenant-id>/v2.0",
      "audience": "<api-app-registration-client-id>"
    }
  ]
}
Field Value
authority https://login.microsoftonline.com/<Directory (tenant) ID>/v2.0, using the tenant ID from the app registration step
audience Application (client) ID of the API app registration from the app registration step
name Any label to recognize this provider later; not security-relevant

Step 3: Role assignment

Entra only authenticates the caller; it does not grant any roles. Once the identity provider is configured, the identity from the app registration step can obtain a token, but every API call still returns 403 Forbidden until it is assigned roles on the Empirum side.

  1. Sign in to Permission Management (/admin) with an account that already has admin.manage (e.g. the ADMIN Local Principal created during installation).
  2. Under Local Principals (the same page also lists Entra-backed principals once they exist), find or add the identity:
    • Service Client: use the PermissionManager tool, or wait for its first API call, which auto-creates a zero-role row. Assign roles to the Object ID of its service principal.
    • Public Client / interactive user: either let the user sign in once (a zero-role row is created automatically on first token use), or use the EntraRegistration tool, which signs the user in interactively and registers their Object ID automatically.
  3. Assign only the roles the application actually needs (least privilege). See the role reference in Cloud Permission Management & Local Principals → Choosing roles.

Until roles are assigned, requests authenticate successfully (200 on the token request) but every API call fails with 403. This is expected and not a configuration error.

 

Obtaining a token and calling the API

In both flows, scope must be <api-app-id>/.default. This is the standard way to request the API's audience as a scope.

 

Service Client (Client Credentials Flow)

$body = @{
    grant_type    = 'client_credentials'
    client_id     = '<client-app-id>'
    client_secret = '<client-secret>'
    scope         = '<api-app-id>/.default'
}

$response = Invoke-RestMethod -Method Post `
    -Uri 'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token' `
    -Body $body

$token = $response.access_token

Invoke-RestMethod -Uri 'http://localhost:5057/api/computer' `
    -Headers @{ Authorization = "Bearer $token"; 'api-version' = '1' }

Public Client (Device Code Flow)

Interactive, no browser automation needed. Requires the MSAL.PS module (Install-Module MSAL.PS).

$token = Get-MsalToken `
    -ClientId '<public-client-app-id>' `
    -TenantId '<tenant-id>' `
    -Scopes '<api-app-id>/.default' `
    -DeviceCode

Invoke-RestMethod -Uri 'http://localhost:5057/api/computer' `
    -Headers @{ Authorization = "Bearer $($token.AccessToken)"; 'api-version' = '1' }

You are prompted with a code and the URL https://microsoft.com/devicelogin. Sign in there with the client's user account.

Related Articles