- 0 minutes to read

Call the Nodinite Web API with OAuth 2.0 Client Credentials

When Nodinite runs in OpenID Connect / OAuth 2.0 mode, every Web API request needs a bearer access token. This guide shows how to let another application call the Web API without a signed-in user: an integration, a background service, a scheduled job, or a CI/CD pipeline.

The calling application uses the OAuth 2.0 client credentials flow: it proves who it is with its own client ID and secret (or certificate), gets an access token from your identity provider, and sends that token with each request.

The steps use Microsoft Entra ID (formerly Azure AD). For Keycloak, Okta, and other providers, see Other identity providers.

Tip

The Web API is deterministic: the same request with the same permissions returns the same result every time. That makes it the right choice for scripts and pipelines. To ask questions about your integration landscape in natural language from VS Code, Claude Desktop, or similar tools, use the MCP Endpoint instead. See Web API or MCP Endpoint?.

Understanding the Client Credentials Flow

sequenceDiagram participant C as Client application participant IDP as Identity provider (Entra ID) participant API as Nodinite Web API C->>IDP: 1. Request access token (client ID + secret) IDP-->>C: 2. Access token with app role claim C->>API: 3. HTTP request + Authorization: Bearer token API->>API: 4. Map role claim to Claim, Policy, Role API-->>C: 5. Response, limited by the Nodinite Role

The client gets a token from the identity provider and sends it to the Web API. Nodinite maps the role claim in the token to a Nodinite Role, which decides what the client can see and do.

Key Components

Component Where Role in the flow
Client app registration Entra ID The identity of the calling system. Holds the client ID and the secret or certificate. One per calling system.
Nodinite Web API app registration Entra ID The protected API, created during installation. Defines the app roles that clients can be granted.
App role (for example Nodinite.Integration) Entra ID Sent in the token as a roles claim. Tells Nodinite which kind of client is calling.
Claim → Policy → Role Nodinite Web Client Maps the app role to a Nodinite Role with exactly the permissions the client needs.
Access token Runtime Short-lived (about one hour). Sent as Authorization: Bearer <token>.

Dedicated Client vs Installation Client

The installation guide creates a NodiniteInstallationClient with the AppRole_WebApi_all app role. Don't reuse it for your own scripts.

Dedicated client (this guide) Installation client
Purpose Your script, service, or pipeline Nodinite installation and internal services
Permissions Only what the client needs, through its own Nodinite Role Full access
Revoke or rotate On its own, without affecting Nodinite Affects the Nodinite installation
Audit Each calling system has its own identity in Log Audits All callers look the same

Prerequisites

  • Nodinite is installed in OpenID/OAuth mode, and users can sign in to the Web Client. See Install Nodinite v7 - OpenID.
  • You know these values for your environment:
    • The Tenant ID of your Entra ID tenant.
    • The Application ID URI of the Nodinite Web API app registration, for example api://nodinite-prod-webapi.
    • The Web API base URL of your environment: scheme, host, and port of the environment's Web API site, for example https://nodinite.contoso.com:40001. The port is set for each environment in the Nodinite Portal (Servers step). To check it, add /scalar and open it in your browser. See Find the base URL.
  • You can create app registrations and grant admin consent in Entra ID.
  • You are a Nodinite administrator.

Step 1: Register the Client App

  1. In the Microsoft Entra admin center, go to Identity → Applications → App registrations → New registration.
  2. Name: a descriptive name, for example nodinite-prod-client-<system>. Use one registration per calling system, so that you can audit and revoke each one on its own.
  3. Supported account types: Accounts in this organizational directory only.
  4. Redirect URI: leave it empty.
  5. Select Register and note the Application (client) ID.
  6. Open Certificates & secrets and create a client secret, or upload a certificate.
  7. Store the secret in a secret store, such as Azure Key Vault. Never put it in source code. Note the expiry date: when the secret expires, the client stops working.

Step 2: Give the Client Permission to Call the Web API

Add an App Role to the Nodinite Web API

