@arbium/enterprise-agent-connect 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +117 -2
  3. package/dist/connectors/dell-techdirect/connector.json +39 -0
  4. package/dist/connectors/dell-techdirect/index.js +7 -0
  5. package/dist/connectors/dell-techdirect/index.js.map +1 -0
  6. package/dist/connectors/dell-techdirect/tools.js +49 -0
  7. package/dist/connectors/dell-techdirect/tools.js.map +1 -0
  8. package/dist/connectors/dell-techdirect/vendor.js +57 -0
  9. package/dist/connectors/dell-techdirect/vendor.js.map +1 -0
  10. package/dist/connectors/entra/connector.json +85 -0
  11. package/dist/connectors/entra/index.js +4 -0
  12. package/dist/connectors/entra/index.js.map +1 -0
  13. package/dist/connectors/entra/tools.js +262 -0
  14. package/dist/connectors/entra/tools.js.map +1 -0
  15. package/dist/connectors/fedex/connector.json +118 -0
  16. package/dist/connectors/fedex/index.js +7 -0
  17. package/dist/connectors/fedex/index.js.map +1 -0
  18. package/dist/connectors/fedex/tools.js +152 -0
  19. package/dist/connectors/fedex/tools.js.map +1 -0
  20. package/dist/connectors/fedex/vendor.js +98 -0
  21. package/dist/connectors/fedex/vendor.js.map +1 -0
  22. package/dist/connectors/http/index.js +235 -0
  23. package/dist/connectors/http/index.js.map +1 -0
  24. package/dist/connectors/intune/connector.json +106 -0
  25. package/dist/connectors/intune/index.js +4 -0
  26. package/dist/connectors/intune/index.js.map +1 -0
  27. package/dist/connectors/intune/tools.js +416 -0
  28. package/dist/connectors/intune/tools.js.map +1 -0
  29. package/dist/connectors/jira/connector.json +129 -0
  30. package/dist/connectors/jira/index.js +7 -0
  31. package/dist/connectors/jira/index.js.map +1 -0
  32. package/dist/connectors/jira/jql.js +67 -0
  33. package/dist/connectors/jira/jql.js.map +1 -0
  34. package/dist/connectors/jira/tools.js +464 -0
  35. package/dist/connectors/jira/tools.js.map +1 -0
  36. package/dist/connectors/jira/vendor.js +94 -0
  37. package/dist/connectors/jira/vendor.js.map +1 -0
  38. package/dist/connectors/microsoft/client.js +434 -0
  39. package/dist/connectors/microsoft/client.js.map +1 -0
  40. package/dist/connectors/microsoft/provider.json +118 -0
  41. package/dist/connectors/outlook-calendar/connector.json +136 -0
  42. package/dist/connectors/outlook-calendar/index.js +4 -0
  43. package/dist/connectors/outlook-calendar/index.js.map +1 -0
  44. package/dist/connectors/outlook-calendar/tools.js +398 -0
  45. package/dist/connectors/outlook-calendar/tools.js.map +1 -0
  46. package/dist/connectors/outlook-mail/connector.json +154 -0
  47. package/dist/connectors/outlook-mail/index.js +4 -0
  48. package/dist/connectors/outlook-mail/index.js.map +1 -0
  49. package/dist/connectors/outlook-mail/tools.js +236 -0
  50. package/dist/connectors/outlook-mail/tools.js.map +1 -0
  51. package/dist/connectors/salesforce/connector.json +122 -0
  52. package/dist/connectors/salesforce/index.js +7 -0
  53. package/dist/connectors/salesforce/index.js.map +1 -0
  54. package/dist/connectors/salesforce/soql.js +60 -0
  55. package/dist/connectors/salesforce/soql.js.map +1 -0
  56. package/dist/connectors/salesforce/tools.js +234 -0
  57. package/dist/connectors/salesforce/tools.js.map +1 -0
  58. package/dist/connectors/salesforce/vendor.js +107 -0
  59. package/dist/connectors/salesforce/vendor.js.map +1 -0
  60. package/dist/connectors/sharepoint-files/connector.json +194 -0
  61. package/dist/connectors/sharepoint-files/excel.js +300 -0
  62. package/dist/connectors/sharepoint-files/excel.js.map +1 -0
  63. package/dist/connectors/sharepoint-files/index.js +12 -0
  64. package/dist/connectors/sharepoint-files/index.js.map +1 -0
  65. package/dist/connectors/sharepoint-files/tools.js +365 -0
  66. package/dist/connectors/sharepoint-files/tools.js.map +1 -0
  67. package/dist/connectors/sharepoint-files/vendor.js +167 -0
  68. package/dist/connectors/sharepoint-files/vendor.js.map +1 -0
  69. package/dist/connectors/teams/connector.json +298 -0
  70. package/dist/connectors/teams/index.js +5 -0
  71. package/dist/connectors/teams/index.js.map +1 -0
  72. package/dist/connectors/teams/tools.js +391 -0
  73. package/dist/connectors/teams/tools.js.map +1 -0
  74. package/dist/packages/connector-kit/src/branding.js +102 -0
  75. package/dist/packages/connector-kit/src/branding.js.map +1 -0
  76. package/dist/packages/connector-kit/src/certificate.js +84 -0
  77. package/dist/packages/connector-kit/src/certificate.js.map +1 -0
  78. package/dist/packages/connector-kit/src/common.js +107 -0
  79. package/dist/packages/connector-kit/src/common.js.map +1 -0
  80. package/dist/packages/connector-kit/src/connector.js +492 -0
  81. package/dist/packages/connector-kit/src/connector.js.map +1 -0
  82. package/dist/packages/connector-kit/src/oauth.js +79 -0
  83. package/dist/packages/connector-kit/src/oauth.js.map +1 -0
  84. package/dist/packages/connector-kit/src/types.js +20 -0
  85. package/dist/packages/connector-kit/src/types.js.map +1 -0
  86. package/dist/packages/server/migrations/0001_tables.sql +268 -0
  87. package/dist/packages/server/migrations/0002_organizations.sql +72 -0
  88. package/dist/packages/server/migrations/0003_drop_unused.sql +3 -0
  89. package/dist/packages/server/migrations/0004_trigger_runners.sql +15 -0
  90. package/dist/packages/server/migrations/0005_run_runner.sql +3 -0
  91. package/dist/packages/server/migrations/0006_approvals.sql +36 -0
  92. package/dist/packages/server/migrations/rollback/to-legacy.sql +87 -0
  93. package/dist/packages/server/src/api.js +684 -0
  94. package/dist/packages/server/src/api.js.map +1 -0
  95. package/dist/packages/server/src/app.js +444 -0
  96. package/dist/packages/server/src/app.js.map +1 -0
  97. package/dist/packages/server/src/builtin.js +24 -0
  98. package/dist/packages/server/src/builtin.js.map +1 -0
  99. package/dist/packages/server/src/consent.js +36 -0
  100. package/dist/packages/server/src/consent.js.map +1 -0
  101. package/dist/packages/server/src/index.js +129 -0
  102. package/dist/packages/server/src/index.js.map +1 -0
  103. package/dist/packages/server/src/migrate.js +134 -0
  104. package/dist/packages/server/src/migrate.js.map +1 -0
  105. package/dist/packages/server/src/openapi.js +101 -0
  106. package/dist/packages/server/src/openapi.js.map +1 -0
  107. package/dist/packages/server/src/secrets.js +76 -0
  108. package/dist/packages/server/src/secrets.js.map +1 -0
  109. package/dist/packages/server/src/server.js +69 -0
  110. package/dist/packages/server/src/server.js.map +1 -0
  111. package/dist/packages/server/src/service.js +908 -0
  112. package/dist/packages/server/src/service.js.map +1 -0
  113. package/dist/packages/server/src/store.js +411 -0
  114. package/dist/packages/server/src/store.js.map +1 -0
  115. package/dist/packages/server/src/teams/activity.js +187 -0
  116. package/dist/packages/server/src/teams/activity.js.map +1 -0
  117. package/dist/packages/server/src/teams/admin.js +699 -0
  118. package/dist/packages/server/src/teams/admin.js.map +1 -0
  119. package/dist/packages/server/src/teams/approval.js +118 -0
  120. package/dist/packages/server/src/teams/approval.js.map +1 -0
  121. package/dist/packages/server/src/teams/bot.js +124 -0
  122. package/dist/packages/server/src/teams/bot.js.map +1 -0
  123. package/dist/packages/server/src/teams/claude.js +165 -0
  124. package/dist/packages/server/src/teams/claude.js.map +1 -0
  125. package/dist/packages/server/src/teams/jwt.js +121 -0
  126. package/dist/packages/server/src/teams/jwt.js.map +1 -0
  127. package/dist/packages/server/src/teams/package.js +319 -0
  128. package/dist/packages/server/src/teams/package.js.map +1 -0
  129. package/dist/packages/server/src/teams/provision.js +505 -0
  130. package/dist/packages/server/src/teams/provision.js.map +1 -0
  131. package/dist/packages/server/src/teams/runner.js +143 -0
  132. package/dist/packages/server/src/teams/runner.js.map +1 -0
  133. package/dist/packages/server/src/teams/runtime.js +701 -0
  134. package/dist/packages/server/src/teams/runtime.js.map +1 -0
  135. package/dist/packages/server/src/teams/store.js +507 -0
  136. package/dist/packages/server/src/teams/store.js.map +1 -0
  137. package/package.json +42 -4
