Entra ID single sign-on
DTM supports Microsoft Entra ID single sign-on over OIDC. With it configured, users click Sign in with Microsoft in the web UI and authenticate against your tenant. DTM validates the sign-in and derives the user’s role from an app role you assign in Entra.
The sign-in uses the authorization-code flow with PKCE and validates the
identity token against your tenant’s published signing keys. The API example
below sends an admin bearer token as $DTM_TOKEN; the
API overview shows how to capture one.
Role mapping
Section titled “Role mapping”DTM reads the roles claim from the identity token:
- Users assigned the Admin app role get the admin role in DTM.
- Everyone else who can sign in gets read-only. DTM does not limit which accounts in your tenant can sign in, so unless you turn on Assignment required (step 8 below), that is every member and guest in the tenant.
Manage fine-grained editor access with local accounts, or assign the Admin app role to those who need full control.
If you set an SSO user’s role manually (over the API or UI), later sign-ins keep that manual role with one exception: when Entra proposes a lower role, the downgrade is honored and the manual role clears, so revoking someone’s Entra app role reliably demotes them. An Entra-proposed higher role never silently escalates a manually set one.
How SSO and local accounts coexist
Section titled “How SSO and local accounts coexist”Local accounts and Entra identities are separate, disjoint namespaces. Enabling SSO changes nothing about existing local accounts, in either direction:
- Mixed sign-in is the model. With SSO configured, the sign-in page offers both the Microsoft button and the local username/password form. Local accounts keep working, and there is deliberately no switch to turn local sign-in off: an Entra outage or misconfiguration must never lock every admin out.
- An Entra user is its own account. The first Microsoft sign-in creates a
DTM account keyed to the Entra user’s immutable subject, named with an
entra:prefix (for exampleentra:[email protected]) and holding no local password. It shows up in the user list like any other account and can be disabled or deleted there; a disabled Entra user is refused at the Microsoft sign-in too. - Same name, no collision. A local
aliceand an Entraentra:[email protected]are unrelated accounts: neither inherits the other’s role or sessions, and the local password only ever signs in to the local account. Local usernames cannot contain:, so a local account can never occupy theentra:namespace. And because the Entra account is keyed to the immutable subject rather than the name, a UPN your tenant later reassigns to a different person maps to a new DTM account instead of inheriting the old one. - Order does not matter. Creating a local account that happens to match an Entra UPN, before or after enabling SSO, is allowed and has no effect on the Entra identity.
- Turning SSO off later. Entra-created accounts hold no password, so they can no longer sign in; the accounts and their audit history remain, and the same people map back to the same accounts if you re-enable SSO. Local accounts are unaffected throughout.
- Sessions. Enabling or disabling SSO does not revoke tokens that are already issued; they run out their 24-hour lifetime. Disabling or deleting any account, local or Entra, cuts its tokens off immediately, because every request re-checks the account.
Register the application in Entra
Section titled “Register the application in Entra”- In the Azure portal, go to Microsoft Entra ID → App registrations → New registration.
- Give it a name (for example
Hayami DTM). - Add a redirect URI of type Web pointing at DTM’s callback endpoint,
for example
https://dtm.example.com/api/v1/auth/entra/callback. - Note the Application (client) ID and Directory (tenant) ID.
- Under Certificates & secrets, create a client secret and copy its value.
- Under App roles, create an app role whose Value is exactly
Admin(allowed member type: Users/Groups). DTM reads the role’s value from the sign-in token, not its display name, and the match is case-sensitive: a value ofadminorDTM.Admingives read-only access. The display name can be anything. - Under Enterprise applications → your app → Users and groups, assign the Admin app role to the users or groups who should administer DTM.
- Under Enterprise applications → your app → Properties, set
Assignment required? to Yes, so only the users and groups assigned to
the app can sign in. Read-only users then need an assignment too: create a
second app role for them (for example with the value
Reader) and assign it. DTM gives read-only access to any role value other thanAdmin.
Configure DTM
Section titled “Configure DTM”Provide the tenant ID, client ID, client secret, and redirect URI to DTM. You
can do this from the Settings area of the UI, with the dtm_sso_config
Terraform resource (the client secret is a write-only argument that never
enters state; see
users and SSO as code), or over
the API (admin role):
curl -sk -X PUT https://dtm.internal:8443/api/v1/auth/entra/config \ -H "Authorization: Bearer $DTM_TOKEN" \ -H "X-DTM-CSRF: 1" \ -H "Content-Type: application/json" \ -d '{ "tenant_id": "<tenant-id>", "client_id": "<client-id>", "client_secret": "<client-secret>", "redirect_uri": "https://dtm.example.com/api/v1/auth/entra/callback" }'The stored client secret is redacted when you read the config back
(GET /api/v1/auth/entra/config).
tenant_id must be your directory (tenant) ID, a GUID. The multi-tenant
endpoints common, organizations and consumers are refused with 400,
and so is the former allow_meta_tenant: true switch (from v1.0.4: they
would let the administrator of any tenant that consents to the app make
themselves a DTM admin). A node whose stored configuration still names one of
them starts with Entra sign-in off and logs a warning naming the fix; local
sign-in is unaffected. Set tenant_id to the GUID to turn it back on. From
v1.0.5 the tenant ID is stored in lower case, so a GUID typed in upper case
signs in normally, from the console, the API or Terraform.
One configuration for the whole cluster
Section titled “One configuration for the whole cluster”From v1.0.5 the Entra configuration is cluster-wide. One save, through any node, from the Settings page, the API or Terraform, configures every node:
- Every node applies it. The node that serves the save signs it and replicates it. Each node verifies it, stores the client secret encrypted at rest, and switches to it. The last save wins.
- The first save after upgrading replaces each node’s own. A configuration a node held before v1.0.5 stays on that node: DTM never publishes one by itself. Once every node runs v1.0.5, the Settings page shows when the nodes’ configurations differ, and its button reads Apply to the whole cluster. That save replaces every node’s configuration, including any per-node redirect URI.
- Before that first save, a save can stay on one node. It applies only to
the node that serves it while any node runs an earlier version, while a
member is down but has not been removed from the cluster, or on a node that
has never reached its cluster (for example a replacement whose seeds have
not answered yet). The API response says
"replicated": falsein each of these cases. After the first cluster-wide save, every save is cluster-wide, and a node still on an earlier version keeps its own configuration until it is upgraded. - Replacement nodes receive it. A node that joins later, such as the replacement in a blue/green swap, receives the cluster-wide configuration by replication.
- Sign-in still completes on one node. On a cluster of more than one node, a Microsoft sign-in completes only on the node the redirect URI names, or behind a load balancer that keeps a user on one node between the start of the sign-in and the callback. Point users at that node’s address. If you gave each node its own redirect URI so that every node could complete sign-in, the first cluster-wide save ends that.
Verify
Section titled “Verify”-
Check that SSO is active on the node (admin role):
Terminal window curl -sk https://dtm.internal:8443/api/v1/auth/entra/config \-H "Authorization: Bearer $DTM_TOKEN"# {..., "configured": true, "active": true, "scope": "cluster", ...}configuredmeans the tenant, client and secret are stored;activemeans this node actually offers and completes Entra sign-in. When a stored configuration is not active (a refused tenant, or a missingredirect_uri),activeisfalseandinactive_reasonsays why, and the console’s Entra ID settings page shows Inactive with that reason. The publicGET /api/v1/auth/entra/statusreportsconfiguredonly. -
Check that every node holds the same configuration (from v1.0.5). In the same response,
scopeisclusterfor the cluster-wide configuration andlocalfor a node’s own, andversionandupdated_atidentify the cluster-wide save.cluster.distinct_configscounts the different configurations the nodes hold: more than one means the nodes differ, or a node has not applied the latest save yet. A node on an earlier version reports no configuration and is not counted, so during an upgrade1does not prove every node matches.cluster.nodeslists each node with a fingerprint of its tenant, client and redirect URI, never the secret. -
Open the UI sign-in page: a Sign in with Microsoft button appears. Sign in as a user with the Admin app role and confirm you have admin access; sign in as a user without it and confirm read-only access.