@openacp/cli 2026.414.1 → 2026.414.2

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/dist/cli.js CHANGED
@@ -2633,6 +2633,22 @@ function registerSetupRoutes(app, deps) {
2633
2633
  if (user2) return user2;
2634
2634
  }
2635
2635
  const body = request.body;
2636
+ if (body?.identitySecret) {
2637
+ const oldToken = tokenStore?.getByIdentitySecret(body.identitySecret);
2638
+ if (!oldToken) {
2639
+ return reply.status(401).send({ error: "Invalid identity secret" });
2640
+ }
2641
+ const userId = tokenStore?.getUserId(oldToken.id);
2642
+ if (!userId) {
2643
+ return reply.status(401).send({ error: "No identity linked to this secret" });
2644
+ }
2645
+ await service.createIdentity(userId, {
2646
+ source: "api",
2647
+ platformId: auth.tokenId
2648
+ });
2649
+ tokenStore?.setUserId?.(auth.tokenId, userId);
2650
+ return service.getUser(userId);
2651
+ }
2636
2652
  if (body?.linkCode) {
2637
2653
  const entry = linkCodes.get(body.linkCode);
2638
2654
  if (!entry || entry.expiresAt < Date.now()) {
@@ -8273,9 +8289,15 @@ var init_token_store = __esm({
8273
8289
  return;
8274
8290
  }
8275
8291
  this.tokens.clear();
8292
+ let needsMigration = false;
8276
8293
  for (const token of parsed.tokens) {
8294
+ if (!token.identitySecret) {
8295
+ token.identitySecret = randomBytes2(16).toString("hex");
8296
+ needsMigration = true;
8297
+ }
8277
8298
  this.tokens.set(token.id, token);
8278
8299
  }
8300
+ if (needsMigration) this.scheduleSave();
8279
8301
  this.codes.clear();
8280
8302
  for (const code of parsed.codes ?? []) {
8281
8303
  this.codes.set(code.code, code);
@@ -8321,7 +8343,8 @@ var init_token_store = __esm({
8321
8343
  scopes: opts.scopes,
8322
8344
  createdAt: now.toISOString(),
8323
8345
  refreshDeadline: new Date(now.getTime() + REFRESH_DEADLINE_MS).toISOString(),
8324
- revoked: false
8346
+ revoked: false,
8347
+ identitySecret: randomBytes2(16).toString("hex")
8325
8348
  };
8326
8349
  this.tokens.set(token.id, token);
8327
8350
  this.scheduleSave();
@@ -8390,6 +8413,19 @@ var init_token_store = __esm({
8390
8413
  getUserId(tokenId) {
8391
8414
  return this.tokens.get(tokenId)?.userId;
8392
8415
  }
8416
+ /**
8417
+ * Looks up a non-revoked token by its identity secret.
8418
+ *
8419
+ * Used by the identity re-link flow: the App sends the old token's identitySecret
8420
+ * to prove continuity of identity when reconnecting with a new JWT.
8421
+ * Returns undefined if no match, or if the matching token is revoked.
8422
+ */
8423
+ getByIdentitySecret(secret) {
8424
+ for (const token of this.tokens.values()) {
8425
+ if (!token.revoked && token.identitySecret === secret) return token;
8426
+ }
8427
+ return void 0;
8428
+ }
8393
8429
  /**
8394
8430
  * Generates a one-time authorization code that can be exchanged for a JWT.
8395
8431
  *
@@ -10812,7 +10848,8 @@ async function authRoutes(app, deps) {
10812
10848
  tokenId: stored.id,
10813
10849
  accessToken,
10814
10850
  expiresAt,
10815
- refreshDeadline: stored.refreshDeadline
10851
+ refreshDeadline: stored.refreshDeadline,
10852
+ identitySecret: stored.identitySecret
10816
10853
  };
10817
10854
  });
10818
10855
  app.get("/tokens", {
@@ -11572,7 +11609,8 @@ function createApiServerPlugin() {
11572
11609
  accessToken,
11573
11610
  tokenId: token.id,
11574
11611
  expiresAt: new Date(Date.now() + parseDuration2(code.expire)).toISOString(),
11575
- refreshDeadline: token.refreshDeadline
11612
+ refreshDeadline: token.refreshDeadline,
11613
+ identitySecret: token.identitySecret
11576
11614
  });
11577
11615
  });
11578
11616
  }, { auth: false });