@camstack/core 0.1.37 → 0.1.39

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 (82) hide show
  1. package/dist/auth/auth-manager.d.ts +12 -1
  2. package/dist/auth/auth-manager.d.ts.map +1 -1
  3. package/dist/auth/scope-matcher.d.ts +8 -0
  4. package/dist/auth/scope-matcher.d.ts.map +1 -0
  5. package/dist/auth/totp-manager.d.ts +0 -1
  6. package/dist/auth/totp-manager.d.ts.map +1 -1
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +15 -0
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts.map +1 -1
  9. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +27 -6
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js.map +1 -1
  11. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +27 -6
  12. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs.map +1 -1
  13. package/dist/builtins/device-manager/device-config-contribution.d.ts +33 -0
  14. package/dist/builtins/device-manager/device-config-contribution.d.ts.map +1 -0
  15. package/dist/builtins/device-manager/device-manager.addon.d.ts +52 -17
  16. package/dist/builtins/device-manager/device-manager.addon.d.ts.map +1 -1
  17. package/dist/builtins/device-manager/device-manager.addon.js +285 -161
  18. package/dist/builtins/device-manager/device-manager.addon.js.map +1 -1
  19. package/dist/builtins/device-manager/device-manager.addon.mjs +286 -162
  20. package/dist/builtins/device-manager/device-manager.addon.mjs.map +1 -1
  21. package/dist/builtins/local-auth/auth-schema.d.ts +1 -0
  22. package/dist/builtins/local-auth/auth-schema.d.ts.map +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.d.ts +1 -0
  24. package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.js +354 -3
  26. package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
  27. package/dist/builtins/local-auth/local-auth.addon.mjs +355 -3
  28. package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
  29. package/dist/builtins/local-auth/oauth-grants.d.ts +46 -0
  30. package/dist/builtins/local-auth/oauth-grants.d.ts.map +1 -0
  31. package/dist/builtins/local-auth/oauth-session-manager.d.ts +51 -0
  32. package/dist/builtins/local-auth/oauth-session-manager.d.ts.map +1 -0
  33. package/dist/builtins/remote-access-orchestrator/enabled-providers-reconcile.d.ts +97 -0
  34. package/dist/builtins/remote-access-orchestrator/enabled-providers-reconcile.d.ts.map +1 -0
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +24 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts.map +1 -1
  37. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +136 -56
  38. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js.map +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +137 -57
  40. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs.map +1 -1
  41. package/dist/builtins/snapshot/index.js +1 -3
  42. package/dist/builtins/snapshot/index.js.map +1 -1
  43. package/dist/builtins/snapshot/index.mjs +1 -3
  44. package/dist/builtins/snapshot/index.mjs.map +1 -1
  45. package/dist/builtins/snapshot/snapshot.addon.d.ts.map +1 -1
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +428 -234
  49. package/dist/index.js.map +1 -1
  50. package/dist/index.mjs +428 -235
  51. package/dist/index.mjs.map +1 -1
  52. package/package.json +19 -37
  53. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts +0 -8
  54. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts.map +0 -1
  55. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js +0 -75
  56. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js.map +0 -1
  57. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs +0 -69
  58. package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs.map +0 -1
  59. package/dist/builtins/auth-orchestrator/index.d.ts +0 -2
  60. package/dist/builtins/auth-orchestrator/index.d.ts.map +0 -1
  61. package/dist/builtins/auth-orchestrator/index.js +0 -7
  62. package/dist/builtins/auth-orchestrator/index.mjs +0 -2
  63. package/dist/builtins/mesh-orchestrator/index.d.ts +0 -2
  64. package/dist/builtins/mesh-orchestrator/index.d.ts.map +0 -1
  65. package/dist/builtins/mesh-orchestrator/index.js +0 -7
  66. package/dist/builtins/mesh-orchestrator/index.mjs +0 -2
  67. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts +0 -9
  68. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts.map +0 -1
  69. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js +0 -113
  70. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js.map +0 -1
  71. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs +0 -107
  72. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs.map +0 -1
  73. package/dist/builtins/turn-orchestrator/index.d.ts +0 -2
  74. package/dist/builtins/turn-orchestrator/index.d.ts.map +0 -1
  75. package/dist/builtins/turn-orchestrator/index.js +0 -7
  76. package/dist/builtins/turn-orchestrator/index.mjs +0 -2
  77. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts +0 -34
  78. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts.map +0 -1
  79. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js +0 -126
  80. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js.map +0 -1
  81. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs +0 -120
  82. package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs.map +0 -1
