@semiont/backend 0.5.8 → 0.5.9

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/index.js CHANGED
@@ -12,7 +12,6 @@ import { SemiontProject, loadEnvironmentConfig } from '@semiont/core/node';
12
12
  import { exportBackup, importBackup, readEntityTypesProjection, exportLinkedData, importLinkedData, startMakeMeaning, ResourceOperations, ResourceContext } from '@semiont/make-meaning';
13
13
  import { PrismaClient } from '@prisma/client';
14
14
  import { PrismaPg } from '@prisma/adapter-pg';
15
- import { setCookie, deleteCookie, getCookie } from 'hono/cookie';
16
15
  import { HTTPException } from 'hono/http-exception';
17
16
  import Ajv from 'ajv';
18
17
  import addFormats from 'ajv-formats';
@@ -195,6 +194,13 @@ var init_jwt_types = __esm({
195
194
  // `_userId` instead of recomputing `userToDid(user)`. Unset for human
196
195
  // tokens.
197
196
  agentDid: z.string().optional(),
197
+ // Per-user revocation epoch (SDK-AUTH-CORS Phase 2): every token carries the
198
+ // user's tokenVersion at mint; logout bumps User.tokenVersion, so a token
199
+ // whose tokenVersion is behind the user's current value is rejected.
200
+ // Required — a token minted before this feature lacks the claim and fails
201
+ // validation, so the holder re-authenticates. That is the intended
202
+ // revoke-every-session-on-rollout behavior, not a regression.
203
+ tokenVersion: z.number().int(),
198
204
  iat: z.number().optional(),
199
205
  exp: z.number().optional()
200
206
  });
@@ -12674,17 +12680,6 @@ var openapi_default = {
12674
12680
  }
12675
12681
  }
12676
12682
  },
