@camstack/core 0.1.38 → 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 (52) 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 +17 -0
  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 +95 -5
  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 +95 -5
  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 +419 -97
  49. package/dist/index.js.map +1 -1
  50. package/dist/index.mjs +419 -98
  51. package/dist/index.mjs.map +1 -1
  52. package/package.json +19 -1
@@ -1,5 +1,6 @@
1
1
  import { i as __toESM, r as __require, t as __commonJSMin } from "../../chunk-hT5z_Zn9.mjs";
2
2
  import * as crypto$1 from "node:crypto";
3
+ import { randomUUID } from "node:crypto";
3
4
  import { ApiKeyRecordSchema, BaseAddon, ScopedTokenSchema, UserRecordSchema, authProviderCapability, userManagementCapability } from "@camstack/types";
4
5
  //#region ../../node_modules/safe-buffer/index.js
5
6
  var require_safe_buffer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -6238,7 +6239,12 @@ var AuthManager = class {
6238
6239
  provider: payload.provider,
6239
6240
  ...payload.email !== void 0 ? { email: payload.email } : {},
6240
6241
  ...payload.displayName !== void 0 ? { displayName: payload.displayName } : {},
6241
- ...payload.hubUrl !== void 0 ? { hubUrl: payload.hubUrl } : {}
6242
+ ...payload.hubUrl !== void 0 ? { hubUrl: payload.hubUrl } : {},
6243
+ ...payload.scopes !== void 0 ? { scopes: payload.scopes } : {},
6244
+ ...payload.redirectUri !== void 0 ? { redirectUri: payload.redirectUri } : {},
6245
+ ...payload.integrationId !== void 0 ? { integrationId: payload.integrationId } : {},
6246
+ ...payload.jti !== void 0 ? { jti: payload.jti } : {},
6247
+ ...payload.sessionId !== void 0 ? { sessionId: payload.sessionId } : {}
6242
6248
  };
6243
6249
  return import_jsonwebtoken.sign(body, this.jwtSecret, { expiresIn: ttlSec });
6244
6250
  }