@@ -2,6 +2,7 @@ import { SettingsStoreClient } from '@camstack/types';
2
2
  export declare const USERS_COLLECTION = "users";
3
3
  export declare const API_KEYS_COLLECTION = "api_keys";
4
4
  export declare const SCOPED_TOKENS_COLLECTION = "scoped_tokens";
5
+ export declare const OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
5
6
  /**
6
7
  * TOTP enrollment table — split off from `users` to avoid the
7
8
  * destructive drop-+-recreate migration `ensureTable` performs when a
@@ -1 +1 @@
1
- {"version":3,"file":"auth-schema.d.ts","sourceRoot":"","sources":["../../../src/builtins/local-auth/auth-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,eAAO,MAAM,gBAAgB,UAAU,CAAA;AACvC,eAAO,MAAM,mBAAmB,aAAa,CAAA;AAC7C,eAAO,MAAM,wBAAwB,kBAAkB,CAAA;AACvD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAwD/C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBjF"}
1
+ {"version":3,"file":"auth-schema.d.ts","sourceRoot":"","sources":["../../../src/builtins/local-auth/auth-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,eAAO,MAAM,gBAAgB,UAAU,CAAA;AACvC,eAAO,MAAM,mBAAmB,aAAa,CAAA;AAC7C,eAAO,MAAM,wBAAwB,kBAAkB,CAAA;AACvD,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AACzD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAoE/C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BjF"}
@@ -9,6 +9,7 @@ export declare class LocalAuthAddon extends BaseAddon<LocalAuthConfig> {
9
9
  private userManager;
10
10
  private apiKeyManager;
11
11
  private scopedTokenManager;
12
+ private oauthSessionManager;
12
13
  private totpManager;
13
14
  constructor();
14
15
  protected onInitialize(): Promise<ProviderRegistration[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"local-auth.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/local-auth/local-auth.addon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAA2B,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACpF,OAAO,EAAE,SAAS,EAAoD,MAAM,iBAAiB,CAAA;AA6B7F,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,qBAAa,cAAe,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC5D,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,WAAW,CAA2B;;cAI9B,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;cAgR/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5C;AAED,eAAe,cAAc,CAAA"}
1
+ {"version":3,"file":"local-auth.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/local-auth/local-auth.addon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAA+C,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACxG,OAAO,EAAE,SAAS,EAAoD,MAAM,iBAAiB,CAAA;AA+B7F,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,qBAAa,cAAe,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC5D,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,OAAO,CAAC,WAAW,CAA2B;;cAI9B,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;cA4U/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5C;AAED,eAAe,cAAc,CAAA"}
@@ -6243,7 +6243,12 @@ var AuthManager = class {
6243
6243
  provider: payload.provider,
6244
6244
  ...payload.email !== void 0 ? { email: payload.email } : {},
6245
6245
  ...payload.displayName !== void 0 ? { displayName: payload.displayName } : {},
6246
- ...payload.hubUrl !== void 0 ? { hubUrl: payload.hubUrl } : {}
6246
+ ...payload.hubUrl !== void 0 ? { hubUrl: payload.hubUrl } : {},
6247
+ ...payload.scopes !== void 0 ? { scopes: payload.scopes } : {},
6248
+ ...payload.redirectUri !== void 0 ? { redirectUri: payload.redirectUri } : {},
6249
+ ...payload.integrationId !== void 0 ? { integrationId: payload.integrationId } : {},
6250
+ ...payload.jti !== void 0 ? { jti: payload.jti } : {},
6251
+ ...payload.sessionId !== void 0 ? { sessionId: payload.sessionId } : {}
6247
6252
  };
6248
6253
  return import_jsonwebtoken.sign(body, this.jwtSecret, { expiresIn: ttlSec });
6249
6254
  }
@@ -6265,6 +6270,11 @@ var AuthManager = class {
6265
6270
  const email = typeof decoded["email"] === "string" ? decoded["email"] : void 0;
6266
6271
  const displayName = typeof decoded["displayName"] === "string" ? decoded["displayName"] : void 0;
6267
6272
  const hubUrl = typeof decoded["hubUrl"] === "string" ? decoded["hubUrl"] : void 0;
6273
+ const scopes = Array.isArray(decoded["scopes"]) ? decoded["scopes"] : void 0;
6274
+ const redirectUri = typeof decoded["redirectUri"] === "string" ? decoded["redirectUri"] : void 0;
6275
+ const integrationId = typeof decoded["integrationId"] === "string" ? decoded["integrationId"] : void 0;
6276
+ const jti = typeof decoded["jti"] === "string" ? decoded["jti"] : void 0;
6277
+ const sessionId = typeof decoded["sessionId"] === "string" ? decoded["sessionId"] : void 0;
6268
6278
  return {
6269
6279
  userId,
6270
6280
  username,
@@ -6272,7 +6282,12 @@ var AuthManager = class {
6272
6282
  provider,
6273
6283
  ...email !== void 0 ? { email } : {},
6274
6284
  ...displayName !== void 0 ? { displayName } : {},
6275
- ...hubUrl !== void 0 ? { hubUrl } : {}
6285
+ ...hubUrl !== void 0 ? { hubUrl } : {},
6286
+ ...scopes !== void 0 ? { scopes } : {},
6287
+ ...redirectUri !== void 0 ? { redirectUri } : {},
6288
+ ...integrationId !== void 0 ? { integrationId } : {},
6289
+ ...jti !== void 0 ? { jti } : {},
6290
+ ...sessionId !== void 0 ? { sessionId } : {}
6276
6291
  };
6277
6292
  } catch {
6278
6293
  return null;
@@ -6669,6 +6684,138 @@ var ScopedTokenManager = class {
6669
6684
  }
6670
6685
  };
6671
6686
  //#endregion
6687
+ //#region src/builtins/local-auth/oauth-session-manager.ts
6688
+ /**
6689
+ * Manages OAuth session records in the `oauth_sessions` collection.
6690
+ *
6691
+ * Each record represents one Alexa-style account link. Sessions can be
6692
+ * active (`revokedAt = null`) or revoked (`revokedAt = <unix-ms>`).
6693
+ * `list()` returns both — callers decide whether to filter.
6694
+ *
6695
+ * Mirrors the pattern of `ScopedTokenManager`: takes a `SettingsStoreClient`
6696
+ * in its constructor and performs all reads/writes via the store's
6697
+ * `insert`, `update`, `delete`, and `query` primitives.
6698
+ */
6699
+ var SESSIONS_COLLECTION = "oauth_sessions";
6700
+ function parseSession(data) {
6701
+ const id = data["id"];
6702
+ const userId = data["userId"];
6703
+ const username = data["username"];
6704
+ const integrationId = data["integrationId"];
6705
+ const scopes = data["scopes"];
6706
+ const createdAt = data["createdAt"];
6707
+ const lastUsedAt = data["lastUsedAt"];
6708
+ const revokedAt = data["revokedAt"];
6709
+ if (typeof id !== "string") throw new Error("oauth_sessions row: missing id");
6710
+ if (typeof userId !== "string") throw new Error("oauth_sessions row: missing userId");
6711
+ if (typeof username !== "string") throw new Error("oauth_sessions row: missing username");
6712
+ if (typeof integrationId !== "string") throw new Error("oauth_sessions row: missing integrationId");
6713
+ if (!Array.isArray(scopes)) throw new Error("oauth_sessions row: scopes must be an array");
6714
+ if (typeof createdAt !== "number") throw new Error("oauth_sessions row: missing createdAt");
6715
+ if (typeof lastUsedAt !== "number") throw new Error("oauth_sessions row: missing lastUsedAt");
6716
+ return {
6717
+ id,
6718
+ userId,
6719
+ username,
6720
+ integrationId,
6721
+ scopes,
6722
+ createdAt,
6723
+ lastUsedAt,
6724
+ revokedAt: typeof revokedAt === "number" ? revokedAt : null
6725
+ };
6726
+ }
6727
+ var OauthSessionManager = class {
6728
+ constructor(store) {
6729
+ this.store = store;
6730
+ }
6731
+ /**
6732
+ * Create a new OAuth session record. Generates a UUID as the session id,
6733
+ * sets `createdAt` and `lastUsedAt` to now, `revokedAt` to null.
6734
+ */
6735
+ async create(input) {
6736
+ const now = Date.now();
6737
+ const session = {
6738
+ id: node_crypto.randomUUID(),
6739
+ userId: input.userId,
6740
+ username: input.username,
6741
+ integrationId: input.integrationId,
6742
+ scopes: input.scopes.map((s) => ({ ...s })),
6743
+ createdAt: now,
6744
+ lastUsedAt: now,
6745
+ revokedAt: null
6746
+ };
6747
+ await this.store.insert.mutate({
6748
+ collection: SESSIONS_COLLECTION,
6749
+ record: {
6750
+ id: session.id,
6751
+ data: { ...session }
6752
+ }
6753
+ });
6754
+ return session;
6755
+ }
6756
+ /**
6757
+ * Return all sessions — both active and revoked. Callers decide
6758
+ * whether to filter by `revokedAt`.
6759
+ */
6760
+ async list() {
6761
+ return (await this.store.query.query({
6762
+ collection: SESSIONS_COLLECTION,
6763
+ filter: {}
6764
+ })).map((r) => parseSession(r.data));
6765
+ }
6766
+ /**
6767
+ * Look up a session by its id. Returns null when not found.
6768
+ */
6769
+ async getById(id) {
6770
+ const results = await this.store.query.query({
6771
+ collection: SESSIONS_COLLECTION,
6772
+ filter: { where: { id } }
6773
+ });
6774
+ if (results.length === 0) return null;
6775
+ return parseSession(results[0].data);
6776
+ }
6777
+ /**
6778
+ * Mark a session as revoked by setting `revokedAt = now`.
6779
+ *
6780
+ * - Returns `true` on success (including idempotent re-revoke — the
6781
+ * existing `revokedAt` timestamp is preserved; it is NOT updated).
6782
+ * - Returns `false` when the session id is not found.
6783
+ */
6784
+ async markRevoked(id) {
6785
+ const existing = await this.getById(id);
6786
+ if (existing === null) return false;
6787
+ if (existing.revokedAt !== null) return true;
6788
+ const updated = {
6789
+ ...existing,
6790
+ revokedAt: Date.now()
6791
+ };
6792
+ await this.store.update.mutate({
6793
+ collection: SESSIONS_COLLECTION,
6794
+ id,
6795
+ data: { ...updated }
6796
+ });
6797
+ return true;
6798
+ }
6799
+ /**
6800
+ * Update `lastUsedAt` to now. No-op (does not throw) when the session
6801
+ * id is not found — the caller (token-use hot path) should not fail
6802
+ * for a missing session that may have been concurrently revoked.
6803
+ */
6804
+ async touch(id) {
6805
+ const existing = await this.getById(id);
6806
+ if (existing === null) return;
6807
+ const updated = {
6808
+ ...existing,
6809
+ lastUsedAt: Date.now()
6810
+ };
6811
+ await this.store.update.mutate({
6812
+ collection: SESSIONS_COLLECTION,
6813
+ id,
6814
+ data: { ...updated }
6815
+ });
6816
+ }
6817
+ };
6818
+ //#endregion
6672
6819
  //#region ../../node_modules/@otplib/plugin-crypto/index.js
