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
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/scalarand 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
- In the Microsoft Entra admin center, go to Identity → Applications → App registrations → New registration.
- 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. - Supported account types: Accounts in this organizational directory only.
- Redirect URI: leave it empty.
- Select Register and note the Application (client) ID.
- Open Certificates & secrets and create a client secret, or upload a certificate.
- 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.
- Open the Nodinite Web API app registration → App roles → Create app role.
- 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
- Display name:
- Select Apply.
Tip
Create one app role per access level, for example
Nodinite.Integration.ReadandNodinite.Integration.Repository, when different clients need different permissions in Nodinite.
Assign the App Role to the Client
- Open the client app registration → API permissions → Add a permission → My APIs, and select the Nodinite Web API.
- Choose Application permissions and select
Nodinite.Integration. - 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:
- 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.
- Key:
- Go to Administration → Access Management → Policies, create a Policy (for example
Integration clients), and add the Claim to it. - 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:
- Create a confidential client that can use the client credentials grant.
- 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.
- Map that claim to a Nodinite Policy and Role, as in Step 3.
- 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.