@@ -6260,6 +6266,11 @@ var AuthManager = class {
6260
6266
  const email = typeof decoded["email"] === "string" ? decoded["email"] : void 0;
6261
6267
  const displayName = typeof decoded["displayName"] === "string" ? decoded["displayName"] : void 0;
6262
6268
  const hubUrl = typeof decoded["hubUrl"] === "string" ? decoded["hubUrl"] : void 0;
6269
+ const scopes = Array.isArray(decoded["scopes"]) ? decoded["scopes"] : void 0;
6270
+ const redirectUri = typeof decoded["redirectUri"] === "string" ? decoded["redirectUri"] : void 0;
6271
+ const integrationId = typeof decoded["integrationId"] === "string" ? decoded["integrationId"] : void 0;
6272
+ const jti = typeof decoded["jti"] === "string" ? decoded["jti"] : void 0;
6273
+ const sessionId = typeof decoded["sessionId"] === "string" ? decoded["sessionId"] : void 0;
6263
6274
  return {
6264
6275
  userId,
6265
6276
  username,
@@ -6267,7 +6278,12 @@ var AuthManager = class {
6267
6278
  provider,
6268
6279
  ...email !== void 0 ? { email } : {},
6269
6280
  ...displayName !== void 0 ? { displayName } : {},
6270
- ...hubUrl !== void 0 ? { hubUrl } : {}
6281
+ ...hubUrl !== void 0 ? { hubUrl } : {},
6282
+ ...scopes !== void 0 ? { scopes } : {},
6283
+ ...redirectUri !== void 0 ? { redirectUri } : {},
6284
+ ...integrationId !== void 0 ? { integrationId } : {},
6285
+ ...jti !== void 0 ? { jti } : {},
6286
+ ...sessionId !== void 0 ? { sessionId } : {}
6271
6287
  };
6272
6288
  } catch {
6273
6289
  return null;
@@ -6664,6 +6680,138 @@ var ScopedTokenManager = class {
6664
6680
  }
6665
6681
  };
6666
6682
  //#endregion
6683
+ //#region src/builtins/local-auth/oauth-session-manager.ts
6684
+ /**
6685
+ * Manages OAuth session records in the `oauth_sessions` collection.
6686
+ *
6687
+ * Each record represents one Alexa-style account link. Sessions can be
6688
+ * active (`revokedAt = null`) or revoked (`revokedAt = <unix-ms>`).
6689
+ * `list()` returns both — callers decide whether to filter.
6690
+ *
6691
+ * Mirrors the pattern of `ScopedTokenManager`: takes a `SettingsStoreClient`
6692
+ * in its constructor and performs all reads/writes via the store's
6693
+ * `insert`, `update`, `delete`, and `query` primitives.
6694
+ */
6695
+ var SESSIONS_COLLECTION = "oauth_sessions";
6696
+ function parseSession(data) {
6697
+ const id = data["id"];
6698
+ const userId = data["userId"];
6699
+ const username = data["username"];
6700
+ const integrationId = data["integrationId"];
6701
+ const scopes = data["scopes"];
6702
+ const createdAt = data["createdAt"];
6703
+ const lastUsedAt = data["lastUsedAt"];
6704
+ const revokedAt = data["revokedAt"];
6705
+ if (typeof id !== "string") throw new Error("oauth_sessions row: missing id");
6706
+ if (typeof userId !== "string") throw new Error("oauth_sessions row: missing userId");
6707
+ if (typeof username !== "string") throw new Error("oauth_sessions row: missing username");
6708
+ if (typeof integrationId !== "string") throw new Error("oauth_sessions row: missing integrationId");
6709
+ if (!Array.isArray(scopes)) throw new Error("oauth_sessions row: scopes must be an array");
6710
+ if (typeof createdAt !== "number") throw new Error("oauth_sessions row: missing createdAt");
6711
+ if (typeof lastUsedAt !== "number") throw new Error("oauth_sessions row: missing lastUsedAt");
6712
+ return {
6713
+ id,
6714
+ userId,
6715
+ username,
6716
+ integrationId,
6717
+ scopes,
6718
+ createdAt,
6719
+ lastUsedAt,
6720
+ revokedAt: typeof revokedAt === "number" ? revokedAt : null
6721
+ };
6722
+ }
6723
+ var OauthSessionManager = class {
6724
+ constructor(store) {
6725
+ this.store = store;
6726
+ }
6727
+ /**
6728
+ * Create a new OAuth session record. Generates a UUID as the session id,
6729
+ * sets `createdAt` and `lastUsedAt` to now, `revokedAt` to null.
6730
+ */
6731
+ async create(input) {
6732
+ const now = Date.now();
6733
+ const session = {
6734
+ id: crypto$1.randomUUID(),
6735
+ userId: input.userId,
6736
+ username: input.username,
6737
+ integrationId: input.integrationId,
6738
+ scopes: input.scopes.map((s) => ({ ...s })),
6739
+ createdAt: now,
6740
+ lastUsedAt: now,
6741
+ revokedAt: null
6742
+ };
6743
+ await this.store.insert.mutate({
6744
+ collection: SESSIONS_COLLECTION,
6745
+ record: {
6746
+ id: session.id,
6747
+ data: { ...session }
6748
+ }
6749
+ });
6750
+ return session;
6751
+ }
6752
+ /**
6753
+ * Return all sessions — both active and revoked. Callers decide
6754
+ * whether to filter by `revokedAt`.
6755
+ */
6756
+ async list() {
6757
+ return (await this.store.query.query({
6758
+ collection: SESSIONS_COLLECTION,
6759
+ filter: {}
6760
+ })).map((r) => parseSession(r.data));
6761
+ }
6762
+ /**
6763
+ * Look up a session by its id. Returns null when not found.
6764
+ */
6765
+ async getById(id) {
6766
+ const results = await this.store.query.query({
6767
+ collection: SESSIONS_COLLECTION,
6768
+ filter: { where: { id } }
6769
+ });
6770
+ if (results.length === 0) return null;
6771
+ return parseSession(results[0].data);
6772
+ }
6773
+ /**
6774
+ * Mark a session as revoked by setting `revokedAt = now`.
6775
+ *
6776
+ * - Returns `true` on success (including idempotent re-revoke — the
6777
+ * existing `revokedAt` timestamp is preserved; it is NOT updated).
6778
+ * - Returns `false` when the session id is not found.
6779
+ */
6780
+ async markRevoked(id) {
6781
+ const existing = await this.getById(id);
6782
+ if (existing === null) return false;
6783
+ if (existing.revokedAt !== null) return true;
6784
+ const updated = {
6785
+ ...existing,
6786
+ revokedAt: Date.now()
6787
+ };
6788
+ await this.store.update.mutate({
6789
+ collection: SESSIONS_COLLECTION,
6790
+ id,
6791
+ data: { ...updated }
6792
+ });
6793
+ return true;
6794
+ }
6795
+ /**
6796
+ * Update `lastUsedAt` to now. No-op (does not throw) when the session
6797
+ * id is not found — the caller (token-use hot path) should not fail
6798
+ * for a missing session that may have been concurrently revoked.
6799
+ */
6800
+ async touch(id) {
6801
+ const existing = await this.getById(id);
6802
+ if (existing === null) return;
6803
+ const updated = {
6804
+ ...existing,
6805
+ lastUsedAt: Date.now()
6806
+ };
6807
+ await this.store.update.mutate({
6808
+ collection: SESSIONS_COLLECTION,
6809
+ id,
6810
+ data: { ...updated }
6811
+ });
6812
+ }
6813
+ };
6814
+ //#endregion
6667
6815
  //#region ../../node_modules/@otplib/plugin-crypto/index.js
6668
6816
  /**
6669
6817
  * @otplib/plugin-crypto
@@ -7374,7 +7522,6 @@ function parseRow(data) {
7374
7522
  var TotpManager = class {
7375
7523
  constructor(store, opts = {}) {
7376
7524
  this.store = store;
7377
- this.opts = opts;
7378
7525
  import_otplib.authenticator.options = { window: opts.window ?? 1 };
7379
7526
  }
7380
7527
  /**
@@ -7485,6 +7632,7 @@ var TotpManager = class {
7485
7632
  var USERS_COLLECTION = "users";
7486
7633
  var API_KEYS_COLLECTION = "api_keys";
7487
7634
  var SCOPED_TOKENS_COLLECTION = "scoped_tokens";
7635
+ var OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
7488
7636
  /**
7489
7637
  * TOTP enrollment table — split off from `users` to avoid the
7490
7638
  * destructive drop-+-recreate migration `ensureTable` performs when a
@@ -7659,6 +7807,47 @@ var SCOPED_TOKENS_COLUMNS = [
7659
7807
  notNull: true
7660
7808
  }
7661
7809
  ];
7810
+ var OAUTH_SESSIONS_COLUMNS = [
7811
+ {
7812
+ name: "id",
7813
+ type: "TEXT",
7814
+ primaryKey: true,
7815
+ notNull: true
7816
+ },
7817
+ {
7818
+ name: "userId",
7819
+ type: "TEXT",
7820
+ notNull: true
7821
+ },
7822
+ {
7823
+ name: "username",
7824
+ type: "TEXT",
7825
+ notNull: true
7826
+ },
7827
+ {
7828
+ name: "integrationId",
7829
+ type: "TEXT",
7830
+ notNull: true
7831
+ },
7832
+ {
7833
+ name: "scopes",
7834
+ type: "JSON"
7835
+ },
7836
+ {
7837
+ name: "createdAt",
7838
+ type: "INTEGER",
7839
+ notNull: true
7840
+ },
7841
+ {
7842
+ name: "lastUsedAt",
7843
+ type: "INTEGER",
7844
+ notNull: true
7845
+ },
7846
+ {
7847
+ name: "revokedAt",
7848
+ type: "INTEGER"
7849
+ }
7850
+ ];
7662
7851
  /**
7663
7852
  * Declare every auth collection with the typed schema. Called by
7664
7853
  * `LocalAuthAddon.onInitialize` before constructing the per-collection
@@ -7690,6 +7879,124 @@ async function declareAuthSchema(store) {
7690
7879
  collection: USER_TOTP_COLLECTION,
7691
7880
  columns: USER_TOTP_COLUMNS
7692
7881
  });
7882
+ await store.declareCollection.mutate({
7883
+ collection: OAUTH_SESSIONS_COLLECTION,
7884
+ columns: OAUTH_SESSIONS_COLUMNS,
7885
+ indexes: [{
7886
+ name: "idx_oauth_sessions_user_id",
7887
+ columns: ["userId"]
7888
+ }]
7889
+ });
7890
+ }
7891
+ //#endregion
7892
+ //#region src/builtins/local-auth/oauth-grants.ts
7893
+ var TTL = {
7894
+ "oauth-code": 60,
7895
+ "oauth-access": 3600,
7896
+ "oauth-refresh": 2592e3
7897
+ };
7898
+ /** OAuth account-linking grant logic. Pure over an injected sso-bridge
7899
+ * signer so it is unit-testable without the capability registry.
7900
+ *
7901
+ * Authorization codes are short-lived (60 s). The `redirectUri`,
7902
+ * `integrationId`, and a unique `jti` are embedded in the signed JWT
7903
+ * so the HMAC signature is the real security boundary. Single-use is
7904
+ * enforced by a `jti` consumed-set. The old in-process `pendingCodes`
7905
+ * Map is gone — a hub restart no longer breaks in-flight exchanges,
7906
+ * only risking a single replay within the remaining 60 s TTL. */
7907
+ function createOauthGrants(ssoBridge, sessionManager) {
7908
+ const consumedJtis = /* @__PURE__ */ new Map();
7909
+ const baseClaims = (userId, username, scopes, hubUrl) => ({
7910
+ userId,
7911
+ username,
7912
+ isAdmin: false,
7913
+ scopes,
7914
+ hubUrl
7915
+ });
7916
+ async function mintPair(userId, username, scopes, hubUrl, sessionId) {
7917
+ const access = await ssoBridge.signBridgeToken({
7918
+ claims: {
7919
+ ...baseClaims(userId, username, scopes, hubUrl),
7920
+ provider: "oauth-access",
7921
+ sessionId
7922
+ },
7923
+ ttlSec: TTL["oauth-access"]
7924
+ });
7925
+ const refresh = await ssoBridge.signBridgeToken({
7926
+ claims: {
7927
+ ...baseClaims(userId, username, scopes, hubUrl),
7928
+ provider: "oauth-refresh",
7929
+ sessionId
7930
+ },
7931
+ ttlSec: TTL["oauth-refresh"]
7932
+ });
7933
+ return {
7934
+ accessToken: access.token,
7935
+ refreshToken: refresh.token,
7936
+ expiresIn: TTL["oauth-access"]
7937
+ };
7938
+ }
7939
+ return {
7940
+ async oauthIssueCode(input) {
7941
+ const jti = randomUUID();
7942
+ const { token } = await ssoBridge.signBridgeToken({
7943
+ claims: {
7944
+ ...baseClaims(input.userId, input.username, input.scopes, input.hubUrl),
7945
+ provider: "oauth-code",
7946
+ redirectUri: input.redirectUri,
7947
+ integrationId: input.integrationId,
7948
+ jti
7949
+ },
7950
+ ttlSec: TTL["oauth-code"]
7951
+ });
7952
+ return { code: token };
7953
+ },
7954
+ async oauthExchangeCode(input) {
7955
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.code });
7956
+ if (!claims || claims.provider !== "oauth-code") return null;
7957
+ if (claims.redirectUri !== input.redirectUri) return null;
7958
+ const jti = claims.jti;
7959
+ if (!jti) return null;
7960
+ const expiryMs = Date.now() + TTL["oauth-code"] * 1e3;
7961
+ if (consumedJtis.has(jti)) return null;
7962
+ consumedJtis.set(jti, expiryMs);
7963
+ setTimeout(() => {
7964
+ consumedJtis.delete(jti);
7965
+ }, TTL["oauth-code"] * 1e3).unref?.();
7966
+ const scopes = claims.scopes ?? [];
7967
+ const session = await sessionManager.create({
7968
+ userId: claims.userId,
7969
+ username: claims.username,
7970
+ integrationId: typeof claims.integrationId === "string" ? claims.integrationId : "<unknown>",
7971
+ scopes
7972
+ });
7973
+ return mintPair(claims.userId, claims.username, scopes, claims.hubUrl ?? "", session.id);
7974
+ },
7975
+ async oauthRefresh(input) {
7976
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.refreshToken });
7977
+ if (!claims || claims.provider !== "oauth-refresh") return null;
7978
+ const sessionId = typeof claims.sessionId === "string" ? claims.sessionId : null;
7979
+ if (!sessionId) return null;
7980
+ const session = await sessionManager.getById(sessionId);
7981
+ if (!session || session.revokedAt != null) return null;
7982
+ await sessionManager.touch(sessionId);
7983
+ const scopes = claims.scopes ?? [];
7984
+ return mintPair(claims.userId, claims.username, scopes, claims.hubUrl ?? "", sessionId);
7985
+ },
7986
+ async oauthVerifyAccessToken(input) {
7987
+ const claims = await ssoBridge.verifyBridgeToken({ token: input.token });
7988
+ if (!claims || claims.provider !== "oauth-access") return null;
7989
+ const sessionId = typeof claims.sessionId === "string" ? claims.sessionId : null;
7990
+ if (!sessionId) return null;
7991
+ const session = await sessionManager.getById(sessionId);
7992
+ if (!session || session.revokedAt != null) return null;
7993
+ return {
7994
+ userId: claims.userId,
7995
+ username: claims.username,
7996
+ scopes: claims.scopes ?? []
7997
+ };
7998
+ }
7999
+ };
7693
8000
  }