6673
6820
  /**
6674
6821
  * @otplib/plugin-crypto
@@ -7379,7 +7526,6 @@ function parseRow(data) {
7379
7526
  var TotpManager = class {
7380
7527
  constructor(store, opts = {}) {
7381
7528
  this.store = store;
7382
- this.opts = opts;
7383
7529
  import_otplib.authenticator.options = { window: opts.window ?? 1 };
7384
7530
  }
7385
7531
  /**
@@ -7490,6 +7636,7 @@ var TotpManager = class {
7490
7636
  var USERS_COLLECTION = "users";
7491
7637
  var API_KEYS_COLLECTION = "api_keys";
7492
7638
  var SCOPED_TOKENS_COLLECTION = "scoped_tokens";
7639
+ var OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
7493
7640
  /**
7494
7641
  * TOTP enrollment table — split off from `users` to avoid the
7495
7642
  * destructive drop-+-recreate migration `ensureTable` performs when a
@@ -7664,6 +7811,47 @@ var SCOPED_TOKENS_COLUMNS = [
7664
7811
  notNull: true
7665
7812
  }
7666
7813
  ];
7814
+ var OAUTH_SESSIONS_COLUMNS = [
7815
+ {
7816
+ name: "id",
7817
+ type: "TEXT",
7818
+ primaryKey: true,
7819
+ notNull: true
7820
+ },
7821
+ {
7822
+ name: "userId",
7823
+ type: "TEXT",
7824
+ notNull: true
7825
+ },
7826
+ {
7827
+ name: "username",
7828
+ type: "TEXT",
7829
+ notNull: true
7830
+ },
7831
+ {
7832
+ name: "integrationId",
7833
+ type: "TEXT",
7834
+ notNull: true
7835
+ },
7836
+ {
7837
+ name: "scopes",
7838
+ type: "JSON"
7839
+ },
7840
+ {
7841
+ name: "createdAt",
7842
+ type: "INTEGER",
7843
+ notNull: true
7844
+ },
7845
+ {
7846
+ name: "lastUsedAt",
7847
+ type: "INTEGER",
7848
+ notNull: true
7849
+ },
7850
+ {
7851
+ name: "revokedAt",
7852
+ type: "INTEGER"
7853
+ }
7854
+ ];
7667
7855
  /**
7668
7856
  * Declare every auth collection with the typed schema. Called by
7669
7857
  * `LocalAuthAddon.onInitialize` before constructing the per-collection
@@ -7695,6 +7883,124 @@ async function declareAuthSchema(store) {
7695
7883
  collection: USER_TOTP_COLLECTION,
7696
7884
  columns: USER_TOTP_COLUMNS
7697
7885
  });
7886
+ await store.declareCollection.mutate({
7887
+ collection: OAUTH_SESSIONS_COLLECTION,
7888
+ columns: OAUTH_SESSIONS_COLUMNS,
7889
+ indexes: [{
7890
+ name: "idx_oauth_sessions_user_id",
7891
+ columns: ["userId"]
7892
+ }]
7893
+ });
7894
+ }
7895
+ //#endregion
7896
+ //#region src/builtins/local-auth/oauth-grants.ts
7897
+ var TTL = {
7898
+ "oauth-code": 60,
7899
+ "oauth-access": 3600,
7900
+ "oauth-refresh": 2592e3
7901
+ };
7902
+ /** OAuth account-linking grant logic. Pure over an injected sso-bridge
7903
+ * signer so it is unit-testable without the capability registry.
7904
+ *
7905
+ * Authorization codes are short-lived (60 s). The `redirectUri`,
7906
+ * `integrationId`, and a unique `jti` are embedded in the signed JWT
7907
+ * so the HMAC signature is the real security boundary. Single-use is
7908
+ * enforced by a `jti` consumed-set. The old in-process `pendingCodes`
7909
+ * Map is gone — a hub restart no longer breaks in-flight exchanges,
7910
+ * only risking a single replay within the remaining 60 s TTL. */
7911
+ function createOauthGrants(ssoBridge, sessionManager) {
7912
+ const consumedJtis = /* @__PURE__ */ new Map();
7913
+ const baseClaims = (userId, username, scopes, hubUrl) => ({
7914
+ userId,
7915
+ username,
7916
+ isAdmin: false,
7917
+ scopes,
7918
+ hubUrl
7919
+ });
7920
+ async function mintPair(userId, username, scopes, hubUrl, sessionId) {
7921
+ const access = await ssoBridge.signBridgeToken({
7922
+ claims: {
7923
+ ...baseClaims(userId, username, scopes, hubUrl),
7924
+ provider: "oauth-access",
7925
+ sessionId
7926
+ },
7927
+ ttlSec: TTL["oauth-access"]
7928
+ });
7929
+ const refresh = await ssoBridge.signBridgeToken({
7930
+ claims: {
7931
+ ...baseClaims(userId, username, scopes, hubUrl),
7932
+ provider: "oauth-refresh",
7933
+ sessionId
7934
+ },
7935
+ ttlSec: TTL["oauth-refresh"]
7936
+ });
7937
+ return {
7938
+ accessToken: access.token,
7939
+ refreshToken: refresh.token,
7940
+ expiresIn: TTL["oauth-access"]
7941
+ };
7942
+ }
7943
+ return {
7944
+ async oauthIssueCode(input) {
7945
+ const jti = (0, node_crypto.randomUUID)();
7946
+ const { token } = await ssoBridge.signBridgeToken({
7947
+ claims: {
7948
+ ...baseClaims(input.userId, input.username, input.scopes, input.hubUrl),
7949
+ provider: "oauth-code",
7950
+ redirectUri: input.redirectUri,
7951
+ integrationId: input.integrationId,
7952
+ jti
7953
+ },
7954
+ ttlSec: TTL["oauth-code"]
7955
+ });
7956
+ return { code: token };
7957
+ },
7958
+ async oauthExchangeCode(input) {
7959
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.code });
7960
+ if (!claims || claims.provider !== "oauth-code") return null;
7961
+ if (claims.redirectUri !== input.redirectUri) return null;
7962
+ const jti = claims.jti;
7963
+ if (!jti) return null;
7964
+ const expiryMs = Date.now() + TTL["oauth-code"] * 1e3;
7965
+ if (consumedJtis.has(jti)) return null;
7966
+ consumedJtis.set(jti, expiryMs);
7967
+ setTimeout(() => {
7968
+ consumedJtis.delete(jti);
7969
+ }, TTL["oauth-code"] * 1e3).unref?.();
7970
+ const scopes = claims.scopes ?? [];
7971
+ const session = await sessionManager.create({
7972
+ userId: claims.userId,
7973
+ username: claims.username,
7974
+ integrationId: typeof claims.integrationId === "string" ? claims.integrationId : "<unknown>",
7975
+ scopes
7976
+ });
7977
+ return mintPair(claims.userId, claims.username, scopes, claims.hubUrl ?? "", session.id);
7978
+ },
7979
+ async oauthRefresh(input) {
7980
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.refreshToken });
7981
+ if (!claims || claims.provider !== "oauth-refresh") return null;
7982
+ const sessionId = typeof claims.sessionId === "string" ? claims.sessionId : null;
7983
+ if (!sessionId) return null;
7984
+ const session = await sessionManager.getById(sessionId);
7985
+ if (!session || session.revokedAt != null) return null;
7986
+ await sessionManager.touch(sessionId);
7987
+ const scopes = claims.scopes ?? [];
7988
+ return mintPair(claims.userId, claims.username, scopes, claims.hubUrl ?? "", sessionId);
7989
+ },
7990
+ async oauthVerifyAccessToken(input) {
7991
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.token });
7992
+ if (!claims || claims.provider !== "oauth-access") return null;
7993
+ const sessionId = typeof claims.sessionId === "string" ? claims.sessionId : null;
7994
+ if (!sessionId) return null;
7995
+ const session = await sessionManager.getById(sessionId);
7996
+ if (!session || session.revokedAt != null) return null;
7997
+ return {
7998
+ userId: claims.userId,
7999
+ username: claims.username,
8000
+ scopes: claims.scopes ?? []
8001
+ };
8002
+ }
8003
+ };
7698
8004
  }
