@semiont/backend 0.5.7 → 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 +53 -110
- package/dist/index.js.map +1 -1
- package/dist/openapi.json +4 -118
- package/package.json +19 -20
- package/prisma/migrations/20260619120000_add_token_version/migration.sql +3 -0
- package/prisma/schema.prisma +1 -0
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';
|
|
@@ -30,11 +29,20 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
30
29
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
31
30
|
var __getProtoOf = Object.getPrototypeOf;
|
|
32
31
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
33
|
-
var __esm = (fn, res) => function __init() {
|
|
34
|
-
|
|
32
|
+
var __esm = (fn, res, err) => function __init() {
|
|
33
|
+
if (err) throw err[0];
|
|
34
|
+
try {
|
|
35
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
throw err = [e], e;
|
|
38
|
+
}
|
|
35
39
|
};
|
|
36
40
|
var __commonJS = (cb, mod) => function __require() {
|
|
37
|
-
|
|
41
|
+
try {
|
|
42
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw mod = 0, e;
|
|
45
|
+
}
|
|
38
46
|
};
|
|
39
47
|
var __export = (target, all) => {
|
|
40
48
|
for (var name in all)
|
|
@@ -186,6 +194,13 @@ var init_jwt_types = __esm({
|
|
|
186
194
|
// `_userId` instead of recomputing `userToDid(user)`. Unset for human
|
|
187
195
|
// tokens.
|
|
188
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(),
|
|
189
204
|
iat: z.number().optional(),
|
|
190
205
|
exp: z.number().optional()
|
|
191
206
|
});
|
|
@@ -12665,17 +12680,6 @@ var openapi_default = {
|
|
|
12665
12680
|
}
|
|
12666
12681
|
}
|
|
12667
12682
|
},
|
|
12668
|
-
MCPGenerateResponse: {
|
|
12669
|
-
type: "object",
|
|
12670
|
-
properties: {
|
|
12671
|
-
refresh_token: {
|
|
12672
|
-
type: "string"
|
|
12673
|
-
}
|
|
12674
|
-
},
|
|
12675
|
-
required: [
|
|
12676
|
-
"refresh_token"
|
|
12677
|
-
]
|
|
12678
|
-
},
|
|
12679
12683
|
Motivation: {
|
|
12680
12684
|
type: "string",
|
|
12681
12685
|
enum: [
|
|
@@ -16214,9 +16218,10 @@ var OAuthService = class {
|
|
|
16214
16218
|
...user.name && { name: user.name },
|
|
16215
16219
|
domain: user.domain,
|
|
16216
16220
|
provider: user.provider,
|
|
16217
|
-
isAdmin: user.isAdmin
|
|
16221
|
+
isAdmin: user.isAdmin,
|
|
16222
|
+
tokenVersion: user.tokenVersion
|
|
16218
16223
|
};
|
|
16219
|
-
const token = accessToken(JWTService.generateToken(jwtPayload, "
|
|
16224
|
+
const token = accessToken(JWTService.generateToken(jwtPayload, "10m"));
|
|
16220
16225
|
const refreshToken = JWTService.generateToken(jwtPayload, "30d");
|
|
16221
16226
|
return { user, token, refreshToken, isNewUser };
|
|
16222
16227
|
}
|
|
@@ -16240,6 +16245,9 @@ var OAuthService = class {
|
|
|
16240
16245
|
if (!user || !user.isActive) {
|
|
16241
16246
|
throw new Error("User not found or inactive");
|
|
16242
16247
|
}
|
|
16248
|
+
if (payload.tokenVersion !== user.tokenVersion) {
|
|
16249
|
+
throw new Error("Token revoked");
|
|
16250
|
+
}
|
|
16243
16251
|
return payload.agentDid ? { user, agentDid: payload.agentDid } : { user };
|
|
16244
16252
|
}
|
|
16245
16253
|
static async acceptTerms(userId2) {
|
|
@@ -16284,8 +16292,6 @@ var authMiddleware = async (c, next) => {
|
|
|
16284
16292
|
let tokenStr;
|
|
16285
16293
|
if (authHeader?.startsWith("Bearer ")) {
|
|
16286
16294
|
tokenStr = authHeader.substring(7).trim();
|
|
16287
|
-
} else {
|
|
16288
|
-
tokenStr = getCookie(c, "semiont-token");
|
|
16289
16295
|
}
|
|
16290
16296
|
if (!tokenStr) {
|
|
16291
16297
|
logger2.warn("Authentication failed: No token", {
|
|
@@ -16294,7 +16300,10 @@ var authMiddleware = async (c, next) => {
|
|
|
16294
16300
|
path: c.req.path,
|
|
16295
16301
|
method: c.req.method
|
|
16296
16302
|
});
|
|
16297
|
-
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);
|
|
16298
16307
|
}
|
|
16299
16308
|
try {
|
|
16300
16309
|
const { user, agentDid } = await OAuthService.getPrincipalFromToken(accessToken(tokenStr));
|
|
@@ -16464,22 +16473,15 @@ authRouter.post(
|
|
|
16464
16473
|
...user.name && { name: user.name },
|
|
16465
16474
|
domain: user.domain,
|
|
16466
16475
|
provider: user.provider,
|
|
16467
|
-
isAdmin: user.isAdmin
|
|
16476
|
+
isAdmin: user.isAdmin,
|
|
16477
|
+
tokenVersion: user.tokenVersion
|
|
16468
16478
|
};
|
|
16469
|
-
const token = JWTService.generateToken(jwtPayload, "
|
|
16479
|
+
const token = JWTService.generateToken(jwtPayload, "10m");
|
|
16470
16480
|
const refreshToken = JWTService.generateToken(jwtPayload, "30d");
|
|
16471
16481
|
await prisma2.user.update({
|
|
16472
16482
|
where: { id: user.id },
|
|
16473
16483
|
data: { lastLogin: /* @__PURE__ */ new Date() }
|
|
16474
16484
|
});
|
|
16475
|
-
setCookie(c, "semiont-token", token, {
|
|
16476
|
-
httpOnly: true,
|
|
16477
|
-
secure: process.env.NODE_ENV === "production",
|
|
16478
|
-
sameSite: "Lax",
|
|
16479
|
-
path: "/",
|
|
16480
|
-
maxAge: 60 * 60
|
|
16481
|
-
// 1 hour, matching access token lifetime
|
|
16482
|
-
});
|
|
16483
16485
|
const response = {
|
|
16484
16486
|
success: true,
|
|
16485
16487
|
user: {
|
|
@@ -16520,14 +16522,6 @@ authRouter.post(
|
|
|
16520
16522
|
}
|
|
16521
16523
|
const googleUser = await OAuthService.verifyGoogleToken(googleCredential(access_token));
|
|
16522
16524
|
const { user, token, refreshToken, isNewUser } = await OAuthService.createOrUpdateUser(googleUser);
|
|
16523
|
-
setCookie(c, "semiont-token", token, {
|
|
16524
|
-
httpOnly: true,
|
|
16525
|
-
secure: process.env.NODE_ENV === "production",
|
|
16526
|
-
sameSite: "Lax",
|
|
16527
|
-
path: "/",
|
|
16528
|
-
maxAge: 60 * 60
|
|
16529
|
-
// 1 hour, matching access token lifetime
|
|
16530
|
-
});
|
|
16531
16525
|
const response = {
|
|
16532
16526
|
success: true,
|
|
16533
16527
|
user: {
|
|
@@ -16579,15 +16573,19 @@ authRouter.post(
|
|
|
16579
16573
|
if (!user || !user.isActive) {
|
|
16580
16574
|
return c.json({ error: "User not found or inactive" }, 401);
|
|
16581
16575
|
}
|
|
16576
|
+
if (payload.tokenVersion !== user.tokenVersion) {
|
|
16577
|
+
return c.json({ error: "Token revoked" }, 401);
|
|
16578
|
+
}
|
|
16582
16579
|
const accessTokenPayload = {
|
|
16583
16580
|
userId: userId(user.id),
|
|
16584
16581
|
email: email(user.email),
|
|
16585
16582
|
domain: user.domain,
|
|
16586
16583
|
provider: user.provider,
|
|
16587
16584
|
isAdmin: user.isAdmin,
|
|
16588
|
-
...user.name && { name: user.name }
|
|
16585
|
+
...user.name && { name: user.name },
|
|
16586
|
+
tokenVersion: user.tokenVersion
|
|
16589
16587
|
};
|
|
16590
|
-
const accessToken2 = JWTService.generateToken(accessTokenPayload, "
|
|
16588
|
+
const accessToken2 = JWTService.generateToken(accessTokenPayload, "10m");
|
|
16591
16589
|
const response = {
|
|
16592
16590
|
access_token: accessToken2
|
|
16593
16591
|
};
|
|
@@ -16628,63 +16626,6 @@ authRouter.get("/api/users/me", authMiddleware, async (c) => {
|
|
|
16628
16626
|
};
|
|
16629
16627
|
return c.json(response, 200);
|
|
16630
16628
|
});
|
|
16631
|
-
authRouter.get("/api/tokens/mcp-setup", authMiddleware, async (c) => {
|
|
16632
|
-
const callback = c.req.query("callback");
|
|
16633
|
-
if (!callback) {
|
|
16634
|
-
return c.json({ error: "Callback URL required" }, 400);
|
|
16635
|
-
}
|
|
16636
|
-
const allowedCallbackPatterns = [
|
|
16637
|
-
/^http:\/\/localhost:\d+\/.*$/,
|
|
16638
|
-
/^http:\/\/127\.0\.0\.1:\d+\/.*$/,
|
|
16639
|
-
/^http:\/\/\[::1\]:\d+\/.*$/
|
|
16640
|
-
];
|
|
16641
|
-
if (!allowedCallbackPatterns.some((p) => p.test(callback))) {
|
|
16642
|
-
return c.json({ error: "Invalid callback URL. Must be a localhost URL for CLI authentication." }, 400);
|
|
16643
|
-
}
|
|
16644
|
-
const user = c.get("user");
|
|
16645
|
-
try {
|
|
16646
|
-
const tokenPayload = {
|
|
16647
|
-
userId: userId(user.id),
|
|
16648
|
-
email: email(user.email),
|
|
16649
|
-
domain: user.domain,
|
|
16650
|
-
provider: user.provider,
|
|
16651
|
-
isAdmin: user.isAdmin,
|
|
16652
|
-
...user.name && { name: user.name }
|
|
16653
|
-
};
|
|
16654
|
-
const refreshToken = JWTService.generateToken(tokenPayload, "30d");
|
|
16655
|
-
return c.redirect(`${callback}?token=${refreshToken}`, 302);
|
|
16656
|
-
} catch (error) {
|
|
16657
|
-
getRouteLogger2().error("MCP setup error", {
|
|
16658
|
-
error: error instanceof Error ? error.message : String(error),
|
|
16659
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
16660
|
-
});
|
|
16661
|
-
return c.json({ error: "Failed to generate refresh token" }, 500);
|
|
16662
|
-
}
|
|
16663
|
-
});
|
|
16664
|
-
authRouter.post("/api/tokens/mcp-generate", authMiddleware, async (c) => {
|
|
16665
|
-
const user = c.get("user");
|
|
16666
|
-
try {
|
|
16667
|
-
const tokenPayload = {
|
|
16668
|
-
userId: userId(user.id),
|
|
16669
|
-
email: email(user.email),
|
|
16670
|
-
domain: user.domain,
|
|
16671
|
-
provider: user.provider,
|
|
16672
|
-
isAdmin: user.isAdmin,
|
|
16673
|
-
...user.name && { name: user.name }
|
|
16674
|
-
};
|
|
16675
|
-
const refreshToken = JWTService.generateToken(tokenPayload, "30d");
|
|
16676
|
-
const response = {
|
|
16677
|
-
refresh_token: refreshToken
|
|
16678
|
-
};
|
|
16679
|
-
return c.json(response, 200);
|
|
16680
|
-
} catch (error) {
|
|
16681
|
-
getRouteLogger2().error("MCP token generation error", {
|
|
16682
|
-
error: error instanceof Error ? error.message : String(error),
|
|
16683
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
16684
|
-
});
|
|
16685
|
-
return c.json({ error: "Failed to generate refresh token" }, 401);
|
|
16686
|
-
}
|
|
16687
|
-
});
|
|
16688
16629
|
authRouter.post("/api/tokens/agent", async (c) => {
|
|
16689
16630
|
const workerSecret = process.env.SEMIONT_WORKER_SECRET;
|
|
16690
16631
|
if (!workerSecret) {
|
|
@@ -16739,7 +16680,8 @@ authRouter.post("/api/tokens/agent", async (c) => {
|
|
|
16739
16680
|
domain: agentUser.domain,
|
|
16740
16681
|
provider: agentUser.provider,
|
|
16741
16682
|
isAdmin: false,
|
|
16742
|
-
agentDid: did
|
|
16683
|
+
agentDid: did,
|
|
16684
|
+
tokenVersion: agentUser.tokenVersion
|
|
16743
16685
|
}, "24h");
|
|
16744
16686
|
return c.json({ token, did }, 200);
|
|
16745
16687
|
});
|
|
@@ -16767,11 +16709,13 @@ authRouter.post("/api/users/accept-terms", authMiddleware, async (c) => {
|
|
|
16767
16709
|
return c.json(response, 200);
|
|
16768
16710
|
});
|
|
16769
16711
|
authRouter.post("/api/users/logout", authMiddleware, async (c) => {
|
|
16770
|
-
|
|
16771
|
-
|
|
16772
|
-
|
|
16773
|
-
|
|
16774
|
-
|
|
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);
|
|
16775
16719
|
});
|
|
16776
16720
|
authRouter.get("/api/cookies/consent", authMiddleware, async (c) => {
|
|
16777
16721
|
return c.json({
|
|
@@ -17802,9 +17746,6 @@ var config = loadEnvironmentConfig(projectRoot, env);
|
|
|
17802
17746
|
if (!config.services?.backend) {
|
|
17803
17747
|
throw new Error("services.backend is required in environment config");
|
|
17804
17748
|
}
|
|
17805
|
-
if (!config.services.backend.corsOrigin) {
|
|
17806
|
-
throw new Error("services.backend.corsOrigin is required in environment config");
|
|
17807
|
-
}
|
|
17808
17749
|
var backendService = config.services.backend;
|
|
17809
17750
|
initializeLogger(config.logLevel);
|
|
17810
17751
|
var logger = getLogger();
|
|
@@ -17835,10 +17776,7 @@ var makeMeaning = await startMakeMeaning(new SemiontProject(projectRoot), makeMe
|
|
|
17835
17776
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
17836
17777
|
var __dirname$1 = path.dirname(__filename$1);
|
|
17837
17778
|
var app = new Hono();
|
|
17838
|
-
app.use("*", cors({
|
|
17839
|
-
origin: backendService.corsOrigin,
|
|
17840
|
-
credentials: true
|
|
17841
|
-
}));
|
|
17779
|
+
app.use("*", cors({ origin: "*" }));
|
|
17842
17780
|
app.use("*", securityHeaders());
|
|
17843
17781
|
app.use("*", requestIdMiddleware);
|
|
17844
17782
|
app.use("*", errorLoggerMiddleware);
|
|
@@ -17926,6 +17864,11 @@ if (config.env?.NODE_ENV !== "test") {
|
|
|
17926
17864
|
url: `http://localhost:${info.port}/api`,
|
|
17927
17865
|
environment: config.env?.NODE_ENV ?? "development"
|
|
17928
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
|
+
});
|
|
17929
17872
|
try {
|
|
17930
17873
|
const { JWTService: JWTService2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
|
|
17931
17874
|
JWTService2.initialize(config);
|