Skip to content

Commit 1237119

Browse files
committed
feat(credentials): Microsoft Entra app-only credentials for Entra ID, SharePoint, Dataverse, and Dynamics 365
A saved Microsoft app credential (OAuth 2.0 client credentials) as an additional option next to Microsoft OAuth, which is unchanged. One app registration serves the four services whose tools run without a signed-in user; Outlook, OneDrive, Excel, Word, Planner, and Teams never offer it and token resolution refuses it for them (provider mismatch). - Modes: client secret; certificate (PS256 client assertion with the certificate's x5t#S256 thumbprint, aud = the tenant token endpoint, 5-minute lifetime; key/certificate pairing, RSA size, and expiry checked before any request); keyless federated credential (a Sim workload-identity token for the credential's own subject, audience api://AzureADTokenExchange, subject bound to the saving user, refused if the app also accepts an unheld subject). - The token resource comes from the tool's service, passed from the server-validated tool policy: Graph for Entra ID and SharePoint, the stored environment for Dataverse and Dynamics 365. Any other service is refused. - The environment URL is stored on the credential (public-cloud Dynamics hosts, https root only). Dataverse tools take credentialType and instanceUrl as authoritative params; an app credential's request goes only to its own environment and a block environment that names another is refused. OAuth requests keep the block environment exactly as before. Dynamics 365 already matched instanceUrl against the block environment. - Save check: a Graph token reading the app's own service principal (no permission needed), plus Dataverse WhoAmI when an environment is set. Entra AADSTS codes map to actionable copy; runtime failures keep the shared invalid_credentials / provider_unavailable split. Tokens cache per credential, resource, and stored-secret version in an LRU. - Azure AD Reset password refuses app-only credentials: Graph does not support application permissions for resetPassword. - Connect dialog with tenant ID, client ID, environment URL, and the issuer/audience/subject to paste; docs page with per-mode setup.
1 parent f843739 commit 1237119

47 files changed

Lines changed: 1962 additions & 60 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/docs/content/docs/integrations/meta.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
"manageengine_sdp",
174174
"mem0",
175175
"memory",
176+
"microsoft-app-credentials",
176177
"microsoft_ad",
177178
"microsoft_dataverse",
178179
"microsoft_dynamics_365",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
title: Microsoft app credentials
3+
description: Run Microsoft Entra ID, SharePoint, Dataverse, and Dynamics 365 blocks as an app registration with a client secret, a certificate, or a keyless federated credential that trusts Sim
4+
---
5+
6+
import { Callout } from '@/components/ui/callout'
7+
import { Step, Steps } from '@/components/ui/steps'
8+
9+
A Microsoft app credential signs in as a Microsoft Entra app registration instead of a person (the OAuth 2.0 client credentials flow). One credential works for the **Azure AD** (Microsoft Entra ID), **SharePoint**, **Microsoft Dataverse**, and **Dynamics 365** blocks. Connecting a Microsoft account through OAuth keeps working exactly as before; the app credential is an additional option in the same account picker.
10+
11+
<Callout type="info">
12+
Outlook, OneDrive, Excel, Word, Planner, and Teams act on behalf of a signed-in person and don't accept an app credential. The Azure AD **Reset password** operation also needs a signed-in admin, because Microsoft Graph doesn't support application permissions for it.
13+
</Callout>
14+
15+
## Choose how the app signs in
16+
17+
| Option | What Sim stores | When to use it |
18+
| --- | --- | --- |
19+
| **Federated credential (keyless, via Sim OIDC)** | Nothing secret: the tenant ID, client ID, and this credential's subject | Recommended. No secret to rotate, expire, or leak. |
20+
| **Client secret** | The tenant ID, client ID, and client secret | Quick setup. Secrets expire, so plan to rotate them. |
21+
| **Certificate** | The tenant ID, client ID, the certificate, and its private key | When your organization requires certificate credentials. Sim signs each token request with the key. |
22+
23+
Every option resolves on Sim's servers at run time, and tokens never appear in block outputs, logs, or anything a model sees. Azure AD and SharePoint blocks get a Microsoft Graph token; Dataverse and Dynamics 365 blocks get a token for the credential's own environment.
24+
25+
## Register the app and grant permissions
26+
27+
<Steps>
28+
<Step>
29+
In the [Microsoft Entra admin center](https://entra.microsoft.com), go to **App registrations** → **New registration**. A single-tenant app is fine. Copy the **Application (client) ID** and **Directory (tenant) ID** from its Overview page.
30+
</Step>
31+
<Step>
32+
Under **API permissions** → **Add a permission** → **Microsoft Graph** → **Application permissions**, add what your workflows need, then select **Grant admin consent**. Application permissions always need admin consent. For example:
33+
- Azure AD: `User.Read.All` or `User.ReadWrite.All`, `Group.ReadWrite.All`, `GroupMember.ReadWrite.All`, `AuditLog.Read.All` for sign-in and audit logs, `Policy.Read.All` for conditional access, `UserAuthenticationMethod.Read.All` for authentication methods, `User.RevokeSessions.All` to revoke sessions. Setting a password needs `User-PasswordProfile.ReadWrite.All` **and** the app assigned at least the User Administrator role.
34+
- SharePoint: `Sites.Read.All` or `Sites.ReadWrite.All`. Creating and editing pages needs `Sites.ReadWrite.All`. `Sites.Selected` limits the app to sites you grant it, but searching for sites doesn't work with it, so enter site IDs directly.
35+
</Step>
36+
<Step>
37+
Dataverse and Dynamics 365 don't use API permissions. Instead, in the [Power Platform admin center](https://admin.powerplatform.microsoft.com), open the environment → **Settings** → **Users + permissions** → **Application users** → **New app user**, add this app, pick a business unit, and assign a security role. Then enter the environment URL (like `https://contoso.crm.dynamics.com`) on the credential.
38+
</Step>
39+
</Steps>
40+
41+
## Set up a keyless federated credential [#keyless-federated-credential]
42+
43+
<Steps>
44+
<Step>
45+
In Sim, add a **Microsoft app credential**, choose **Federated credential (keyless, via Sim OIDC)**, and enter the tenant ID and client ID. The dialog shows an **Issuer**, **Audience**, and **Subject**.
46+
</Step>
47+
<Step>
48+
In the app registration, go to **Certificates & secrets** → **Federated credentials** → **Add credential** → **Other issuer**. Paste the **Issuer** and the **Subject identifier** exactly as shown, set the audience to `api://AzureADTokenExchange` (the dialog's value), and give it a name. Matching is case-sensitive.
49+
</Step>
50+
<Step>
51+
Save in Sim. Sim requests a token as the app and reads the app's own service principal from Microsoft Graph, which needs no extra permission. It also checks that the app refuses a subject no credential holds, so only this credential can use the app. A new federated credential can take a few minutes to take effect; if saving fails with a matching error right after you add it, wait and try again.
52+
</Step>
53+
</Steps>
54+
55+
An app can have up to 20 federated credentials. Each Sim credential has its own subject, and reconnecting keeps the subject by default so the federated credential keeps working. Keyless credentials are available for workspace credentials, not organization credentials.
56+
57+
## Use a client secret or certificate
58+
59+
- **Client secret:** under **Certificates & secrets** → **Client secrets** → **New client secret**, copy the secret's **Value** (not the Secret ID).
60+
- **Certificate:** upload the certificate's public part (`.cer`, `.pem`, or `.crt`) under **Certificates & secrets** → **Certificates**, then paste the certificate and its unencrypted PEM RSA private key (2048 to 4096 bits) into Sim. Sim checks the key belongs to the certificate before saving, and signs a PS256 assertion with the certificate's SHA-256 thumbprint.
61+
62+
## Self-hosted deployments
63+
64+
Keyless credentials need `WORKLOAD_IDENTITY_SIGNING_KEY` set and an `https` issuer Microsoft Entra can reach over the public internet. See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments) for the same settings and for rotating the signing key. App credentials use the global Microsoft Entra endpoint (`login.microsoftonline.com`) and public-cloud Dataverse environments.
65+
66+
## Troubleshooting
67+
68+
- **"No federated credential on this app matches…":** the federated credential's issuer, subject, or audience differs from the dialog (check case and trailing slashes), it's on a different app than the client ID, or it was added moments ago.
69+
- **"Microsoft Entra rejected the client secret":** you pasted the Secret ID, or the secret was deleted. **"This client secret has expired":** create a new one and reconnect.
70+
- **"…could not verify the signed assertion":** the certificate in Sim isn't the one uploaded to the app registration.
71+
- **"…the Dataverse environment did not accept it":** add the app as an application user of the environment with a security role.
72+
- **A block fails with 403 "Insufficient privileges":** the app is missing an application permission for that operation, or admin consent wasn't granted after adding it.

‎apps/docs/content/docs/integrations/microsoft_ad.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { ReferenceDetails } from "@/components/ui/reference-details"
1313
Use [Microsoft Entra ID](https://entra.microsoft.com), formerly Azure Active Directory, to manage directory users, groups, memberships, licenses, passwords, sign-in sessions, app roles, and directory roles. Read sign-in and audit logs, registered devices, and conditional access policies; device writes are excluded.
1414

1515
Use OData filters to query directory records. Grant the connection the permissions and administrative roles required by each operation before using it for onboarding, offboarding, or access review. Microsoft 365 groups have a 30-day recovery window. Cloud security-group recovery is a [public-cloud preview with tenant restrictions](https://learn.microsoft.com/en-us/entra/identity/users/groups-restore-deleted); confirm eligibility before deleting a group.
16+
17+
To run as an app registration instead of a signed-in admin, use a [Microsoft app credential](/integrations/microsoft-app-credentials).
1618
{/* MANUAL-CONTENT-END */}
1719

1820
## OAuth scopes [#oauth-scopes]

‎apps/docs/content/docs/integrations/microsoft_dataverse.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { ReferenceDetails } from "@/components/ui/reference-details"
1111

1212
{/* MANUAL-CONTENT-START:intro */}
1313
Use [Microsoft Dataverse](https://learn.microsoft.com/en-us/power-apps/maker/data-platform/data-platform-intro) to manage records and relationships in Dynamics 365, Power Platform, or custom environments. The block also supports queries, bulk operations, file transfer, metadata lookup, and Dataverse actions and functions.
14+
15+
To run as an application user instead of a signed-in person, use a [Microsoft app credential](/integrations/microsoft-app-credentials).
1416
{/* MANUAL-CONTENT-END */}
1517

1618
## OAuth scopes [#oauth-scopes]

‎apps/docs/content/docs/integrations/microsoft_dynamics_365.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Two block operations intentionally reuse the new CRM tools under a simpler CRM l
1818
- **Assign Record** updates the selected record's `ownerid@odata.bind` to an explicitly chosen user or team.
1919

2020
Connect one credential per Dynamics environment. The credential is bound to that environment during Microsoft OAuth, and CRM requests refuse a different environment. This release supports public-cloud Dataverse hosts only.
21+
22+
To run as an application user instead of a signed-in person, use a [Microsoft app credential](/integrations/microsoft-app-credentials).
2123
{/* MANUAL-CONTENT-END */}
2224

2325
## OAuth scopes [#oauth-scopes]

‎apps/docs/content/docs/integrations/sharepoint.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { ReferenceDetails } from "@/components/ui/reference-details"
1111

1212
{/* MANUAL-CONTENT-START:intro */}
1313
Use [SharePoint](https://www.microsoft.com/en-us/microsoft-365/sharepoint/collaboration) in Sim to list sites, manage pages and lists, update list items, and upload or download files. Connect a Microsoft account through OAuth, then choose the target site or resource for each operation.
14+
15+
To run as an app registration instead of a signed-in person, use a [Microsoft app credential](/integrations/microsoft-app-credentials).
1416
{/* MANUAL-CONTENT-END */}
1517

1618
## OAuth scopes [#oauth-scopes]

‎apps/docs/content/integration-navigation.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
"integration": "linear",
5858
"title": "API keys"
5959
},
60+
"microsoft-app-credentials": {
61+
"title": "Microsoft app credentials"
62+
},
6063
"monday-service-account": {
6164
"integration": "monday",
6265
"title": "API tokens"

‎apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/connect-service-account-modal.tsx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ import {
3737
AWS_SERVICE_ACCOUNT_PROVIDER_ID,
3838
DATABRICKS_SERVICE_ACCOUNT_PROVIDER_ID,
3939
GOOGLE_SERVICE_ACCOUNT_PROVIDER_ID,
40+
MICROSOFT_APP_SERVICE_ACCOUNT_PROVIDER_ID,
4041
OCI_API_KEY_SERVICE_ACCOUNT_PROVIDER_ID,
4142
ORACLE_DATABASE_SERVICE_ACCOUNT_PROVIDER_ID,
4243
SLACK_CUSTOM_BOT_PROVIDER_ID,
4344
} from '@/lib/oauth/types'
4445
import { AwsAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/aws-account-modal'
4546
import { ClientCredentialAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/client-credential-account-modal'
4647
import { DatabricksAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/databricks-account-modal'
48+
import { MicrosoftAppAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/microsoft-app-account-modal'
4749
import { OracleDatabaseAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/oracle-database-account-modal'
4850
import { SnowflakeAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/snowflake-account-modal'
4951
import { TokenServiceAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/token-service-account-modal'
@@ -64,6 +66,7 @@ export type ServiceAccountProviderId =
6466
| typeof ORACLE_DATABASE_SERVICE_ACCOUNT_PROVIDER_ID
6567
| typeof AWS_SERVICE_ACCOUNT_PROVIDER_ID
6668
| typeof DATABRICKS_SERVICE_ACCOUNT_PROVIDER_ID
69+
| typeof MICROSOFT_APP_SERVICE_ACCOUNT_PROVIDER_ID
6770
| TokenServiceAccountProviderId
6871
| ClientCredentialAccountProviderId
6972

@@ -200,6 +203,21 @@ export function ConnectServiceAccountModal({
200203
/>
201204
)
202205
}
206+
if (serviceAccountProviderId === MICROSOFT_APP_SERVICE_ACCOUNT_PROVIDER_ID) {
207+
return (
208+
<MicrosoftAppAccountModal
209+
open={open}
210+
onOpenChange={onOpenChange}
211+
workspaceId={workspaceId}
212+
organizationId={organizationId}
213+
serviceIcon={serviceIcon}
214+
credentialId={credentialId}
215+
initialDisplayName={credentialDisplayName}
216+
initialDescription={credentialDescription}
217+
onCreated={onCreated}
218+
/>
219+
)
220+
}
203221
if (serviceAccountProviderId === AWS_SERVICE_ACCOUNT_PROVIDER_ID) {
204222
return (
205223
<AwsAccountModal

0 commit comments

Comments
 (0)