@mitralab.io/platform-sdk 1.1.0-beta.2 → 1.1.0-beta.3

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.
package/README.md CHANGED
@@ -78,6 +78,30 @@ Authentication state is stored under `mitra_auth_{appId}`. Before each authentic
78
78
 
79
79
  The generated-application authentication flow is Google SSO. The old native `signIn` and `signUp` names fail locally with `UNSUPPORTED_AUTH_METHOD` because IAM has no email/password endpoints. Deprecated login bindings remain available only through the legacy reexports.
80
80
 
81
+ ### Signing in as a process
82
+
83
+ Google and Microsoft SSO both need a person to complete a redirect. A process that runs on its own -
84
+ a cron, a background collector, another service - signs in with an api key created in
85
+ **Settings -> API keys**:
86
+
87
+ ```typescript
88
+ const mitra = createClient({ appId, apiUrl, apiKey: process.env.MITRA_API_KEY })
89
+
90
+ await mitra.auth.signInWithApiKey()
91
+ ```
92
+
93
+ Which key to create depends on what the process does. A **Business** key reaches the published
94
+ product: read and write records, run queries, execute functions and integrations. A **Developer**
95
+ key adds authoring. An **Administrator** key belongs to a workspace rather than a product; the SDK
96
+ issues this app's token from it, and the platform decides on that call what the key's owner
97
+ actually reaches here, so the key never widens anyone's access.
98
+
99
+ This session is not written to storage and carries no refresh token. The key does not expire and is
100
+ revoked by deleting it, so renewing means signing in again rather than holding a second secret.
101
+
102
+ **Server runtimes only.** In a browser the key would be served to every visitor inside the bundle,
103
+ so the method refuses to run when a `window` exists.
104
+
81
105
  An embedded preview can adopt the app-scoped session it receives from the platform without exchanging it:
82
106
 
