Set up authentication with Azure AD (Entra ID)

Configure Azure AD (Entra ID) to authenticate against the Moveworks Conversations API, using Issuer + Audience and the oid claim for identity resolution.

View as Markdown

The Conversations API is in Controlled Availability. Endpoints, behaviors, and configuration surfaces may change during CA. Contact your Moveworks account team to participate.

This guide walks through configuring Azure AD (Entra ID) to authenticate against the Moveworks Conversations API.

How authentication works

The Conversations API uses a token exchange model:

  1. Your application obtains an access token from Azure AD (Authorization Code + PKCE for user-facing clients).
  2. Your application exchanges that token at POST /oauth/v1/token for a short-lived Moveworks bearer.
  3. Your application sends the Moveworks bearer as Authorization: Bearer on every Conversations API request, and refreshes it by repeating the exchange with a fresh Azure token.

Azure AD token claims

The Azure AD v2.0 access token claims that matter for this setup:

ClaimValue
audThe Application (client) ID or Application ID URI of your registered app.
oidThe user’s stable Azure AD object ID. This is the claim Moveworks uses to resolve the user. It is a default claim in v2.0 access tokens, so no Token configuration change is required on the app registration.
isshttps://login.microsoftonline.com/{tenant-id}/v2.0

The Moveworks credential is configured with the Application (client) ID as the Audience and reads the oid claim as the user identifier.

Prerequisites