7694
8001
  //#endregion
7695
8002
  //#region src/builtins/local-auth/local-auth.addon.ts
@@ -7706,6 +8013,7 @@ var LocalAuthAddon = class extends BaseAddon {
7706
8013
  userManager = null;
7707
8014
  apiKeyManager = null;
7708
8015
  scopedTokenManager = null;
8016
+ oauthSessionManager = null;
7709
8017
  totpManager = null;
7710
8018
  constructor() {
7711
8019
  super({
@@ -7739,6 +8047,7 @@ var LocalAuthAddon = class extends BaseAddon {
7739
8047
  this.userManager = new UserManager(storageAccess, this.authManager, reader);
7740
8048
  this.apiKeyManager = new ApiKeyManager(storageAccess, this.authManager);
7741
8049
  this.scopedTokenManager = new ScopedTokenManager(store);
8050
+ this.oauthSessionManager = new OauthSessionManager(store);
7742
8051
  this.totpManager = new TotpManager(store);
7743
8052
  try {
7744
8053
  await this.userManager.ensureAdminExists();
@@ -7775,6 +8084,19 @@ var LocalAuthAddon = class extends BaseAddon {
7775
8084
  }
7776
8085
  }
7777
8086
  };
8087
+ let cachedOauthGrants = null;
8088
+ const lazyOauthGrants = () => {
8089
+ if (cachedOauthGrants) return cachedOauthGrants;
8090
+ const bridgeApi = this.ctx.api?.ssoBridge;
8091
+ if (!bridgeApi) throw new Error("local-auth: sso-bridge capability not available via ctx.api — hub boot incomplete");
8092
+ const ssoBridgeProvider = {
8093
+ signBridgeToken: (input) => bridgeApi.signBridgeToken.query(input),
8094
+ verifyBridgeToken: (input) => bridgeApi.verifyBridgeToken.query(input)
8095
+ };
8096
+ if (!this.oauthSessionManager) throw new Error("local-auth: oauthSessionManager not initialized — hub boot incomplete");
8097
+ cachedOauthGrants = createOauthGrants(ssoBridgeProvider, this.oauthSessionManager);
8098
+ return cachedOauthGrants;
8099
+ };
7778
8100
  const userMgmt = {
7779
8101
  listUsers: async () => {
7780
8102
  if (!this.userManager) return [];
@@ -7874,6 +8196,35 @@ var LocalAuthAddon = class extends BaseAddon {
7874
8196
  if (!this.scopedTokenManager) return [];
7875
8197
  return this.scopedTokenManager.listForUser(input.userId);
7876
8198
  },
8199
+ oauthIssueCode: async (input) => {
8200
+ return lazyOauthGrants().oauthIssueCode(input);
8201
+ },
8202
+ oauthExchangeCode: async (input) => {
8203
+ return lazyOauthGrants().oauthExchangeCode(input);
8204
+ },
8205
+ oauthRefresh: async (input) => {
8206
+ return lazyOauthGrants().oauthRefresh(input);
8207
+ },
8208
+ oauthVerifyAccessToken: async (input) => {
8209
+ return lazyOauthGrants().oauthVerifyAccessToken(input);
8210
+ },
8211
+ listOauthSessions: async () => {
8212
+ if (!this.oauthSessionManager) return [];
8213
+ return (await this.oauthSessionManager.list()).map((s) => ({
8214
+ id: s.id,
8215
+ userId: s.userId,
8216
+ username: s.username,
8217
+ integrationId: s.integrationId,
8218
+ scopes: s.scopes,
8219
+ createdAt: s.createdAt,
8220
+ lastUsedAt: s.lastUsedAt,
8221
+ revokedAt: s.revokedAt
8222
+ }));
8223
+ },
8224
+ revokeOauthSession: async (input) => {
8225
+ if (!this.oauthSessionManager) return { success: false };
8226
+ return { success: await this.oauthSessionManager.markRevoked(input.id) };
8227
+ },
7877
8228
  setupTotp: async (input) => {
7878
8229
  if (!this.totpManager || !this.userManager) throw new Error("TOTP management not available");
7879
8230
  const user = await this.userManager.findById(input.userId);
@@ -7916,6 +8267,7 @@ var LocalAuthAddon = class extends BaseAddon {
7916
8267
  this.userManager = null;
7917
8268
  this.apiKeyManager = null;
7918
8269
  this.scopedTokenManager = null;
8270
+ this.oauthSessionManager = null;
7919
8271
  }
7920
8272
  };
7921
8273
  //#endregion