Skip this if a suitable app role already exists.

  1. Open the Nodinite Web API app registration → App roles → Create app role.
  2. Fill in the fields:
    • Display name: Nodinite Integration
    • Allowed member types: Applications
    • Value: Nodinite.Integration. You use this value again in Step 3.
    • Description: Applications calling the Nodinite Web API
  3. Select Apply.

Tip

Create one app role per access level, for example Nodinite.Integration.Read and Nodinite.Integration.Repository, when different clients need different permissions in Nodinite.

Assign the App Role to the Client

  1. Open the client app registration → API permissions → Add a permission → My APIs, and select the Nodinite Web API.
  2. Choose Application permissions and select Nodinite.Integration.
  3. Select Add permissions, then Grant admin consent for <tenant>.

Step 3: Give the Client Access in Nodinite

The app role from Step 2 is what Nodinite uses to decide what the client can access. Map it to a Nodinite Role:

  1. In the Nodinite Web Client, go to Administration → Access Management → Claims and create a Claim:
    • Key: http://schemas.microsoft.com/ws/2008/06/identity/claims/role
    • Value: Nodinite.Integration. Use the exact Value from Step 2.
  2. Go to Administration → Access Management → Policies, create a Policy (for example Integration clients), and add the Claim to it.
  3. Go to Administration → Access Management → Roles. Add the Policy to a Role that has only the permissions the client needs, for example read access to specific Monitor Views. Create a new Role if necessary.

Warning

Don't add clients to the Administrators Role unless they really need full administrative access.

Note

Changes to Claims, Policies, and Roles can take up to about an hour to apply to a client that is already connected. To test a change, request a new token.

Step 4: Get a Token and Call the Web API

Replace these placeholders in the examples:

Placeholder Value
<tenant-id> Your Entra ID Tenant ID
<client-id> The Application (client) ID from Step 1
<client-secret> The client secret from Step 1. Read it from a vault or environment variable at run time.
<api-app-id-uri> The Nodinite Web API's Application ID URI, for example api://nodinite-prod-webapi
<webapi-base-url> The Web API base URL, for example https://nodinite.contoso.com:40001

The token request always uses the scope <api-app-id-uri>/.default. Each example calls api/Users/whoami, which returns the identity of the caller and its Nodinite Roles. Once that works, call any operation you find in Swagger (see Manage the Web API).

curl

TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" \
  -d "grant_type=client_credentials" \
  -d "client_id=<client-id>" \
  -d "client_secret=<client-secret>" \
  -d "scope=<api-app-id-uri>/.default" | jq -r .access_token)

curl -H "Authorization: Bearer $TOKEN" "<webapi-base-url>/api/Users/whoami"

PowerShell

$body = @{
    grant_type    = 'client_credentials'
    client_id     = '<client-id>'
    client_secret = '<client-secret>'
    scope         = '<api-app-id-uri>/.default'
}
$token = (Invoke-RestMethod -Method Post `
    -Uri 'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token' `
    -Body $body).access_token