Confirm the following with your Moveworks account team:

  • Your account team has enabled the Conversations API for your org, and you have created the chatbot and recorded its Bot Name (used in the Assistant-Name header on every API call). See Create the Conversations API chatbot.
  • You have access to the Moveworks Admin Portal (https://{org}.moveworks.com).
  • You have admin access to Azure AD (Entra ID).
  • Your Moveworks user roster is populated from Entra through an MS Graph identity integration, so each user’s Azure AD object ID is already stored on their Moveworks profile.
  • You know the integration ID of that identity integration, commonly ms_graph. You enter it on the credential in step 2, which covers where to find the exact value.

Step 1: Configure the app registration in Entra ID

1

Register or open your app

In the Azure portal, go to Entra ID → App registrations → New registration (or open your existing app).

2

Set the platform and redirect URI

Under Authentication → Add a Redirect URI, choose Single-page application (SPA) and add your redirect URI there. Azure treats SPA as a public client that authenticates with PKCE alone, which is what the token request in step 3 assumes.

If your architecture requires the Web platform instead, Azure treats it as a confidential client and every token request must include a client_secret (or client_assertion). This is a valid path, but the curl in step 3.2 will not work as written — you must add client_secret to the POST body and manage that secret in your backend. If you register the app under the Web platform and omit the secret, Azure returns AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'. For most Conversations API integrations, SPA + PKCE is the simpler choice.

3

Note the Application (client) ID

Copy the Application (client) ID from the app’s Overview page. You will enter this value as the Audience on the Moveworks credential in step 2, and it is what Azure stamps as aud in access tokens issued for this app.

4

Expose an API scope

Under Expose an API, confirm the Application ID URI (Azure defaults to api://{client-id}; keep the default unless you have a reason to change it). Click Add a scope and create a delegated scope named access_as_user. Set “Who can consent?” to Admins and users so users can consent at sign-in time. Fill in the other required fields and save the scope.

Then go to API permissions → Add a permission → My APIs, select this same app, expand Delegated permissions, check the scope you just created (access_as_user), and click Add permissions. Grant admin consent if your organization requires it.

This is required for Azure to issue tokens when your client requests the {client-id}/.default scope in step 3. Without an exposed API scope granted to this app, the token request fails.

5

Assign users or groups

Assign the users or groups that should have access.

This guide assumes v2.0 access tokens. If your app registration’s Manifest has accessTokenAcceptedVersion: 1 (or omitted), tokens are issued in v1 format and the iss claim will be https://sts.windows.net/{tenant-id}/ rather than the value used below. Set accessTokenAcceptedVersion: 2 in the Manifest or request tokens from the v2.0 endpoint. Make sure you switch from the Graph App manifest view to the AAD manifest view.

Step 2: Create the JWT OAuth credential in Moveworks

In the Moveworks Admin Portal, go to https://{org}.moveworks.com/http-connectors/connector_studio/homeCredentials → create a new credential, and set the fields as follows.

FieldValue for Azure ADNotes
Credential NameA descriptive label, e.g. AzureAD-CAPIFree text.
Credential TypeJWT OAuthReveals the fields below.
Public Key valuehttps://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configurationThis field accepts a WKE (well-known endpoint) URL in place of a PEM string. Paste your tenant’s OpenID Connect discovery URL. Moveworks auto-discovers the issuer and fetches the JWKS signing keys from it, so you never paste a static key.
AudienceThe Application (client) ID GUID you noted in step 1, e.g. a1b2c3d4-e5f6-7890-abcd-ef1234567890Enter the exact aud value your tokens carry. If your tokens carry the Application ID URI, e.g. api://<client-id>, enter that instead.
Issuerhttps://login.microsoftonline.com/{tenant-id}/v2.0The iss claim your tokens carry. Together with Audience, it uniquely identifies this credential.
Identifier TypeIdPTells Moveworks to resolve the identifier claim through an identity integration rather than as a raw email or record ID. Azure identifies users by GUID, so IdP is the correct type here.
Integration IDYour MS Graph identity integration, commonly ms_graphRequired when Identifier Type is IdP. Point it at the integration that already stores each user’s Azure AD object ID, so no additional identity mapping is needed. To find the exact name, go to Moveworks Setup > Connectors > Built-in Connectors, find the row whose System matches your identity source, open it, and copy the connection name shown at the top of the connector page.
Identifier ClaimoidThe claim Moveworks reads to identify the user. Azure’s sub is a pairwise GUID and is not usable for identity resolution, so use oid, which matches the object ID synced from Entra.

The Audience value must exactly match the aud claim your Azure tokens actually carry. That value depends on your app registration and token version:

  • v2.0 tokens with the default Application ID URI: aud is typically the Application (client) ID GUID (the common case).
  • v1.0 tokens: aud is typically the Application ID URI (api://<client-id>).
  • Custom Application ID URI: aud matches whatever value you set.

Recommended: obtain a test token in step 3 first, decode it at jwt.ms, and paste the exact aud value from the decoded token into this field before saving the credential. If the values don’t match, the token exchange fails with a 401 and INVALID_JWT, because Moveworks locates your credential by its issuer and audience pair.

Credential fields cannot be edited after you save. If the Integration ID, Audience, Issuer, Identifier Type, or Identifier Claim turns out to be wrong, create a new credential with the corrected values.

The value indexed under that integration must be the Azure AD object ID itself, a GUID such as 8fb83dba-7da8-43af-a84c-0735f50b3bb1, matching the oid claim in the token. If it holds an email address, an employee number, or a username, it will not match oid and the token exchange fails. The exchange in step 4.1 is the definitive check, so run it with a test user before rolling out to real traffic.

If your user profiles already carry a channel entry with the integration ID conversations_rest_api, enter that instead. It is equally supported, and existing configurations need no change. The entry must hold the Azure AD object ID rather than an email address or an employee number, which is a common cause of a failed exchange. Your Moveworks account team can confirm what it currently holds and repoint it if needed; the change takes effect after the next identity ingestion run.

Once the values are confirmed, Save and then Publish the credential. The token exchange in step 4 fails while the credential is still in draft.

Step 3: Request an access token from Azure AD

Your client application obtains an Azure access token via the OAuth 2.0 Authorization Code + PKCE flow. This is a two-hop flow: your app redirects the user’s browser to Azure to sign in, then exchanges the returned authorization code for an access token.

The Azure endpoints

Both endpoints are tenant-specific. Substitute your Entra tenant ID (a GUID).

PurposeURL
Authorization (browser redirect for sign-in)https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize
Token (exchange the authorization code)https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token

Step 3.1: Redirect the user to sign in

Construct the authorization URL with the following query parameters and redirect the user’s browser to it.

ParameterValue
client_idYour Application (client) ID
response_typecode
redirect_uriThe redirect URI you configured on the app registration
response_modequery
scope{client-id}/.default (see note below)
stateA CSRF token you generate and validate on return
code_challengePKCE code challenge (base64url of SHA-256 of the verifier)
code_challenge_methodS256

Example:

https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize?
client_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890
&response_type=code
&redirect_uri=https%3A%2F%2Fapp.acme.com%2Fauth%2Fcallback
&response_mode=query
&scope=a1b2c3d4-e5f6-7890-abcd-ef1234567890%2F.default
&state=<csrf-token>
&code_challenge=<pkce-challenge>
&code_challenge_method=S256

The scope parameter must be {client-id}/.default (using your Application (client) ID). This is what causes Azure to issue a token whose aud claim equals your client ID, matching the Audience registered on the Moveworks credential. Requesting a different scope (for example, openid profile email alone) causes Azure to issue a token intended for a different resource and Moveworks will reject the exchange with a 401 and INVALID_JWT.

After the user signs in, Azure redirects the browser to your redirect_uri with code and state query parameters. Validate state matches what you sent, then use code in the next step.

Step 3.2: Exchange the authorization code for an access token

POST to the token endpoint with the authorization code, PKCE verifier, and application details.

curl -X POST "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "client_id=<APPLICATION_CLIENT_ID>" \
--data-urlencode "scope=<APPLICATION_CLIENT_ID>/.default" \
--data-urlencode "code=<AUTHORIZATION_CODE>" \
--data-urlencode "redirect_uri=<REDIRECT_URI>" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code_verifier=<PKCE_VERIFIER>"

A successful response returns an access_token (an Azure-issued JWT). This is what you pass to Moveworks in step 4.

Manual testing tips

For testing the setup without wiring up a full client:

  • Use OAuth Debugger or Postman’s OAuth 2.0 support to run the Authorization Code + PKCE flow interactively against your Entra app registration. Both let you paste in the endpoints, client ID, scope, and redirect URI, and hand back the access token.
  • Alternatively, use the Microsoft Graph Explorer if you just want to verify a user can sign in and see what claims their token carries.

Decode the returned access token (for example, at jwt.ms) and confirm the claims before moving to step 4:

  • aud equals the Application (client) ID you registered as the Audience.
  • iss equals https://login.microsoftonline.com/{your-tenant-id}/v2.0.
  • oid is present and matches the object ID Moveworks has for the test user.

Step 4: Validate end to end

The examples below use https://api.moveworks.ai, which is only correct for orgs on the US Production data center. Replace it with the base URL for your data center from the Base URLs table (for example, https://api.am-eu-central.moveworks.ai for EU). Requests to the wrong host fail authentication, because credentials are registered per data center.

4.1 Exchange for a Moveworks token

Pass the Azure-issued token to the Moveworks token endpoint:

curl -X POST "https://api.moveworks.ai/oauth/v1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
--data-urlencode "assertion=<AZURE_ACCESS_TOKEN>"

A successful response returns a Moveworks access token (valid ~1 hour). Cache it for its lifetime and repeat the exchange with a fresh Azure token when it expires; do not exchange on every API call.

4.2 Test the API

curl -X POST "https://api.moveworks.ai/assistant/v1/conversations" \
-H "Content-Type: application/json" \
-H "Assistant-Name: <YOUR_BOT_NAME>" \
-H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
-d '{"title": "Test conversation"}'

A 201 with a conversation_id confirms the integration works end to end.

Troubleshooting

400 INVALID_JWT at /oauth/v1/token.

Work through these in order:

  1. Confirm the assertion is the token you think it is. This response is returned when the assertion value is not a well-formed three-part JWT. Print the token’s length and split it on . before sending; the result must be exactly three parts with no whitespace. A Bearer prefix, surrounding quotes, or a value read from a file without trimming newlines all produce this error. On Windows, long tokens pasted into a terminal are frequently wrapped or truncated; read the token from a file instead of pasting it.
  2. Confirm the token has not expired. Azure access tokens are short-lived. Mint a fresh one and retry.
  3. Confirm identity resolution. If the assertion is well-formed and unexpired, the oid claim could not be matched to a Moveworks user. Decode the token at jwt.ms and note the oid value, then ask your Moveworks account team to confirm that this is the identifier indexed under the integration ID registered on your credential. The common cause is that the registered integration holds a different identifier, such as an employee number. Correcting the integration ID means creating a new credential, since credentials cannot be edited; correcting the indexed value requires an identity configuration change and a re-ingestion run.

401 UNAUTHORIZED with Token validation failed when calling /assistant/v1/*.

Confirm your Authorization: Bearer value is the Moveworks token from step 4.1 (starts with mwt_), not the raw Azure access token (starts with eyJ). If you are already sending mwt_..., the token has likely expired; repeat step 4.1 with a fresh Azure token.

401 INVALID_JWT, “Client credentials not found”, at /oauth/v1/token.

Moveworks locates your credential by the iss and aud pair on the incoming token, so this means that pair matched no registered credential. Decode the token and confirm aud exactly equals the Audience on the credential and iss exactly equals its Issuer. Also confirm the credential is Published and that you are calling the base URL for your data center.

401 INVALID_JWT, “JWT signature verification failed”, at /oauth/v1/token.

The credential was found, so the issuer and audience are right. The kid header does not match a registered key, or the key registered on the credential is not the public half of the pair Azure signed with.

Identity resolves to the wrong user, or responses look empty.

The oid claim matched a Moveworks user, but not the intended one. Confirm the object ID in the token belongs to the test user you expect, and that the same object ID is not stored on more than one Moveworks profile.

Summary

Credential fieldAzure AD value
Public Key valueWKE URL: https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration
AudienceApplication (client) ID GUID from the Entra app registration
Issuerhttps://login.microsoftonline.com/{tenant-id}/v2.0
Identifier TypeIdP
Integration IDYour MS Graph identity integration, commonly ms_graph
Identifier Claimoid
Entra sideNo claim configuration required. oid is a default claim in v2.0 access tokens, and no custom claims are needed.

For interaction patterns (polling and streaming) and the full API contract, see the Conversations API reference.