@@ -0,0 +1,434 @@
1
+ import { createHash } from "node:crypto";
2
+ import { brand } from "../../packages/connector-kit/src/branding.js";
3
+ import { clientAssertion } from "../../packages/connector-kit/src/certificate.js";
4
+ import { choiceFields } from "../../packages/connector-kit/src/connector.js";
5
+ import { ConnectError, TokenCache, } from "../../packages/connector-kit/src/types.js";
6
+ // One Microsoft connection per organization, shared by the Microsoft apps (mail, files).
7
+ // Normally the customer's own single-tenant Entra app, signing in with a certificate the
8
+ // service made (client assertion) or a client secret. Where the deployment offers it, instead
9
+ // the deployment's shared multi-tenant app, approved by the customer's administrator.
10
+ // config.auth: "certificate" | "client_secret" | SHARED_APP_AUTH.
11
+ /** config.auth of a connection approved for the shared app. A value stored in existing rows
12
+ * (it predates the service's own name), so it never changes. */
13
+ export const SHARED_APP_AUTH = "arbium_app";
14
+ // Entra role template ids (the same in every tenant) of the roles that can approve an app:
15
+ // Global Administrator, Privileged Role Administrator, Cloud Application Administrator,
16
+ // Application Administrator.
17
+ const APPROVER_ROLES = new Set([
18
+ "62e90394-69f5-4237-9190-012177145e10",
19
+ "e8611ab8-c189-46e8-94e1-60213ab1f814",
20
+ "158c047a-c907-4556-b7ef-446551a6b5f7",
21
+ "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
22
+ ]);
23
+ const LOGIN = "https://login.microsoftonline.com";
24
+ const GRAPH = "https://graph.microsoft.com";
25
+ // Office 365 Exchange Online: permissions granted on it in Entra (full_access_as_app, its
26
+ // Mail.*) open mailboxes through EWS and never show in a Graph token.
27
+ const EXCHANGE = "https://outlook.office365.com";
28
+ export const EXCHANGE_SUFFIX = " (Exchange Online)";
29
+ export const GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
30
+ // Right after approval Microsoft can take a few seconds to make the app usable in the tenant.
31
+ const CONNECT_RETRY_MS = [3000, 6000, 10000];
32
+ const tokenUrl = (tenant) => `${LOGIN}/${tenant}/oauth2/v2.0/token`;
33
+ // Claims of a token Microsoft just handed us over TLS; no signature check needed.
34
+ function tokenClaims(jwt) {
35
+ try {
36
+ return JSON.parse(Buffer.from(jwt.split(".")[1], "base64url").toString("utf8"));
37
+ }
38
+ catch {
39
+ throw new ConnectError("Microsoft returned an unreadable token");
40
+ }
41
+ }
42
+ export function tokenClaim(jwt, claim) {
43
+ try {
44
+ const value = tokenClaims(jwt)[claim];
45
+ return typeof value === "string" ? value : null;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /** The application permissions in an access token (its `roles` claim). */
52
+ export function tokenRoles(jwt) {
53
+ const roles = tokenClaims(jwt).roles;
54
+ return (Array.isArray(roles) ? roles : [])
55
+ .filter((r) => typeof r === "string")
56
+ .sort();
57
+ }
58
+ /** The permissions the ticked capabilities need that the app lacks. Extra ones are the
59
+ * admin's choice, so they are not called out. */
60
+ export function compareRoles(granted, input) {
61
+ const missing = input.expected.filter((r) => !granted.includes(r));
62
+ if (!missing.length)
63
+ return undefined;
64
+ const them = missing.length > 1 ? "them" : "it";
65
+ return `Missing: ${missing.join(", ")}. Add ${them} to the app and grant admin consent, or agents cannot do what you ticked.`;
66
+ }
67
+ function safeJson(raw) {
68
+ try {
69
+ return JSON.parse(raw);
70
+ }
71
+ catch {
72
+ return raw;
73
+ }
74
+ }
75
+ export function microsoftClient(options, fetchImpl = fetch, sleep = (ms) => new Promise((r) => setTimeout(r, ms))) {
76
+ const { sharedApp } = options;
77
+ const tokens = new TokenCache();
78
+ async function requestToken(tenant, form) {
79
+ const res = await fetchImpl(tokenUrl(tenant), {
80
+ method: "POST",
81
+ headers: { "content-type": "application/x-www-form-urlencoded" },
82
+ body: new URLSearchParams(form),
83
+ });
84
+ const body = (await res.json().catch(() => ({})));
85
+ return { ok: res.ok, status: res.status, body };
86
+ }
87
+ // The token request's form for this connection's way of signing in.
88
+ function credentials(c) {
89
+ const { auth, tenantId, clientId, certificate } = c.config;
90
+ if (auth === SHARED_APP_AUTH) {
91
+ if (!sharedApp)
92
+ throw new ConnectError(`This Microsoft connection was approved for ${brand().productName}'s shared app, which this deployment does not offer. Disconnect Microsoft and connect it with your own app registration.`);
93
+ return { client_id: sharedApp.clientId, client_secret: sharedApp.clientSecret };
94
+ }
95
+ if (!GUID.test(tenantId ?? "") || !GUID.test(clientId ?? ""))
96
+ throw new Error("Microsoft connection has no tenantId or clientId");
97
+ if (auth === "client_secret") {
98
+ if (!c.secret.client_secret)
99
+ throw new Error("Microsoft connection has no client secret");
100
+ return { client_id: clientId, client_secret: c.secret.client_secret };
101
+ }
102
+ if (auth === "certificate") {
103
+ if (!certificate || !c.secret.private_key)
104
+ throw new Error("Microsoft connection has no certificate");
105
+ return {
106
+ client_id: clientId,
107
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
108
+ client_assertion: clientAssertion({
109
+ clientId,
110
+ tokenUrl: tokenUrl(tenantId),
111
+ certificate,
112
+ privateKey: c.secret.private_key,
113
+ }),
114
+ };
115
+ }
116
+ throw new Error(`Microsoft connection has unknown sign-in ${auth}`);
117
+ }
118
+ // `resource`: the API the token is for, Microsoft Graph unless an app names another (e.g.
119
+ // Exchange Online).
120
+ async function mint(c, resource = GRAPH) {
121
+ const tenant = c.config.tenantId ?? "";
122
+ if (!GUID.test(tenant))
123
+ throw new Error("Microsoft connection has no tenantId");
124
+ const { ok, status, body } = await requestToken(tenant, {
125
+ grant_type: "client_credentials",
126
+ scope: `${resource}/.default`,
127
+ ...credentials(c),
128
+ });
129
+ if (!ok || !body.access_token) {
130
+ const why = (body.error_description ?? body.error ?? String(status)).split(/\r?\n/)[0];
131
+ throw new ConnectError(`Microsoft refused the app's sign-in: ${why.slice(0, 300)}`);
132
+ }
133
+ return { token: body.access_token, expiresIn: body.expires_in ?? 3600 };
134
+ }
135
+ // Cached per credential, so a new certificate or secret never reuses an old token.
136
+ const cacheKey = (c, resource) => createHash("sha256")
137
+ .update([
138
+ resource,
139
+ c.config.auth,
140
+ c.config.tenantId,
141
+ c.config.clientId,
142
+ c.config.certificate,
143
+ c.secret.client_secret,
144
+ ].join("\n"))
145
+ .digest("hex");
146
+ async function send(token, request, origin = GRAPH) {
147
+ // Uploads send the file's bytes; everything else is JSON.
148
+ const raw = request.body instanceof Uint8Array;
149
+ const res = await fetchImpl(`${origin}${request.path}`, {
150
+ method: request.method,
151
+ headers: {
152
+ authorization: `Bearer ${token}`,
153
+ accept: "application/json",
154
+ ...request.headers,
155
+ ...(request.body === undefined
156
+ ? {}
157
+ : { "content-type": raw ? "application/octet-stream" : "application/json" }),
158
+ },
159
+ body: request.body === undefined
160
+ ? undefined
161
+ : raw
162
+ ? request.body
163
+ : JSON.stringify(request.body),
164
+ });
165
+ const text = await res.text();
166
+ const asText = request.responseType === "text" && res.ok;
167
+ return { status: res.status, data: asText ? text : text ? safeJson(text) : null };
168
+ }
169
+ /** A request to Graph, or to `origin` with a token for it, re-minting the token once on a
170
+ * 401. */
171
+ async function call(connection, request, origin = GRAPH) {
172
+ const key = cacheKey(connection, origin);
173
+ const token = () => tokens.get(key, () => mint(connection, origin));
174
+ let res = await send(await token(), request, origin);
175
+ if (res.status === 401) {
176
+ tokens.forget(key);
177
+ res = await send(await token(), request, origin);
178
+ }
179
+ return res;
180
+ }
181
+ /** Compares the app's permissions, read from fresh Graph and Exchange Online tokens, with
182
+ * what the app's capabilities need. Exchange Online ones are shown with EXCHANGE_SUFFIX.
183
+ * Microsoft calls its tokens opaque to clients, so an unreadable one only means this
184
+ * readout is unavailable; the connection itself still works. */
185
+ async function permissions(connection, input) {
186
+ const [graph, exchange] = await Promise.all([mint(connection), mint(connection, EXCHANGE)]);
187
+ let roles;
188
+ try {
189
+ roles = [
190
+ ...tokenRoles(graph.token),
191
+ ...tokenRoles(exchange.token).map((r) => `${r}${EXCHANGE_SUFFIX}`),
192
+ ];
193
+ }
194
+ catch (error) {
195
+ if (!(error instanceof ConnectError))
196
+ throw error;
197
+ return {
198
+ values: [],
199
+ warning: "Could not read the app's permissions from Microsoft's token.",
200
+ };
201
+ }
202
+ return { values: roles, warning: compareRoles(roles, input) };
203
+ }
204
+ const provider = {
205
+ async connect(fields, existing) {
206
+ const tenantId = fields.tenant_id?.trim().toLowerCase() ?? "";
207
+ const clientId = fields.client_id?.trim().toLowerCase() ?? "";
208
+ const clientSecret = fields.client_secret?.trim() ?? "";
209
+ if (!GUID.test(tenantId))
210
+ throw new ConnectError("Enter the Directory (tenant) ID from the app's Overview page");
211
+ if (!GUID.test(clientId))
212
+ throw new ConnectError("Enter the Application (client) ID from the app's Overview page");
213
+ let connection;
214
+ if (clientSecret)
215
+ connection = {
216
+ config: { auth: "client_secret", tenantId, clientId },
217
+ secret: { client_secret: clientSecret },
218
+ };
219
+ else {
220
+ const certificate = existing?.config.certificate;
221
+ const privateKey = existing?.secret.private_key;
222
+ if (!certificate || !privateKey)
223
+ throw new ConnectError("Create the certificate and upload it to the app, or enter a client secret");
224
+ connection = {
225
+ config: { auth: "certificate", tenantId, clientId, certificate },
226
+ secret: { private_key: privateKey },
227
+ };
228
+ }
229
+ const { token } = await mint(connection);
230
+ const servicePrincipalId = tokenClaim(token, "oid");
231
+ if (!servicePrincipalId)
232
+ throw new Error("Microsoft token has no oid claim");
233
+ if (tokenClaim(token, "tid") !== tenantId)
234
+ throw new ConnectError("Microsoft signed the app in to another organization");
235
+ // A certificate made for rotating stays waiting with the connection.
236
+ const next = existing?.config.nextCertificate;
237
+ return {
238
+ config: {
239
+ ...connection.config,
240
+ servicePrincipalId,
241
+ ...(next ? { nextCertificate: next } : {}),
242
+ },
243
+ secret: {
244
+ ...connection.secret,
245
+ ...(next && existing?.secret.next_private_key
246
+ ? { next_private_key: existing.secret.next_private_key }
247
+ : {}),
248
+ },
249
+ accountLabel: tenantId,
250
+ };
251
+ },
252
+ async verify(connection) {
253
+ await mint(connection);
254
+ },
255
+ ...(sharedApp ? { approval: sharedApproval(sharedApp) } : {}),
256
+ };
257
+ function sharedApproval(app) {
258
+ return {
259
+ clientId: app.clientId,
260
+ consentUrl({ state, redirectUri }) {
261
+ const q = new URLSearchParams({
262
+ client_id: app.clientId,
263
+ scope: `${GRAPH}/.default`,
264
+ redirect_uri: redirectUri,
265
+ state,
266
+ });
267
+ return `${LOGIN}/organizations/v2.0/adminconsent?${q}`;
268
+ },
269
+ signInUrl({ tenant, state, redirectUri, nonce }) {
270
+ if (!GUID.test(tenant))
271
+ throw new ConnectError(`Microsoft did not say which organization approved ${brand().productName}`);
272
+ const q = new URLSearchParams({
273
+ client_id: app.clientId,
274
+ response_type: "code",
275
+ response_mode: "query",
276
+ scope: "openid",
277
+ redirect_uri: redirectUri,
278
+ state,
279
+ nonce,
280
+ });
281
+ return `${LOGIN}/${tenant}/oauth2/v2.0/authorize?${q}`;
282
+ },
283
+ async proveTenant({ tenant, code, redirectUri, nonce }) {
284
+ const { ok, status, body } = await requestToken(tenant, {
285
+ grant_type: "authorization_code",
286
+ client_id: app.clientId,
287
+ client_secret: app.clientSecret,
288
+ code,
289
+ redirect_uri: redirectUri,
290
+ scope: "openid",
291
+ });
292
+ if (!ok || !body.id_token)
293
+ throw new ConnectError(`Microsoft did not confirm the sign-in (${body.error ?? status})`);
294
+ // Straight from Microsoft's token endpoint over TLS with the shared app's secret, so the token's
295
+ // signature need not be checked again (OpenID Connect Core 3.1.3.7).
296
+ const claims = tokenClaims(body.id_token);
297
+ const issuer = `${LOGIN}/${tenant}/v2.0`;
298
+ const fresh = typeof claims.exp === "number" && claims.exp * 1000 > Date.now();
299
+ if (claims.aud !== app.clientId ||
300
+ claims.iss !== issuer ||
301
+ claims.nonce !== nonce ||
302
+ !fresh)
303
+ throw new ConnectError("Microsoft's sign-in did not match this approval. Start again.");
304
+ // A guest signs in to the tenant but belongs to another one (idp names its home).
305
+ const guest = typeof claims.idp === "string" && !claims.idp.includes(tenant);
306
+ if (claims.tid !== tenant || guest)
307
+ throw new ConnectError(`Sign in with an account of the organization that approved ${brand().productName}, not a guest or another organization's account.`);
308
+ // The approval's admin_consent=true came through the browser, so the account signing in
309
+ // must itself hold a role that can approve apps. `wids` is in the id token only when
310
+ // the shared app's manifest sets groupMembershipClaims to DirectoryRole (or All).
311
+ const roles = Array.isArray(claims.wids) ? claims.wids : null;
312
+ if (!roles)
313
+ throw new ConnectError(`Microsoft did not say whether this account is an administrator, so the approval cannot be confirmed. Contact ${brand().productName}.`);
314
+ if (!roles.some((r) => typeof r === "string" && APPROVER_ROLES.has(r.toLowerCase())))
315
+ throw new ConnectError("Sign in with an administrator who can approve apps for your organization (Global Administrator, Privileged Role Administrator, Cloud Application Administrator or Application Administrator).");
316
+ },
317
+ async connect(rawTenant) {
318
+ const tenant = rawTenant.trim().toLowerCase();
319
+ if (!GUID.test(tenant))
320
+ throw new ConnectError(`Microsoft did not say which organization approved ${brand().productName}`);
321
+ const connection = {
322
+ config: { auth: SHARED_APP_AUTH, tenantId: tenant, clientId: app.clientId },
323
+ secret: {},
324
+ };
325
+ let failure = null;
326
+ for (const wait of [0, ...CONNECT_RETRY_MS]) {
327
+ if (wait)
328
+ await sleep(wait);
329
+ try {
330
+ // A fresh token each try: one minted before the approval applied lacks its role.
331
+ const { token } = await mint(connection);
332
+ const org = await send(token, {
333
+ method: "GET",
334
+ path: "/v1.0/organization?$select=displayName,verifiedDomains",
335
+ });
336
+ if (org.status !== 200) {
337
+ failure = new ConnectError(`Microsoft answered ${org.status} when ${brand().productName} checked the connection`);
338
+ continue;
339
+ }
340
+ const servicePrincipalId = tokenClaim(token, "oid");
341
+ if (!servicePrincipalId)
342
+ throw new Error("Microsoft token has no oid claim");
343
+ const info = org.data.value?.[0];
344
+ const domain = info?.verifiedDomains?.find((d) => d.isDefault)?.name;
345
+ const name = info?.displayName ?? tenant;
346
+ return {
347
+ config: { ...connection.config, servicePrincipalId, organization: name },
348
+ secret: {},
349
+ accountLabel: domain ? `${name} (${domain})` : name,
350
+ };
351
+ }
352
+ catch (error) {
353
+ if (!(error instanceof ConnectError))
354
+ throw error;
355
+ failure = error;
356
+ }
357
+ }
358
+ throw failure;
359
+ },
360
+ };
361
+ }
362
+ // Cancels an upload session. Its address is pre-authenticated, so no token is sent, and
363
+ // only a SharePoint address is called.
364
+ async function cancelUpload(uploadUrl) {
365
+ const url = new URL(uploadUrl);
366
+ if (url.protocol !== "https:" || !/\.sharepoint\.com$/i.test(url.hostname))
367
+ throw new Error(`Upload session address is not SharePoint: ${url.hostname}`);
368
+ return (await fetchImpl(url.toString(), { method: "DELETE" })).status;
369
+ }
370
+ // An administrator signing in to the connection's own app, for what Microsoft allows only on
371
+ // a person's behalf (publishing to the Teams app catalog). The token is used at once and
372
+ // never kept.
373
+ const delegated = {
374
+ signInUrl(c, input) {
375
+ const { tenantId, clientId } = c.config;
376
+ if (!GUID.test(tenantId ?? "") || !GUID.test(clientId ?? ""))
377
+ throw new Error("Microsoft connection has no tenantId or clientId");
378
+ const q = new URLSearchParams({
379
+ client_id: clientId,
380
+ response_type: "code",
381
+ response_mode: "query",
382
+ scope: `openid ${input.scope}`,
383
+ redirect_uri: input.redirectUri,
384
+ state: input.state,
385
+ nonce: input.nonce,
386
+ prompt: "select_account",
387
+ });
388
+ return `${LOGIN}/${tenantId}/oauth2/v2.0/authorize?${q}`;
389
+ },
390
+ /** The code redeemed with the connection's own credential. Returns the access token and
391
+ * the id token's claims (straight from Microsoft over TLS, so not signature-checked). */
392
+ async redeem(c, input) {
393
+ const { ok, status, body } = await requestToken(c.config.tenantId ?? "", {
394
+ grant_type: "authorization_code",
395
+ code: input.code,
396
+ redirect_uri: input.redirectUri,
397
+ scope: `openid ${input.scope}`,
398
+ ...credentials(c),
399
+ });
400
+ if (!ok || !body.access_token || !body.id_token) {
401
+ const why = (body.error_description ?? body.error ?? String(status)).split(/\r?\n/)[0];
402
+ throw new ConnectError(`Microsoft did not confirm the sign-in: ${why.slice(0, 300)}`);
403
+ }
404
+ return { accessToken: body.access_token, claims: tokenClaims(body.id_token) };
405
+ },
406
+ };
407
+ return { provider, call, permissions, cancelUpload, delegated };
408
+ }
409
+ /** Apps that need only the Microsoft connection: Outlook mail and calendar (mailboxes are
410
+ * reached through the customer's Exchange role assignments to the app, scoped to the
411
+ * branding's mailbox group, never through Graph Mail or Calendars permissions), Teams, Entra ID
412
+ * and Intune. `keep`: the app's own choice fields kept in its config; nothing else the form
413
+ * sends is, so no field can shadow the provider's config. */
414
+ export function mailboxVendor(ms, keep = []) {
415
+ return {
416
+ async connect(fields, provider) {
417
+ if (!provider)
418
+ throw new Error("Microsoft apps connect through the Microsoft connection");
419
+ const config = Object.fromEntries(keep.filter((k) => fields[k]).map((k) => [k, fields[k]]));
420
+ return { config, secret: {}, accountLabel: provider.accountLabel };
421
+ },
422
+ call: ms.call,
423
+ checks: { permissions: ms.permissions },
424
+ };
425
+ }
426
+ // A Microsoft app keeps its own choice fields (its JSON) and nothing else.
427
+ export const microsoftApp = (id, toolset) => ({
428
+ id,
429
+ create: ({ microsoft, connector }) => ({
430
+ vendor: mailboxVendor(microsoft, choiceFields(connector)),
431
+ toolset,
432
+ }),
433
+ });
434
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../connectors/microsoft/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ {
2
+ "id": "microsoft",
3
+ "name": "Microsoft 365",
4
+ "logo": "/brand/registry/microsoft-365.svg",
5
+ "identity": "An app you register in your own Microsoft Entra tenant. Your Microsoft apps here share it: you enter its details once. It reaches only what you grant it, and you can remove it at any time.",
6
+ "sharedIdentity": "{brand.productName}'s shared app, approved by your administrator in your Microsoft Entra tenant. Your Microsoft apps here share it. It reaches only what was approved, and you can remove it at any time under Enterprise applications.",
7
+ "accountKey": "tenantId",
8
+ "principalKeys": ["clientId", "servicePrincipalId"],
9
+ "certificate": true,
10
+ "setup": [
11
+ {
12
+ "title": "Register the {brand.microsoft.appName} app",
13
+ "body": "Every Microsoft app you set up here uses this one app registration, for this organization only and with no redirect address. Keep its Application (client) ID and Directory (tenant) ID for step 3.",
14
+ "link": {
15
+ "label": "Open App registrations",
16
+ "href": "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade"
17
+ },
18
+ "click": [
19
+ "Sign in to the Microsoft Entra admin center as at least an Application Developer.",
20
+ "Go to Entra ID, then App registrations, and select New registration.",
21
+ "Name it {brand.microsoft.appName}. Under Supported account types choose Single tenant only. Leave the redirect address empty and select Register.",
22
+ "On the app's Overview page, copy the Application (client) ID and the Directory (tenant) ID."
23
+ ],
24
+ "copy": [
25
+ "az login --allow-no-subscriptions",
26
+ "APP_ID=$(az ad app create --display-name \"{brand.microsoft.appName}\" --sign-in-audience AzureADMyOrg --query appId -o tsv)",
27
+ "az ad sp create --id $APP_ID",
28
+ "echo \"Application (client) ID: $APP_ID\"; echo \"Directory (tenant) ID: $(az account show --query tenantId -o tsv)\""
29
+ ],
30
+ "shell": "bash",
31
+ "confirm": "microsoft-app-registered"
32
+ },
33
+ {
34
+ "title": "Add its sign-in certificate",
35
+ "body": "Create the certificate here and download it, then upload it to the app. {brand.productName} signs in with the matching private key, which never leaves {brand.productName} and is never shown to anyone.",
36
+ "certificate": true,
37
+ "click": [
38
+ "Select Download certificate below (or Create certificate if there is none yet) to get {brand.resourcePrefix}-agents.cer.",
39
+ "In App registrations, open {brand.microsoft.appName}.",
40
+ "Select Certificates & secrets, then Certificates, then Upload certificate.",
41
+ "Choose {brand.resourcePrefix}-agents.cer and select Add."
42
+ ],
43
+ "copy": [
44
+ "# In the folder you downloaded {brand.resourcePrefix}-agents.cer to, in the same shell as step 1 (or set APP_ID to the Application (client) ID)",
45
+ "az ad app credential reset --id $APP_ID --cert \"@{brand.resourcePrefix}-agents.cer\" --append"
46
+ ],
47
+ "shell": "bash"
48
+ }
49
+ ],
50
+ "fields": [
51
+ {
52
+ "id": "tenant_id",
53
+ "label": "Directory (tenant) ID",
54
+ "help": "On the app's Overview page.",
55
+ "placeholder": "00000000-0000-0000-0000-000000000000",
56
+ "secret": false
57
+ },
58
+ {
59
+ "id": "client_id",
60
+ "label": "Application (client) ID",
61
+ "help": "On the app's Overview page.",
62
+ "placeholder": "00000000-0000-0000-0000-000000000000",
63
+ "secret": false
64
+ },
65
+ {
66
+ "id": "client_secret",
67
+ "label": "Client secret",
68
+ "help": "Only if you use a client secret instead of the certificate: the secret's Value, from Certificates & secrets.",
69
+ "secret": true,
70
+ "optional": true
71
+ }
72
+ ],
73
+ "addAgent": [
74
+ {
75
+ "title": "Create the agent's mailbox",
76
+ "body": "A shared mailbox: it needs no license, and nobody signs in to it. Its address is the agent's email.",
77
+ "apps": ["outlook-mail", "outlook-calendar"],
78
+ "link": {
79
+ "label": "Open Shared mailboxes",
80
+ "href": "https://admin.microsoft.com/Adminportal/Home#/SharedMailbox"
81
+ },
82
+ "click": [
83
+ "In the Microsoft 365 admin center, go to Teams & groups, then Shared mailboxes. Select Show all if you do not see Teams & groups.",
84
+ "Select Add a shared mailbox. Enter the name \"{agentName}\" and the email address \"{agentEmail}\", then select Save changes.",
85
+ "Skip adding members: the agent needs none."
86
+ ],
87
+ "copy": [
88
+ "# Needs the ExchangeOnlineManagement module: Install-Module ExchangeOnlineManagement -Scope CurrentUser",
89
+ "Connect-ExchangeOnline",
90
+ "New-Mailbox -Shared -Name '{agentName}' -DisplayName '{agentName}' -PrimarySmtpAddress '{agentEmail}'"
91
+ ],
92
+ "shell": "powershell"
93
+ },
94
+ {
95
+ "title": "Add it to {brand.microsoft.mailboxGroup}",
96
+ "body": "Membership of this group is what lets the app open the mailbox, and nothing else. Microsoft can take up to about 2 hours to apply it. Check access tells you when it works.",
97
+ "apps": ["outlook-mail", "outlook-calendar"],
98
+ "agentCheck": true,
99
+ "link": {
100
+ "label": "Open Mail-enabled security groups",
101
+ "href": "https://admin.exchange.microsoft.com/#/groups"
102
+ },
103
+ "click": [
104
+ "In the Exchange admin center, go to Recipients, then Groups, then Mail-enabled security.",
105
+ "Open {brand.microsoft.mailboxGroup}, then Members, and select View all and manage members.",
106
+ "Add \"{agentEmail}\" and select Save changes."
107
+ ],
108
+ "copy": [
109
+ "# Needs the ExchangeOnlineManagement module: Install-Module ExchangeOnlineManagement -Scope CurrentUser",
110
+ "Connect-ExchangeOnline",
111
+ "Add-DistributionGroupMember -Identity '{brand.microsoft.mailboxGroup}' -Member '{agentEmail}' -BypassSecurityGroupManagerCheck",
112
+ "Test-ServicePrincipalAuthorization -Identity '{clientId}' -Resource '{agentEmail}'"
113
+ ],
114
+ "shell": "powershell"
115
+ }
116
+ ],
117
+ "revoke": "In the Microsoft Entra admin center, open App registrations, then {brand.microsoft.appName}, and delete it, or remove its certificate or secret. Every Microsoft app connected here stops at once."
118
+ }