Invoke-RestMethod -Uri '<webapi-base-url>/api/Users/whoami' `
    -Headers @{ Authorization = "Bearer $token" }

C# (.NET) with MSAL

Package: Microsoft.Identity.Client.

using System.Net.Http.Headers;
using Microsoft.Identity.Client;

var app = ConfidentialClientApplicationBuilder
    .Create("<client-id>")
    .WithClientSecret("<client-secret>")          // or .WithCertificate(cert)
    .WithAuthority("https://login.microsoftonline.com/<tenant-id>")
    .Build();

// MSAL caches the token and renews it when needed. Keep 'app' as a singleton.
var result = await app
    .AcquireTokenForClient(["<api-app-id-uri>/.default"])
    .ExecuteAsync();

using var http = new HttpClient { BaseAddress = new Uri("<webapi-base-url>/") };
http.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", result.AccessToken);

var whoAmI = await http.GetStringAsync("api/Users/whoami");

Python

Install the dependency first: pip install requests.

import os
import requests

token_response = requests.post(
    "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token",
    data={
        "grant_type": "client_credentials",
        "client_id": "<client-id>",
        "client_secret": os.environ["NODINITE_CLIENT_SECRET"],
        "scope": "<api-app-id-uri>/.default",
    },
    timeout=30,
)
token_response.raise_for_status()
access_token = token_response.json()["access_token"]

response = requests.get(
    "<webapi-base-url>/api/Users/whoami",
    headers={"Authorization": f"Bearer {access_token}"},
    timeout=30,
)
response.raise_for_status()
print(response.json())

Node.js

Requires Node.js 18 or later (built-in fetch).

const tokenResponse = await fetch(
  'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token',
  {
    method: 'POST',
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: '<client-id>',
      client_secret: process.env.NODINITE_CLIENT_SECRET,
      scope: '<api-app-id-uri>/.default'
    })
  }
);
const { access_token } = await tokenResponse.json();

const response = await fetch('<webapi-base-url>/api/Users/whoami', {
  headers: { Authorization: `Bearer ${access_token}` }
});
console.log(await response.json());

Token Lifetime

Tokens expire after about an hour. Request a new token before it expires, or when you get a 401 response. MSAL does this for you; in the other examples, request a new token at the start of each run, or cache it with its expiry time.

Step 5: Verify

Call GET <webapi-base-url>/api/Users/whoami with the token. The response shows the client and the Roles it has in Nodinite. To see the response model, open the operation in Scalar at <webapi-base-url>/scalar.

The response looks like this (shortened). This example is from a Windows user in the Administrators Role; your client shows its own identity and Roles:

{
  "Collection": {
    "Version": "7.10.4.0",
    "Href": "https://nodinite.contoso.com:40001/api/Users/current",
    "Items": [
      {
        "Href": "https://nodinite.contoso.com:40001/api/Users/12",
        "Data": {
          "UserId": 12,
          "Domain": "DEV",
          "Name": "Administrator",
          "Roles": [
            {
              "RoleId": 0,
              "Name": "Administrators",
              "IsActive": true,
              "IsDeleted": false
            }
          ],
          "IsActive": true,
          "IsDeleted": false
        }
      }
    ],
    "Error": null
  }
}

Check Collection.Items[0].Data.Roles: it lists the Nodinite Roles the client has. To print only the Role names in PowerShell:

$me = Invoke-RestMethod -Uri '<webapi-base-url>/api/Users/whoami' -Headers @{ Authorization = "Bearer $token" }
$me.Collection.Items[0].Data.Roles.Name
Result Meaning
200 and the expected Roles The setup is complete.
200 but no Roles The client is connected but has no access. Check Step 3.
401 The token was not accepted. See Troubleshooting.

Troubleshooting

Symptom Fix
401 from the Web API Check that the token was requested with <api-app-id-uri>/.default for the Nodinite Web API, and that the token has not expired.
Token request fails: invalid client secret Check that you used the secret Value, not the secret ID, and that the secret has not expired.
Token request fails: consent required Grant admin consent on the client's API permissions page (Step 2).
whoami shows no Roles Check that the Claim value matches the app role value exactly, and that the Policy is added to a Role (Step 3). Then request a new token.

For token validation errors on the Nodinite side, see Troubleshooting OAuth2 Validation Failures and the Web API Troubleshooting guide.

Other Identity Providers

Nodinite works with any OpenID Connect identity provider, for example Keycloak or Okta. The steps are the same:

  1. Create a confidential client that can use the client credentials grant.
  2. Configure the provider so that the client's tokens are issued for the Nodinite Web API audience (in Keycloak, use an audience mapper), and include a role or group claim.
  3. Map that claim to a Nodinite Policy and Role, as in Step 3.
  4. Request the token from the provider's token endpoint and send it as Authorization: Bearer <token>.

Security Checklist

  • One app registration per calling system, with an owner and a description.
  • Certificates, or secrets kept in a vault, with a reminder before they expire.
  • The client's Nodinite Role has only the permissions it needs, and is never Administrators by default.
  • Tokens and secrets are never logged.
  • Always call the Web API over HTTPS. See Hardening.

Next Steps