83
107
  ```typescript
package/dist/index.cjs CHANGED
@@ -60,7 +60,7 @@ function stripTrailingSlashes(value) {
60
60
  }
61
61
 
62
62
  // src/client.ts
63
- var import_sdk_core9 = require("@mitralab.io/sdk-core");
63
+ var import_sdk_core10 = require("@mitralab.io/sdk-core");
64
64
 
65
65
  // src/utils/http-client.ts
66
66
  var bearerCredentialPattern = /(Bearer\s+)\S+/gi;
@@ -396,10 +396,46 @@ function adoptLegacySession(session) {
396
396
  }
397
397
 
398
398
  // src/modules/auth.ts
399
- var import_sdk_core2 = require("@mitralab.io/sdk-core");
399
+ var import_sdk_core3 = require("@mitralab.io/sdk-core");
400
400
 
401
- // src/modules/google-auth.ts
401
+ // src/modules/api-key-auth.ts
402
402
  var import_sdk_core = require("@mitralab.io/sdk-core");
403
+ async function withoutLeakingKey(apiKey, call) {
404
+ try {
405
+ return await call();
406
+ } catch (error) {
407
+ if (error instanceof MitraApiError && error.message.includes(apiKey)) {
408
+ throw new MitraApiError(error.message.split(apiKey).join("[REDACTED]"), error.status);
409
+ }
410
+ throw error;
411
+ }
412
+ }
413
+ function assertServerRuntime() {
414
+ if (globalThis.window !== void 0) {
415
+ throw new MitraApiError(
416
+ "Api key authentication runs only on a server. In a browser the key would be served to every visitor; sign in with Google or Microsoft instead.",
417
+ 400
418
+ );
419
+ }
420
+ }
421
+ async function resolveApiKeySession(publicClient, appId, apiKey) {
422
+ return (0, import_sdk_core.resolveApiKeyToken)(
423
+ (path, init) => withoutLeakingKey(
424
+ apiKey,
425
+ () => publicClient.request(path, {
426
+ method: "POST",
427
+ ...init.body === void 0 ? {} : { body: init.body },
428
+ ...init.bearer === void 0 ? {} : { headers: { Authorization: `Bearer ${init.bearer}` } }
429
+ })
430
+ ),
431
+ appId,
432
+ apiKey,
433
+ coreErrors
434
+ );
435
+ }
436
+
437
+ // src/modules/google-auth.ts
438
+ var import_sdk_core2 = require("@mitralab.io/sdk-core");
403
439
  var RESULT_TYPE = "mitra-oauth-result";
404
440
  var PROVIDER_LABELS = {
405
441
  google: "Google",
@@ -410,7 +446,7 @@ var POPUP_HEIGHT = 600;
410
446
  var POPUP_TIMEOUT_MS = 5 * 60 * 1e3;
411
447
  var POPUP_CLOSED_POLL_MS = 500;
412
448
  function expectAuthTokenResponse(value) {
413
- const response = (0, import_sdk_core.expectObject)(
449
+ const response = (0, import_sdk_core2.expectObject)(
414
450
  value,
415
451
  "Authentication token response",
416
452
  coreErrors
@@ -685,6 +721,7 @@ function isDefinitiveRefreshFailure(error) {
685
721
  }
686
722
  var AuthModule = class {
687
723
  appId;
724
+ defaultApiKey;
688
725
  _currentUser = null;
689
726
  #accessToken = null;
690
727
  #refreshToken = null;
@@ -705,6 +742,7 @@ var AuthModule = class {
705
742
  const apiUrl = stripTrailingSlashes(
706
743
  options.apiUrl ?? (trimmedIamBaseUrl.endsWith("/iam") ? trimmedIamBaseUrl.slice(0, -"/iam".length) : trimmedIamBaseUrl)
707
744
  );
745
+ this.defaultApiKey = options.apiKey;
708
746
  this.storageKey = `mitra_auth_${appId}`;
709
747
  this.publicClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => null });
710
748
  this.authedClient = new HttpClient({
@@ -713,7 +751,7 @@ var AuthModule = class {
713
751
  beforeAuthenticatedRequest: () => this.ensureFreshSession().then(() => void 0),
714
752
  onUnauthorized: (requestToken) => this.handleUnauthorized(requestToken)
715
753
  });
716
- this.currentUserApi = (0, import_sdk_core2.createAuthModule)(this.authedClient, coreErrors);
754
+ this.currentUserApi = (0, import_sdk_core3.createAuthModule)(this.authedClient, coreErrors);
717
755
  this.googleAuth = new GoogleAuthFlow({
718
756
  appId,
719
757
  apiUrl,
@@ -791,6 +829,51 @@ var AuthModule = class {
791
829
  async signInWithGoogle(options = {}) {
792
830
  return this.establishSession(await this.googleAuth.signIn(options));
793
831
  }
832
+ /**
833
+ * Signs in as a process, with an api key instead of a person's SSO session.
834
+ *
835
+ * Use this where nobody can complete a redirect: a cron, a background collector, another
836
+ * service. Create the key in Settings -> API keys. A Business key reaches the published
837
+ * product, a Developer key adds authoring, and an Administrator key belongs to a workspace -
838
+ * from that one the platform issues this app's token, deciding then and there what the key's
839
+ * owner actually reaches here.
840
+ *
841
+ * The session is not written to storage and carries no refresh token: the key does not
842
+ * expire and is revoked by deleting it, so renewing means signing in again.
843
+ *
844
+ * Server runtimes only. In a browser the key would ship inside the bundle, so this method
845
+ * refuses to run there.
846
+ *
847
+ * @param apiKey - The key to use. Defaults to `apiKey` from the client configuration.
848
+ * @returns The user the key belongs to.
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * const mitra = createClient({ appId, apiUrl, apiKey: process.env.MITRA_API_KEY });
853
+ * await mitra.auth.signInWithApiKey();
854
+ * ```
855
+ */
856
+ async signInWithApiKey(apiKey = this.defaultApiKey) {
857
+ assertServerRuntime();
858
+ if (!apiKey?.trim()) {
859
+ throw new MitraApiError(
860
+ "No api key was given. Pass one to signInWithApiKey, or set apiKey when creating the client.",
861
+ 400
862
+ );
863
+ }
864
+ const token = await resolveApiKeySession(this.publicClient, this.appId, apiKey);
865
+ if (!this.belongsToConfiguredApp(token)) {
866
+ throw new MitraApiError("This api key does not reach the app this client is configured for.", 403);
867
+ }
868
+ this.invalidatePendingRefreshes();
869
+ this.#accessToken = token;
870
+ this.#refreshToken = null;
871
+ const user = await this.getCurrentUser();
872
+ this._currentUser = user;
873
+ this.notifySessionListeners();
874
+ this.notifyListeners();
875
+ return user;
876
+ }
794
877
  /**
795
878
  * Completes a Google redirect response from `#codeMitra` and `#stateMitra`.
796
879
  *
@@ -1270,11 +1353,11 @@ var AuthModule = class {
1270
1353
  };
1271
1354
 
1272
1355
  // src/modules/entities.ts
1273
- var import_sdk_core3 = require("@mitralab.io/sdk-core");
1356
+ var import_sdk_core4 = require("@mitralab.io/sdk-core");
1274
1357
  var EntitiesModule = class _EntitiesModule {
1275
1358
  constructor(httpClient, _dataSourceId) {
1276
1359
  this.httpClient = httpClient;
1277
- this.core = (0, import_sdk_core3.createEntitiesModule)(httpClient, coreErrors);
1360
+ this.core = (0, import_sdk_core4.createEntitiesModule)(httpClient, coreErrors);
1278
1361
  }
1279
1362
  core;
1280
1363
  static createProxy(httpClient, dataSourceId) {
@@ -1293,7 +1376,7 @@ var EntitiesModule = class _EntitiesModule {
1293
1376
  * Records now resolve the app from authenticated context instead of a data source path.
1294
1377
  */
