@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, "
|
|
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({
|
|
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, "
|
|
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, "
|
|
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
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
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);
|