12677
- MCPGenerateResponse: {
12678
- type: "object",
12679
- properties: {
12680
- refresh_token: {
12681
- type: "string"
12682
- }
12683
- },
12684
- required: [
12685
- "refresh_token"
12686
- ]
12687
- },
12688
12683
  Motivation: {
12689
12684
  type: "string",
12690
12685
  enum: [
@@ -16223,9 +16218,10 @@ var OAuthService = class {
16223
16218
  ...user.name && { name: user.name },
16224
16219
  domain: user.domain,
16225
16220
  provider: user.provider,
16226
- isAdmin: user.isAdmin
16221
+ isAdmin: user.isAdmin,
16222
+ tokenVersion: user.tokenVersion
16227
16223
  };
16228
- const token = accessToken(JWTService.generateToken(jwtPayload, "1h"));
16224
+ const token = accessToken(JWTService.generateToken(jwtPayload, "10m"));
16229
16225
  const refreshToken = JWTService.generateToken(jwtPayload, "30d");
16230
16226
  return { user, token, refreshToken, isNewUser };
16231
16227
  }
@@ -16249,6 +16245,9 @@ var OAuthService = class {
16249
16245
  if (!user || !user.isActive) {
16250
16246
  throw new Error("User not found or inactive");
16251
16247
  }
16248
+ if (payload.tokenVersion !== user.tokenVersion) {
16249
+ throw new Error("Token revoked");
16250
+ }
16252
16251
  return payload.agentDid ? { user, agentDid: payload.agentDid } : { user };
16253
16252
  }
16254
16253
  static async acceptTerms(userId2) {
@@ -16293,8 +16292,6 @@ var authMiddleware = async (c, next) => {
16293
16292
  let tokenStr;
16294
16293
  if (authHeader?.startsWith("Bearer ")) {
16295
16294
  tokenStr = authHeader.substring(7).trim();
16296
- } else {
16297
- tokenStr = getCookie(c, "semiont-token");
16298
16295
  }
16299
16296
  if (!tokenStr) {
16300
16297
  logger2.warn("Authentication failed: No token", {
@@ -16303,7 +16300,10 @@ var authMiddleware = async (c, next) => {
16303
16300
  path: c.req.path,
16304
16301
  method: c.req.method
16305
16302
  });
16306
- return c.json({ error: "Unauthorized" }, 401);
16303
+ return c.json({
16304
+ error: "Unauthorized",
16305
+ hint: "Authentication required: send an `Authorization: Bearer <token>` header. A raw browser navigation to a protected resource is unauthenticated."
16306
+ }, 401);
16307
16307
  }
16308
16308
  try {
16309
16309
  const { user, agentDid } = await OAuthService.getPrincipalFromToken(accessToken(tokenStr));
@@ -16473,22 +16473,15 @@ authRouter.post(
16473
16473
  ...user.name && { name: user.name },
16474
16474
  domain: user.domain,
16475
16475
  provider: user.provider,
16476
- isAdmin: user.isAdmin
16476
+ isAdmin: user.isAdmin,
16477
+ tokenVersion: user.tokenVersion
16477
16478
  };
16478
- const token = JWTService.generateToken(jwtPayload, "1h");
16479
+ const token = JWTService.generateToken(jwtPayload, "10m");
16479
16480
  const refreshToken = JWTService.generateToken(jwtPayload, "30d");
16480
16481
  await prisma2.user.update({
16481
16482
  where: { id: user.id },
16482
16483
  data: { lastLogin: /* @__PURE__ */ new Date() }
16483
16484
  });
16484
- setCookie(c, "semiont-token", token, {
16485
- httpOnly: true,
16486
- secure: process.env.NODE_ENV === "production",
16487
- sameSite: "Lax",
16488
- path: "/",
16489
- maxAge: 60 * 60
16490
- // 1 hour, matching access token lifetime
16491
- });
16492
16485
  const response = {
16493
16486
  success: true,
16494
16487
  user: {
@@ -16529,14 +16522,6 @@ authRouter.post(
16529
16522
  }
16530
16523
  const googleUser = await OAuthService.verifyGoogleToken(googleCredential(access_token));
16531
16524
  const { user, token, refreshToken, isNewUser } = await OAuthService.createOrUpdateUser(googleUser);
16532
- setCookie(c, "semiont-token", token, {
16533
- httpOnly: true,
16534
- secure: process.env.NODE_ENV === "production",
16535
- sameSite: "Lax",
16536
- path: "/",
16537
- maxAge: 60 * 60
16538
- // 1 hour, matching access token lifetime
16539
- });
16540
16525
  const response = {
16541
16526
  success: true,
16542
16527
  user: {
@@ -16588,15 +16573,19 @@ authRouter.post(
16588
16573
  if (!user || !user.isActive) {
16589
16574
  return c.json({ error: "User not found or inactive" }, 401);
16590
16575
  }
16576
+ if (payload.tokenVersion !== user.tokenVersion) {
16577
+ return c.json({ error: "Token revoked" }, 401);
16578
+ }
16591
16579
  const accessTokenPayload = {
16592
16580
  userId: userId(user.id),
16593
16581
  email: email(user.email),
16594
16582
  domain: user.domain,
16595
16583
  provider: user.provider,
16596
16584
  isAdmin: user.isAdmin,
16597
- ...user.name && { name: user.name }
16585
+ ...user.name && { name: user.name },
16586
+ tokenVersion: user.tokenVersion
16598
16587
  };
16599
- const accessToken2 = JWTService.generateToken(accessTokenPayload, "1h");
16588
+ const accessToken2 = JWTService.generateToken(accessTokenPayload, "10m");
16600
16589
  const response = {
16601
16590
  access_token: accessToken2
16602
16591
  };
@@ -16637,63 +16626,6 @@ authRouter.get("/api/users/me", authMiddleware, async (c) => {
16637
16626
  };
16638
16627
  return c.json(response, 200);
16639
16628
  });
16640
- authRouter.get("/api/tokens/mcp-setup", authMiddleware, async (c) => {
16641
- const callback = c.req.query("callback");
16642
- if (!callback) {
16643
- return c.json({ error: "Callback URL required" }, 400);
16644
- }
16645
- const allowedCallbackPatterns = [
16646
- /^http:\/\/localhost:\d+\/.*$/,
16647
- /^http:\/\/127\.0\.0\.1:\d+\/.*$/,
16648
- /^http:\/\/\[::1\]:\d+\/.*$/
16649
- ];
16650
- if (!allowedCallbackPatterns.some((p) => p.test(callback))) {
16651
- return c.json({ error: "Invalid callback URL. Must be a localhost URL for CLI authentication." }, 400);
16652
- }
16653
- const user = c.get("user");
16654
- try {
16655
- const tokenPayload = {
16656
- userId: userId(user.id),
16657
- email: email(user.email),
16658
- domain: user.domain,
16659
- provider: user.provider,
16660
- isAdmin: user.isAdmin,
16661
- ...user.name && { name: user.name }
16662
- };
16663
- const refreshToken = JWTService.generateToken(tokenPayload, "30d");
16664
- return c.redirect(`${callback}?token=${refreshToken}`, 302);
16665
- } catch (error) {
16666
- getRouteLogger2().error("MCP setup error", {
16667
- error: error instanceof Error ? error.message : String(error),
16668
- stack: error instanceof Error ? error.stack : void 0
16669
- });
16670
- return c.json({ error: "Failed to generate refresh token" }, 500);
16671
- }
16672
- });
16673
- authRouter.post("/api/tokens/mcp-generate", authMiddleware, async (c) => {
16674
- const user = c.get("user");
16675
- try {
16676
- const tokenPayload = {
16677
- userId: userId(user.id),
16678
- email: email(user.email),
16679
- domain: user.domain,
16680
- provider: user.provider,
16681
- isAdmin: user.isAdmin,
16682
- ...user.name && { name: user.name }
16683
- };
16684
- const refreshToken = JWTService.generateToken(tokenPayload, "30d");
16685
- const response = {
16686
- refresh_token: refreshToken
16687
- };
16688
- return c.json(response, 200);
16689
- } catch (error) {
16690
- getRouteLogger2().error("MCP token generation error", {
16691
- error: error instanceof Error ? error.message : String(error),
16692
- stack: error instanceof Error ? error.stack : void 0
16693
- });
16694
- return c.json({ error: "Failed to generate refresh token" }, 401);
16695
- }
16696
- });
16697
16629
  authRouter.post("/api/tokens/agent", async (c) => {
16698
16630
  const workerSecret = process.env.SEMIONT_WORKER_SECRET;
16699
16631
  if (!workerSecret) {
@@ -16748,7 +16680,8 @@ authRouter.post("/api/tokens/agent", async (c) => {
16748
16680
  domain: agentUser.domain,
16749
16681
  provider: agentUser.provider,
16750
16682
  isAdmin: false,
16751
- agentDid: did
16683
+ agentDid: did,
16684
+ tokenVersion: agentUser.tokenVersion
16752
16685
  }, "24h");
16753
16686
  return c.json({ token, did }, 200);
16754
16687
  });
@@ -16776,11 +16709,13 @@ authRouter.post("/api/users/accept-terms", authMiddleware, async (c) => {
16776
16709
  return c.json(response, 200);
16777
16710
  });
16778
16711
  authRouter.post("/api/users/logout", authMiddleware, async (c) => {
16779
- deleteCookie(c, "semiont-token", { path: "/" });
16780
- return c.json({
16781
- success: true,
16782
- message: "Logged out successfully"
16783
- }, 200);
16712
+ const user = c.get("user");
16713
+ const prisma2 = DatabaseConnection.getClient();
16714
+ await prisma2.user.update({
16715
+ where: { id: user.id },
16716
+ data: { tokenVersion: { increment: 1 } }
16717
+ });
16718
+ return c.body(null, 204);
16784
16719
  });
16785
16720
  authRouter.get("/api/cookies/consent", authMiddleware, async (c) => {
16786
16721
  return c.json({
@@ -17811,9 +17746,6 @@ var config = loadEnvironmentConfig(projectRoot, env);
17811
17746
  if (!config.services?.backend) {
17812
17747
  throw new Error("services.backend is required in environment config");
17813
17748
  }
17814
- if (!config.services.backend.corsOrigin) {
17815
- throw new Error("services.backend.corsOrigin is required in environment config");
17816
- }
17817
17749
  var backendService = config.services.backend;
17818
17750
  initializeLogger(config.logLevel);
17819
17751
  var logger = getLogger();
@@ -17844,10 +17776,7 @@ var makeMeaning = await startMakeMeaning(new SemiontProject(projectRoot), makeMe
17844
17776
  var __filename$1 = fileURLToPath(import.meta.url);
17845
17777
  var __dirname$1 = path.dirname(__filename$1);
17846
17778
  var app = new Hono();
17847
- app.use("*", cors({
17848
- origin: backendService.corsOrigin,
17849
- credentials: true
17850
- }));
17779
+ app.use("*", cors({ origin: "*" }));
17851
17780
  app.use("*", securityHeaders());
17852
17781
  app.use("*", requestIdMiddleware);
17853
17782
  app.use("*", errorLoggerMiddleware);
@@ -17935,6 +17864,11 @@ if (config.env?.NODE_ENV !== "test") {
17935
17864
  url: `http://localhost:${info.port}/api`,
17936
17865
  environment: config.env?.NODE_ENV ?? "development"
17937
17866
  });
17867
+ logger.info("Auth posture: bearer-only, open CORS", {
17868
+ cors: "any origin (*)",
17869
+ credentials: "disabled",
17870
+ auth: "Authorization: Bearer; media tokens via ?token= for /api/resources/:id"
17871
+ });
17938
17872
  try {
17939
17873
  const { JWTService: JWTService2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
17940
17874
  JWTService2.initialize(config);