1295
1378
  setDataSourceId(_dataSourceId) {
1296
- this.core = (0, import_sdk_core3.createEntitiesModule)(this.httpClient, coreErrors);
1379
+ this.core = (0, import_sdk_core4.createEntitiesModule)(this.httpClient, coreErrors);
1297
1380
  }
1298
1381
  getTable(tableName) {
1299
1382
  return this.core.getTable(tableName);
@@ -1301,11 +1384,11 @@ var EntitiesModule = class _EntitiesModule {
1301
1384
  };
1302
1385
 
1303
1386
  // src/modules/functions.ts
1304
- var import_sdk_core4 = require("@mitralab.io/sdk-core");
1387
+ var import_sdk_core5 = require("@mitralab.io/sdk-core");
1305
1388
  var FunctionsModule = class {
1306
1389
  core;
1307
1390
  constructor(httpClient) {
1308
- this.core = (0, import_sdk_core4.createFunctionsModule)(
1391
+ this.core = (0, import_sdk_core5.createFunctionsModule)(
1309
1392
  httpClient,
1310
1393
  { emptyInput: "omit-body", executeInvocationType: "sync" },
1311
1394
  coreErrors
@@ -1332,13 +1415,13 @@ var FunctionsModule = class {
1332
1415
  };
1333
1416
 
1334
1417
  // src/modules/integration.ts
1335
- var import_sdk_core5 = require("@mitralab.io/sdk-core");
1418
+ var import_sdk_core6 = require("@mitralab.io/sdk-core");
1336
1419
  var IntegrationModule = class {
1337
1420
  core;
1338
1421
  configs;
1339
1422
  constructor(httpClient) {
1340
- this.core = (0, import_sdk_core5.createIntegrationModule)(httpClient, coreErrors);
1341
- this.configs = (0, import_sdk_core5.createIntegrationAdminModule)(httpClient, coreErrors);
1423
+ this.core = (0, import_sdk_core6.createIntegrationModule)(httpClient, coreErrors);
1424
+ this.configs = (0, import_sdk_core6.createIntegrationAdminModule)(httpClient, coreErrors);
1342
1425
  }
1343
1426
  /** Lists the current app's integration configs without exposing admin mutations. */
1344
1427
  list(options) {
@@ -1357,11 +1440,11 @@ var IntegrationModule = class {
1357
1440
  };
1358
1441
 
1359
1442
  // src/modules/queries.ts
1360
- var import_sdk_core6 = require("@mitralab.io/sdk-core");
1443
+ var import_sdk_core7 = require("@mitralab.io/sdk-core");
1361
1444
  var QueriesModule = class {
1362
1445
  core;
1363
1446
  constructor(httpClient) {
1364
- this.core = (0, import_sdk_core6.createQueriesModule)(httpClient, coreErrors);
1447
+ this.core = (0, import_sdk_core7.createQueriesModule)(httpClient, coreErrors);
1365
1448
  }
1366
1449
  /**
1367
1450
  * @deprecated Preserved for Platform SDK 1.x source compatibility. Data Manager now resolves
@@ -1375,7 +1458,7 @@ var QueriesModule = class {
1375
1458
  };
1376
1459
 
1377
1460
  // src/modules/agent-tasks.ts
1378
- var import_sdk_core7 = require("@mitralab.io/sdk-core");
1461
+ var import_sdk_core8 = require("@mitralab.io/sdk-core");
1379
1462
 
1380
1463
  // src/modules/agent-session.ts
1381
1464
  var CONNECT_TIMEOUT_MS = 15e3;
@@ -1588,23 +1671,23 @@ var BrowserAgentTaskEventSource = class {
1588
1671
 
1589
1672
  // src/modules/agent-tasks.ts
1590
1673
  function createBrowserAgentTasksModule(httpClient, auth, apiUrl) {
1591
- const tasks = (0, import_sdk_core7.createAgentTasksModule)(httpClient, coreErrors);
1592
- const manager = (0, import_sdk_core7.createAgentTaskSessionManager)({
1674
+ const tasks = (0, import_sdk_core8.createAgentTasksModule)(httpClient, coreErrors);
1675
+ const manager = (0, import_sdk_core8.createAgentTaskSessionManager)({
1593
1676
  tasks,
1594
1677
  eventSource: new BrowserAgentTaskEventSource(auth, apiUrl)
1595
1678
  });
1596
- return (0, import_sdk_core7.withAgentTaskSessions)(tasks, manager);
1679
+ return (0, import_sdk_core8.withAgentTaskSessions)(tasks, manager);
1597
1680
  }
1598
1681
 
1599
1682
  // src/modules/agent-credentials.ts
1600
- var import_sdk_core8 = require("@mitralab.io/sdk-core");
1683
+ var import_sdk_core9 = require("@mitralab.io/sdk-core");
1601
1684
  function requireProvider(provider, allowed, flow) {
1602
1685
  if (!allowed.includes(provider)) {
1603
1686
  throw new TypeError(`${flow} does not support provider ${provider}.`);
1604
1687
  }
1605
1688
  }
1606
1689
  function createBrowserAgentCredentialsModule(httpClient) {
1607
- const core = (0, import_sdk_core8.createAgentCredentialsModule)(httpClient, coreErrors);
1690
+ const core = (0, import_sdk_core9.createAgentCredentialsModule)(httpClient, coreErrors);
1608
1691
  return {
1609
1692
  list: () => core.list(),
1610
1693
  listModels: (agentId) => core.listModels(agentId),
@@ -1637,7 +1720,7 @@ function createBrowserAgentCredentialsModule(httpClient) {
1637
1720
 
1638
1721
  // src/client.ts
1639
1722
  function expectAppInfoResponse(value) {
1640
- const response = (0, import_sdk_core9.expectObject)(
1723
+ const response = (0, import_sdk_core10.expectObject)(
1641
1724
  value,
1642
1725
  "App info response",
1643
1726
  coreErrors
@@ -1658,7 +1741,7 @@ function expectAppInfoResponse(value) {
1658
1741
  };
1659
1742
  }
1660
1743
  function createClient(config) {
1661
- const { appId, apiUrl, authPageUrl, onError } = config;
1744
+ const { appId, apiUrl, apiKey, authPageUrl, onError } = config;
1662
1745
  const gatewayUrl = stripTrailingSlashes(apiUrl);
1663
1746
  const iamUrl = `${gatewayUrl}/iam`;
1664
1747
  const dataManagerUrl = `${gatewayUrl}/data-manager`;
@@ -1666,7 +1749,7 @@ function createClient(config) {
1666
1749
  const integrationUrl = `${gatewayUrl}/integration`;
1667
1750
  const codeStudioUrl = `${gatewayUrl}/code-studio`;
1668
1751
  const copilotUrl = `${gatewayUrl}/copilot`;
1669
- const authModule = new AuthModule(appId, iamUrl, { apiUrl: gatewayUrl, authPageUrl });
1752
+ const authModule = new AuthModule(appId, iamUrl, { apiUrl: gatewayUrl, authPageUrl, apiKey });
1670
1753
  const authSession = getAuthSessionPort(authModule);
1671
1754
  const onUnauthorized = (requestToken) => authSession.handleUnauthorized(requestToken);
1672
1755
  const beforeAuthenticatedRequest = () => authModule.ensureFreshSession().then(() => void 0);
@@ -1689,7 +1772,7 @@ function createClient(config) {
1689
1772
  defaultHeaders
1690
1773
  });
1691
1774
  const functionsModule = new FunctionsModule(functionsHttpClient);
1692
- const publicFunctionsModule = (0, import_sdk_core9.createPublicFunctionsModule)(
1775
+ const publicFunctionsModule = (0, import_sdk_core10.createPublicFunctionsModule)(
1693
1776
  new HttpClient({ baseUrl: functionsUrl, getToken: () => null }),
1694
1777
  coreErrors
1695
1778
  );
@@ -1730,7 +1813,7 @@ function createClient(config) {
1730
1813
  });
1731
1814
  const appInfo = expectAppInfoResponse(
1732
1815
  await publicClient.get(
1733
- `/api/v1/apps/${(0, import_sdk_core9.encodePathSegment)(appId, "appId", coreErrors)}/info`
1816
+ `/api/v1/apps/${(0, import_sdk_core10.encodePathSegment)(appId, "appId", coreErrors)}/info`
1734
1817
  )
1735
1818
  );
1736
1819
  if (appInfo.dataSourceId) {
package/dist/index.d.cts CHANGED
@@ -216,6 +216,8 @@ type AuthStateChangeCallback = (user: User | null) => void;
216
216
  interface AuthModuleOptions {
217
217
  apiUrl?: string;
218
218
  authPageUrl?: string;
219
+ /** Default api key for `signInWithApiKey`, taken from the client configuration. */
220
+ apiKey?: string;
219
221
  }
220
222
  /**
221
223
  * Authentication module for managing user sessions.
@@ -233,6 +235,7 @@ interface AuthModuleOptions {
233
235
  declare class AuthModule {
234
236
  #private;
235
237
  private readonly appId;
238
+ private readonly defaultApiKey;
236
239
  private _currentUser;
237
240
  private sessionGeneration;
238
241
  private refreshFlight;
@@ -283,6 +286,31 @@ declare class AuthModule {
283
286
  * ```
284
287
  */
285
288
  signInWithGoogle(options?: GoogleSignInOptions): Promise<User>;
289
+ /**
290
+ * Signs in as a process, with an api key instead of a person's SSO session.
291
+ *
292
+ * Use this where nobody can complete a redirect: a cron, a background collector, another
293
+ * service. Create the key in Settings -> API keys. A Business key reaches the published
294
+ * product, a Developer key adds authoring, and an Administrator key belongs to a workspace -
295
+ * from that one the platform issues this app's token, deciding then and there what the key's
296
+ * owner actually reaches here.
297
+ *
298
+ * The session is not written to storage and carries no refresh token: the key does not
299
+ * expire and is revoked by deleting it, so renewing means signing in again.
300
+ *
301
+ * Server runtimes only. In a browser the key would ship inside the bundle, so this method
302
+ * refuses to run there.
303
+ *
304
+ * @param apiKey - The key to use. Defaults to `apiKey` from the client configuration.
305
+ * @returns The user the key belongs to.
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const mitra = createClient({ appId, apiUrl, apiKey: process.env.MITRA_API_KEY });
310
+ * await mitra.auth.signInWithApiKey();
311
+ * ```
312
+ */
313
+ signInWithApiKey(apiKey?: string | undefined): Promise<User>;
286
314
  /**
287
315
  * Completes a Google redirect response from `#codeMitra` and `#stateMitra`.
288
316
  *
@@ -593,6 +621,11 @@ interface MitraClientConfig {
593
621
  * Found in the Mitra Code Studio dashboard.
594
622
  */
595
623
  appId: string;
624
+ /**
625
+ * Api key for server-side authentication, used by `auth.signInWithApiKey()`.
626
+ * Never set this in code that reaches a browser: it would ship in the bundle.
627
+ */
628
+ apiKey?: string;
596
629
  /**
597
630
  * Base URL for the Mitra API (Kong Gateway).
598
631
  * Injected automatically via `VITE_MITRA_API_URL` environment variable
package/dist/index.d.ts CHANGED
@@ -216,6 +216,8 @@ type AuthStateChangeCallback = (user: User | null) => void;
216
216
  interface AuthModuleOptions {
217
217
  apiUrl?: string;
218
218
  authPageUrl?: string;
219
+ /** Default api key for `signInWithApiKey`, taken from the client configuration. */
220
+ apiKey?: string;
219
221
  }
220
222
  /**
221
223
  * Authentication module for managing user sessions.
@@ -233,6 +235,7 @@ interface AuthModuleOptions {
233
235
  declare class AuthModule {
234
236
  #private;
235
237
  private readonly appId;
238
+ private readonly defaultApiKey;
236
239
  private _currentUser;
237
240
  private sessionGeneration;
238
241
  private refreshFlight;
@@ -283,6 +286,31 @@ declare class AuthModule {
283
286
  * ```
284
287
  */
285
288
  signInWithGoogle(options?: GoogleSignInOptions): Promise<User>;
289
+ /**
290
+ * Signs in as a process, with an api key instead of a person's SSO session.
291
+ *
292
+ * Use this where nobody can complete a redirect: a cron, a background collector, another
293
+ * service. Create the key in Settings -> API keys. A Business key reaches the published
294
+ * product, a Developer key adds authoring, and an Administrator key belongs to a workspace -
295
+ * from that one the platform issues this app's token, deciding then and there what the key's
296
+ * owner actually reaches here.
297
+ *
298
+ * The session is not written to storage and carries no refresh token: the key does not
299
+ * expire and is revoked by deleting it, so renewing means signing in again.
300
+ *
301
+ * Server runtimes only. In a browser the key would ship inside the bundle, so this method
302
+ * refuses to run there.
303
+ *
304
+ * @param apiKey - The key to use. Defaults to `apiKey` from the client configuration.
305
+ * @returns The user the key belongs to.
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const mitra = createClient({ appId, apiUrl, apiKey: process.env.MITRA_API_KEY });
310
+ * await mitra.auth.signInWithApiKey();
311
+ * ```
312
+ */
313
+ signInWithApiKey(apiKey?: string | undefined): Promise<User>;
286
314
  /**
287
315
  * Completes a Google redirect response from `#codeMitra` and `#stateMitra`.
288
316
  *
@@ -593,6 +621,11 @@ interface MitraClientConfig {
593
621
  * Found in the Mitra Code Studio dashboard.
594
622
  */
595
623
  appId: string;
624
+ /**
625
+ * Api key for server-side authentication, used by `auth.signInWithApiKey()`.
626
+ * Never set this in code that reaches a browser: it would ship in the bundle.
627
+ */
628
+ apiKey?: string;
596
629
  /**
597
630
  * Base URL for the Mitra API (Kong Gateway).
598
631
  * Injected automatically via `VITE_MITRA_API_URL` environment variable
package/dist/index.js CHANGED
@@ -348,6 +348,42 @@ function adoptLegacySession(session) {
348
348
  // src/modules/auth.ts
349
349
  import { createAuthModule } from "@mitralab.io/sdk-core";
350
350
 
351
+ // src/modules/api-key-auth.ts
352
+ import { resolveApiKeyToken } from "@mitralab.io/sdk-core";
353
+ async function withoutLeakingKey(apiKey, call) {
354
+ try {
355
+ return await call();
356
+ } catch (error) {
357
+ if (error instanceof MitraApiError && error.message.includes(apiKey)) {
358
+ throw new MitraApiError(error.message.split(apiKey).join("[REDACTED]"), error.status);
359
+ }
360
+ throw error;
361
+ }
362
+ }
363
+ function assertServerRuntime() {
364
+ if (globalThis.window !== void 0) {
365
+ throw new MitraApiError(
366
+ "Api key authentication runs only on a server. In a browser the key would be served to every visitor; sign in with Google or Microsoft instead.",
367
+ 400
368
+ );
369
+ }
370
+ }
371
+ async function resolveApiKeySession(publicClient, appId, apiKey) {
372
+ return resolveApiKeyToken(
373
+ (path, init) => withoutLeakingKey(
374
+ apiKey,
375
+ () => publicClient.request(path, {
376
+ method: "POST",
377
+ ...init.body === void 0 ? {} : { body: init.body },
378
+ ...init.bearer === void 0 ? {} : { headers: { Authorization: `Bearer ${init.bearer}` } }
379
+ })
380
+ ),
381
+ appId,
382
+ apiKey,
383
+ coreErrors
384
+ );
385
+ }
386
+
351
387
  // src/modules/google-auth.ts
352
388
  import { expectObject } from "@mitralab.io/sdk-core";
353
389
  var RESULT_TYPE = "mitra-oauth-result";
@@ -635,6 +671,7 @@ function isDefinitiveRefreshFailure(error) {
635
671
  }
636
672
  var AuthModule = class {
637
673
  appId;
674
+ defaultApiKey;
638
675
  _currentUser = null;
639
676
  #accessToken = null;
640
677
  #refreshToken = null;
@@ -655,6 +692,7 @@ var AuthModule = class {
655
692
  const apiUrl = stripTrailingSlashes(
656
693
  options.apiUrl ?? (trimmedIamBaseUrl.endsWith("/iam") ? trimmedIamBaseUrl.slice(0, -"/iam".length) : trimmedIamBaseUrl)
657
694
  );
695
+ this.defaultApiKey = options.apiKey;
658
696
  this.storageKey = `mitra_auth_${appId}`;
659
697
  this.publicClient = new HttpClient({ baseUrl: iamBaseUrl, getToken: () => null });
660
698
  this.authedClient = new HttpClient({
@@ -741,6 +779,51 @@ var AuthModule = class {
741
779
  async signInWithGoogle(options = {}) {
742
780
  return this.establishSession(await this.googleAuth.signIn(options));
743
781
  }
782
+ /**
783
+ * Signs in as a process, with an api key instead of a person's SSO session.
784
+ *
785
+ * Use this where nobody can complete a redirect: a cron, a background collector, another
786
+ * service. Create the key in Settings -> API keys. A Business key reaches the published
787
+ * product, a Developer key adds authoring, and an Administrator key belongs to a workspace -
788
+ * from that one the platform issues this app's token, deciding then and there what the key's
789
+ * owner actually reaches here.
790
+ *
791
+ * The session is not written to storage and carries no refresh token: the key does not
792
+ * expire and is revoked by deleting it, so renewing means signing in again.
793
+ *
794
+ * Server runtimes only. In a browser the key would ship inside the bundle, so this method
795
+ * refuses to run there.
796
+ *
797
+ * @param apiKey - The key to use. Defaults to `apiKey` from the client configuration.
798
+ * @returns The user the key belongs to.
799
+ *
800
+ * @example
801
+ * ```typescript
802
+ * const mitra = createClient({ appId, apiUrl, apiKey: process.env.MITRA_API_KEY });
803
+ * await mitra.auth.signInWithApiKey();
804
+ * ```
805
+ */
806
+ async signInWithApiKey(apiKey = this.defaultApiKey) {
807
+ assertServerRuntime();
808
+ if (!apiKey?.trim()) {
809
+ throw new MitraApiError(
810
+ "No api key was given. Pass one to signInWithApiKey, or set apiKey when creating the client.",
811
+ 400
812
+ );
813
+ }
814
+ const token = await resolveApiKeySession(this.publicClient, this.appId, apiKey);
815
+ if (!this.belongsToConfiguredApp(token)) {
816
+ throw new MitraApiError("This api key does not reach the app this client is configured for.", 403);
817
+ }
818
+ this.invalidatePendingRefreshes();
819
+ this.#accessToken = token;
820
+ this.#refreshToken = null;
821
+ const user = await this.getCurrentUser();
822
+ this._currentUser = user;
823
+ this.notifySessionListeners();
824
+ this.notifyListeners();
825
+ return user;
826
+ }
744
827
  /**
745
828
  * Completes a Google redirect response from `#codeMitra` and `#stateMitra`.
746
829
  *
@@ -1623,7 +1706,7 @@ function expectAppInfoResponse(value) {
1623
1706
  };
1624
1707
  }
1625
1708
  function createClient(config) {
1626
- const { appId, apiUrl, authPageUrl, onError } = config;
1709
+ const { appId, apiUrl, apiKey, authPageUrl, onError } = config;
1627
1710
  const gatewayUrl = stripTrailingSlashes(apiUrl);
1628
1711
  const iamUrl = `${gatewayUrl}/iam`;
1629
1712
  const dataManagerUrl = `${gatewayUrl}/data-manager`;
@@ -1631,7 +1714,7 @@ function createClient(config) {
1631
1714
  const integrationUrl = `${gatewayUrl}/integration`;
1632
1715
  const codeStudioUrl = `${gatewayUrl}/code-studio`;
1633
1716
  const copilotUrl = `${gatewayUrl}/copilot`;
1634
- const authModule = new AuthModule(appId, iamUrl, { apiUrl: gatewayUrl, authPageUrl });
1717
+ const authModule = new AuthModule(appId, iamUrl, { apiUrl: gatewayUrl, authPageUrl, apiKey });
1635
1718
  const authSession = getAuthSessionPort(authModule);
1636
1719
  const onUnauthorized = (requestToken) => authSession.handleUnauthorized(requestToken);
1637
1720
  const beforeAuthenticatedRequest = () => authModule.ensureFreshSession().then(() => void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mitralab.io/platform-sdk",
3
- "version": "1.1.0-beta.2",
3
+ "version": "1.1.0-beta.3",
4
4
  "description": "JavaScript/TypeScript SDK for building apps on the Mitra Platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -62,7 +62,7 @@
62
62
  "url": "https://github.com/mitralab-dev/mitra-platform-sdk/issues"
63
63
  },
64
64
  "dependencies": {
65
- "@mitralab.io/sdk-core": "0.2.0-beta.1",
65
+ "@mitralab.io/sdk-core": "0.2.0-beta.2",
66
66
  "mitra-interactions-sdk": "1.0.60-beta.48"
67
67
  },
68
68
  "devDependencies": {