7699
8005
  //#endregion
7700
8006
  //#region src/builtins/local-auth/local-auth.addon.ts
@@ -7711,6 +8017,7 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
7711
8017
  userManager = null;
7712
8018
  apiKeyManager = null;
7713
8019
  scopedTokenManager = null;
8020
+ oauthSessionManager = null;
7714
8021
  totpManager = null;
7715
8022
  constructor() {
7716
8023
  super({
@@ -7744,6 +8051,7 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
7744
8051
  this.userManager = new UserManager(storageAccess, this.authManager, reader);
7745
8052
  this.apiKeyManager = new ApiKeyManager(storageAccess, this.authManager);
7746
8053
  this.scopedTokenManager = new ScopedTokenManager(store);
8054
+ this.oauthSessionManager = new OauthSessionManager(store);
7747
8055
  this.totpManager = new TotpManager(store);
7748
8056
  try {
7749
8057
  await this.userManager.ensureAdminExists();
@@ -7780,6 +8088,19 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
7780
8088
  }
7781
8089
  }
7782
8090
  };
8091
+ let cachedOauthGrants = null;
8092
+ const lazyOauthGrants = () => {
8093
+ if (cachedOauthGrants) return cachedOauthGrants;
8094
+ const bridgeApi = this.ctx.api?.ssoBridge;
8095
+ if (!bridgeApi) throw new Error("local-auth: sso-bridge capability not available via ctx.api — hub boot incomplete");
8096
+ const ssoBridgeProvider = {
8097
+ signBridgeToken: (input) => bridgeApi.signBridgeToken.query(input),
8098
+ verifyBridgeToken: (input) => bridgeApi.verifyBridgeToken.query(input)
8099
+ };
8100
+ if (!this.oauthSessionManager) throw new Error("local-auth: oauthSessionManager not initialized — hub boot incomplete");
8101
+ cachedOauthGrants = createOauthGrants(ssoBridgeProvider, this.oauthSessionManager);
8102
+ return cachedOauthGrants;
8103
+ };
7783
8104
  const userMgmt = {
7784
8105
  listUsers: async () => {
7785
8106
  if (!this.userManager) return [];
@@ -7879,6 +8200,35 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
7879
8200
  if (!this.scopedTokenManager) return [];
7880
8201
  return this.scopedTokenManager.listForUser(input.userId);
7881
8202
  },
8203
+ oauthIssueCode: async (input) => {
8204
+ return lazyOauthGrants().oauthIssueCode(input);
8205
+ },
8206
+ oauthExchangeCode: async (input) => {
8207
+ return lazyOauthGrants().oauthExchangeCode(input);
8208
+ },
8209
+ oauthRefresh: async (input) => {
8210
+ return lazyOauthGrants().oauthRefresh(input);
8211
+ },
8212
+ oauthVerifyAccessToken: async (input) => {
8213
+ return lazyOauthGrants().oauthVerifyAccessToken(input);
8214
+ },
8215
+ listOauthSessions: async () => {
8216
+ if (!this.oauthSessionManager) return [];
8217
+ return (await this.oauthSessionManager.list()).map((s) => ({
8218
+ id: s.id,
8219
+ userId: s.userId,
8220
+ username: s.username,
8221
+ integrationId: s.integrationId,
8222
+ scopes: s.scopes,
8223
+ createdAt: s.createdAt,
8224
+ lastUsedAt: s.lastUsedAt,
8225
+ revokedAt: s.revokedAt
8226
+ }));
8227
+ },
8228
+ revokeOauthSession: async (input) => {
8229
+ if (!this.oauthSessionManager) return { success: false };
8230
+ return { success: await this.oauthSessionManager.markRevoked(input.id) };
8231
+ },
7882
8232
  setupTotp: async (input) => {
7883
8233
  if (!this.totpManager || !this.userManager) throw new Error("TOTP management not available");
7884
8234
  const user = await this.userManager.findById(input.userId);
@@ -7921,6 +8271,7 @@ var LocalAuthAddon = class extends _camstack_types.BaseAddon {
7921
8271
  this.userManager = null;
7922
8272
  this.apiKeyManager = null;
7923
8273
  this.scopedTokenManager = null;
8274
+ this.oauthSessionManager = null;
7924
8275
  }
7925
8276
  };
7926
8277
  //#endregion