@mastra/express 1.1.7 → 1.1.8-alpha.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @mastra/express
2
2
 
3
+ ## 1.1.8-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Added RBAC permission enforcement to all server adapters. When an auth provider is configured, each route's required permission is checked against the authenticated user's permissions before the handler runs. Permissions are derived automatically from route paths and HTTP methods using the convention-based system from `@mastra/server`. ([#13163](https://github.com/mastra-ai/mastra/pull/13163))
8
+
9
+ - Added HTTP request logging middleware. Enable with `apiReqLogs: true` for default settings, or pass a configuration object for fine-grained control. ([#11907](https://github.com/mastra-ai/mastra/pull/11907))
10
+
11
+ **Simple activation**
12
+
13
+ ```ts
14
+ const mastra = new Mastra({
15
+ server: { build: { apiReqLogs: true } },
16
+ });
17
+ ```
18
+
19
+ **Advanced configuration**
20
+
21
+ ```ts
22
+ const mastra = new Mastra({
23
+ server: {
24
+ build: {
25
+ apiReqLogs: {
26
+ enabled: true,
27
+ level: 'debug',
28
+ excludePaths: ['/health'],
29
+ includeHeaders: true,
30
+ includeQueryParams: true,
31
+ redactHeaders: ['authorization', 'cookie'],
32
+ },
33
+ },
34
+ },
35
+ });
36
+ ```
37
+
38
+ - Updated dependencies [[`504fc8b`](https://github.com/mastra-ai/mastra/commit/504fc8b9d0ddab717577ad3bf9c95ea4bd5377bd), [`f9c150b`](https://github.com/mastra-ai/mastra/commit/f9c150b7595ad05ad9cc9a11098e2944361e8c22), [`88de7e8`](https://github.com/mastra-ai/mastra/commit/88de7e8dfe4b7e1951a9e441bb33136e705ce24e), [`6dbeeb9`](https://github.com/mastra-ai/mastra/commit/6dbeeb94a8b1eebb727300d1a98961f882180794), [`edee4b3`](https://github.com/mastra-ai/mastra/commit/edee4b37dff0af515fc7cc0e8d71ee39e6a762f0), [`3790c75`](https://github.com/mastra-ai/mastra/commit/3790c7578cc6a47d854eb12d89e6b1912867fe29), [`e7a235b`](https://github.com/mastra-ai/mastra/commit/e7a235be6472e0c870ed6c791ddb17c492dc188b), [`d51d298`](https://github.com/mastra-ai/mastra/commit/d51d298953967aab1f58ec965b644d109214f085), [`6dbeeb9`](https://github.com/mastra-ai/mastra/commit/6dbeeb94a8b1eebb727300d1a98961f882180794), [`d5f0d8d`](https://github.com/mastra-ai/mastra/commit/d5f0d8d6a03e515ddaa9b5da19b7e44b8357b07b), [`09c3b18`](https://github.com/mastra-ai/mastra/commit/09c3b1802ff14e243a8a8baea327440bc8cc2e32), [`b896379`](https://github.com/mastra-ai/mastra/commit/b8963791c6afa79484645fcec596a201f936b9a2), [`85c84eb`](https://github.com/mastra-ai/mastra/commit/85c84ebb78aebfcba9d209c8e152b16d7a00cb71), [`a89272a`](https://github.com/mastra-ai/mastra/commit/a89272a5d71939b9fcd284e6a6dc1dd091a6bdcf), [`ee9c8df`](https://github.com/mastra-ai/mastra/commit/ee9c8df644f19d055af5f496bf4942705f5a47b7), [`77b4a25`](https://github.com/mastra-ai/mastra/commit/77b4a254e51907f8ff3a3ba95596a18e93ae4b35), [`276246e`](https://github.com/mastra-ai/mastra/commit/276246e0b9066a1ea48bbc70df84dbe528daaf99), [`08ecfdb`](https://github.com/mastra-ai/mastra/commit/08ecfdbdad6fb8285deef86a034bdf4a6047cfca), [`d5f628c`](https://github.com/mastra-ai/mastra/commit/d5f628ca86c6f6f3ff1035d52f635df32dd81cab), [`524c0f3`](https://github.com/mastra-ai/mastra/commit/524c0f3c434c3d9d18f66338dcef383d6161b59c), [`c18a0e9`](https://github.com/mastra-ai/mastra/commit/c18a0e9cef1e4ca004b2963d35e4cfc031971eac), [`4bd21ea`](https://github.com/mastra-ai/mastra/commit/4bd21ea43d44d0a0427414fc047577f9f0aa3bec), [`115a7a4`](https://github.com/mastra-ai/mastra/commit/115a7a47db5e9896fec12ae6507501adb9ec89bf), [`22a48ae`](https://github.com/mastra-ai/mastra/commit/22a48ae2513eb54d8d79dad361fddbca97a155e8), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9311c17`](https://github.com/mastra-ai/mastra/commit/9311c17d7a0640d9c4da2e71b814dc67c57c6369), [`7edf78f`](https://github.com/mastra-ai/mastra/commit/7edf78f80422c43e84585f08ba11df0d4d0b73c5), [`1c4221c`](https://github.com/mastra-ai/mastra/commit/1c4221cf6032ec98d0e094d4ee11da3e48490d96), [`6dbeeb9`](https://github.com/mastra-ai/mastra/commit/6dbeeb94a8b1eebb727300d1a98961f882180794), [`d25b9ea`](https://github.com/mastra-ai/mastra/commit/d25b9eabd400167255a97b690ffbc4ee4097ded5), [`fe1ce5c`](https://github.com/mastra-ai/mastra/commit/fe1ce5c9211c03d561606fda95cbfe7df1d9a9b5), [`b03c0e0`](https://github.com/mastra-ai/mastra/commit/b03c0e0389a799523929a458b0509c9e4244d562), [`0a8366b`](https://github.com/mastra-ai/mastra/commit/0a8366b0a692fcdde56c4d526e4cf03c502ae4ac), [`85664e9`](https://github.com/mastra-ai/mastra/commit/85664e9fd857320fbc245e301f764f45f66f32a3), [`bc79650`](https://github.com/mastra-ai/mastra/commit/bc796500c6e0334faa158a96077e3fb332274869), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`3a3a59e`](https://github.com/mastra-ai/mastra/commit/3a3a59e8ffaa6a985fe3d9a126a3f5ade11a6724), [`3108d4e`](https://github.com/mastra-ai/mastra/commit/3108d4e649c9fddbf03253a6feeb388a5fa9fa5a), [`0c33b2c`](https://github.com/mastra-ai/mastra/commit/0c33b2c9db537f815e1c59e2c898ffce2e395a79), [`191e5bd`](https://github.com/mastra-ai/mastra/commit/191e5bd29b82f5bda35243945790da7bc7b695c2), [`f77cd94`](https://github.com/mastra-ai/mastra/commit/f77cd94c44eabed490384e7d19232a865e13214c), [`e8135c7`](https://github.com/mastra-ai/mastra/commit/e8135c7e300dac5040670eec7eab896ac6092e30), [`daca48f`](https://github.com/mastra-ai/mastra/commit/daca48f0fb17b7ae0b62a2ac40cf0e491b2fd0b7), [`bc79650`](https://github.com/mastra-ai/mastra/commit/bc796500c6e0334faa158a96077e3fb332274869), [`257d14f`](https://github.com/mastra-ai/mastra/commit/257d14faca5931f2e4186fc165b6f0b1f915deee), [`352f25d`](https://github.com/mastra-ai/mastra/commit/352f25da316b24cdd5b410fd8dddf6a8b763da2a), [`93477d0`](https://github.com/mastra-ai/mastra/commit/93477d0769b8a13ea5ed73d508d967fb23eaeed9), [`31c78b3`](https://github.com/mastra-ai/mastra/commit/31c78b3eb28f58a8017f1dcc795c33214d87feac), [`0bc0720`](https://github.com/mastra-ai/mastra/commit/0bc07201095791858087cc56f353fcd65e87ab54), [`36516ac`](https://github.com/mastra-ai/mastra/commit/36516aca1021cbeb42e74751b46a2614101f37c8), [`e947652`](https://github.com/mastra-ai/mastra/commit/e9476527fdecb4449e54570e80dfaf8466901254), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`ec248f6`](https://github.com/mastra-ai/mastra/commit/ec248f6b56e8a037c066c49b2178e2507471d988)]:
39
+ - @mastra/core@1.9.0-alpha.0
40
+ - @mastra/server@1.9.0-alpha.0
41
+
3
42
  ## 1.1.7
4
43
 
5
44
  ### Patch Changes
package/LICENSE.md CHANGED
@@ -1,3 +1,18 @@
1
+ Portions of this software are licensed as follows:
2
+
3
+ - All content that resides under any directory named "ee/" within this
4
+ repository, including but not limited to:
5
+ - `packages/core/src/auth/ee/`
6
+ - `packages/server/src/server/auth/ee/`
7
+ is licensed under the license defined in `ee/LICENSE`.
8
+
9
+ - All third-party components incorporated into the Mastra Software are
10
+ licensed under the original license provided by the owner of the
11
+ applicable component.
12
+
13
+ - Content outside of the above-mentioned directories or restrictions is
14
+ available under the "Apache License 2.0" as defined below.
15
+
1
16
  # Apache License 2.0
2
17
 
3
18
  Copyright (c) 2025 Kepler Software, Inc.
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var busboy = require('@fastify/busboy');
4
+ var auth = require('@mastra/server/auth');
4
5
  var error = require('@mastra/server/handlers/error');
5
6
  var serverAdapter = require('@mastra/server/server-adapter');
6
- var auth = require('@mastra/server/auth');
7
7
 
8
8
  // src/index.ts
9
9
 
@@ -213,130 +213,39 @@ ZodError.create = (issues) => {
213
213
  const error = new ZodError(issues);
214
214
  return error;
215
215
  };
216
- var authenticationMiddleware = async (req, res, next) => {
217
- const mastra = res.locals.mastra;
218
- const authConfig = mastra.getServer()?.auth;
219
- const customRouteAuthConfig = res.locals.customRouteAuthConfig;
220
- if (!authConfig) {
221
- return next();
222
- }
223
- const path = req.path;
224
- const method = req.method;
225
- const getHeader = (name) => req.headers[name.toLowerCase()];
226
- if (auth.isDevPlaygroundRequest(path, method, getHeader, authConfig, customRouteAuthConfig)) {
227
- return next();
228
- }
229
- if (!auth.isProtectedPath(req.path, req.method, authConfig, customRouteAuthConfig)) {
230
- return next();
231
- }
232
- if (auth.canAccessPublicly(req.path, req.method, authConfig)) {
233
- return next();
234
- }
235
- const authHeader = req.headers.authorization;
236
- let token = authHeader ? authHeader.replace("Bearer ", "") : null;
237
- if (!token && req.query.apiKey) {
238
- token = req.query.apiKey || null;
239
- }
240
- if (!token) {
241
- return res.status(401).json({ error: "Authentication required" });
242
- }
243
- try {
244
- let user;
245
- if (typeof authConfig.authenticateToken === "function") {
246
- user = await authConfig.authenticateToken(token, req);
247
- } else {
248
- throw new Error("No token verification method configured");
249
- }
250
- if (!user) {
251
- return res.status(401).json({ error: "Invalid or expired token" });
252
- }
253
- res.locals.requestContext.set("user", user);
254
- return next();
255
- } catch (err) {
256
- mastra.getLogger()?.error("Authentication error", {
257
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err
216
+
217
+ // src/index.ts
218
+ var _hasPermissionPromise;
219
+ function loadHasPermission() {
220
+ if (!_hasPermissionPromise) {
221
+ _hasPermissionPromise = import('@mastra/core/auth/ee').then((m) => m.hasPermission).catch(() => {
222
+ console.error(
223
+ "[@mastra/express] Auth features require @mastra/core >= 1.6.0. Please upgrade: npm install @mastra/core@latest"
224
+ );
225
+ return void 0;
258
226
  });
259
- return res.status(401).json({ error: "Invalid or expired token" });
260
- }
261
- };
262
- var authorizationMiddleware = async (req, res, next) => {
263
- const mastra = res.locals.mastra;
264
- const authConfig = mastra.getServer()?.auth;
265
- const customRouteAuthConfig = res.locals.customRouteAuthConfig;
266
- if (!authConfig) {
267
- return next();
268
- }
269
- const path = req.path;
270
- const method = req.method;
271
- const getHeader = (name) => req.headers[name.toLowerCase()];
272
- if (auth.isDevPlaygroundRequest(path, method, getHeader, authConfig, customRouteAuthConfig)) {
273
- return next();
274
227
  }
275
- if (!auth.isProtectedPath(req.path, req.method, authConfig, customRouteAuthConfig)) {
276
- return next();
277
- }
278
- if (auth.canAccessPublicly(path, method, authConfig)) {
279
- return next();
280
- }
281
- const user = res.locals.requestContext.get("user");
282
- if ("authorizeUser" in authConfig && typeof authConfig.authorizeUser === "function") {
283
- try {
284
- const isAuthorized = await authConfig.authorizeUser(user, req);
285
- if (isAuthorized) {
286
- return next();
287
- }
288
- return res.status(403).json({ error: "Access denied" });
289
- } catch (err) {
290
- mastra.getLogger()?.error("Authorization error in authorizeUser", {
291
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err
292
- });
293
- return res.status(500).json({ error: "Authorization error" });
294
- }
295
- }
296
- if ("authorize" in authConfig && typeof authConfig.authorize === "function") {
297
- try {
298
- const context = {
299
- get: (key) => {
300
- if (key === "mastra") return res.locals.mastra;
301
- if (key === "requestContext") return res.locals.requestContext;
302
- if (key === "registeredTools") return res.locals.registeredTools;
303
- if (key === "taskStore") return res.locals.taskStore;
304
- if (key === "customRouteAuthConfig") return res.locals.customRouteAuthConfig;
305
- return void 0;
306
- },
307
- req
308
- };
309
- const isAuthorized = await authConfig.authorize(path, method, user, context);
310
- if (isAuthorized) {
311
- return next();
228
+ return _hasPermissionPromise;
229
+ }
230
+ function toWebRequest(req) {
231
+ const protocol = req.protocol || "http";
232
+ const host = req.get("host") || "localhost";
233
+ const url = `${protocol}://${host}${req.originalUrl || req.url}`;
234
+ const headers = new Headers();
235
+ for (const [key, value] of Object.entries(req.headers)) {
236
+ if (value) {
237
+ if (Array.isArray(value)) {
238
+ value.forEach((v) => headers.append(key, v));
239
+ } else {
240
+ headers.set(key, value);
312
241
  }
313
- return res.status(403).json({ error: "Access denied" });
314
- } catch (err) {
315
- mastra.getLogger()?.error("Authorization error in authorize", {
316
- error: err instanceof Error ? { message: err.message, stack: err.stack } : err,
317
- path,
318
- method
319
- });
320
- return res.status(500).json({ error: "Authorization error" });
321
- }
322
- }
323
- if ("rules" in authConfig && authConfig.rules && authConfig.rules.length > 0) {
324
- const isAuthorized = await auth.checkRules(authConfig.rules, path, method, user);
325
- if (isAuthorized) {
326
- return next();
327
242
  }
328
- return res.status(403).json({ error: "Access denied" });
329
243
  }
330
- if (auth.defaultAuthConfig.rules && auth.defaultAuthConfig.rules.length > 0) {
331
- const isAuthorized = await auth.checkRules(auth.defaultAuthConfig.rules, path, method, user);
332
- if (isAuthorized) {
333
- return next();
334
- }
335
- }
336
- return res.status(403).json({ error: "Access denied" });
337
- };
338
-
339
- // src/index.ts
244
+ return new globalThis.Request(url, {
245
+ method: req.method,
246
+ headers
247
+ });
248
+ }
340
249
  var MastraServer = class extends serverAdapter.MastraServer {
341
250
  createContextMiddleware() {
342
251
  return async (req, res, next) => {
@@ -599,7 +508,9 @@ var MastraServer = class extends serverAdapter.MastraServer {
599
508
  method: String(req.method || "GET"),
600
509
  getHeader: (name) => req.headers[name.toLowerCase()],
601
510
  getQuery: (name) => req.query[name],
602
- requestContext: res.locals.requestContext
511
+ requestContext: res.locals.requestContext,
512
+ request: toWebRequest(req),
513
+ buildAuthorizeContext: () => toWebRequest(req)
603
514
  });
604
515
  if (authError) {
605
516
  return res.status(authError.status).json({ error: authError.error });
@@ -670,6 +581,20 @@ var MastraServer = class extends serverAdapter.MastraServer {
670
581
  abortSignal: res.locals.abortSignal,
671
582
  routePrefix: prefix
672
583
  };
584
+ const authConfig = this.mastra.getServer()?.auth;
585
+ if (authConfig) {
586
+ const hasPermission = await loadHasPermission();
587
+ if (hasPermission) {
588
+ const userPermissions = res.locals.requestContext.get("userPermissions");
589
+ const permissionError = this.checkRoutePermission(route, userPermissions, hasPermission);
590
+ if (permissionError) {
591
+ return res.status(permissionError.status).json({
592
+ error: permissionError.error,
593
+ message: permissionError.message
594
+ });
595
+ }
596
+ }
597
+ }
673
598
  try {
674
599
  const result = await route.handler(handlerParams);
675
600
  await this.sendResponse(route, res, result, req, prefix);
@@ -695,6 +620,43 @@ var MastraServer = class extends serverAdapter.MastraServer {
695
620
  async registerCustomApiRoutes() {
696
621
  if (!await this.buildCustomRouteHandler()) return;
697
622
  this.app.use(async (req, res, next) => {
623
+ const path = String(req.path || "/");
624
+ const method = String(req.method || "GET");
625
+ if (auth.isProtectedCustomRoute(path, method, this.customRouteAuthConfig)) {
626
+ const serverRoute = {
627
+ method,
628
+ path,
629
+ responseType: "json",
630
+ handler: async () => {
631
+ }
632
+ };
633
+ const authError = await this.checkRouteAuth(serverRoute, {
634
+ path,
635
+ method,
636
+ getHeader: (name) => req.headers[name.toLowerCase()],
637
+ getQuery: (name) => req.query[name],
638
+ requestContext: res.locals.requestContext,
639
+ request: toWebRequest(req),
640
+ buildAuthorizeContext: () => toWebRequest(req)
641
+ });
642
+ if (authError) {
643
+ return res.status(authError.status).json({ error: authError.error });
644
+ }
645
+ const authConfig = this.mastra.getServer()?.auth;
646
+ if (authConfig) {
647
+ const hasPermission = await loadHasPermission();
648
+ if (hasPermission) {
649
+ const userPermissions = res.locals.requestContext.get("userPermissions");
650
+ const permissionError = this.checkRoutePermission(serverRoute, userPermissions, hasPermission);
651
+ if (permissionError) {
652
+ return res.status(permissionError.status).json({
653
+ error: permissionError.error,
654
+ message: permissionError.message
655
+ });
656
+ }
657
+ }
658
+ }
659
+ }
698
660
  const response = await this.handleCustomRouteRequest(
699
661
  `${req.protocol}://${req.get("host") || "localhost"}${req.originalUrl}`,
700
662
  req.method,
@@ -710,12 +672,46 @@ var MastraServer = class extends serverAdapter.MastraServer {
710
672
  this.app.use(this.createContextMiddleware());
711
673
  }
712
674
  registerAuthMiddleware() {
713
- const authConfig = this.mastra.getServer()?.auth;
714
- if (!authConfig) {
675
+ }
676
+ registerHttpLoggingMiddleware() {
677
+ if (!this.httpLoggingConfig?.enabled) {
715
678
  return;
716
679
  }
717
- this.app.use(authenticationMiddleware);
718
- this.app.use(authorizationMiddleware);
680
+ this.app.use((req, res, next) => {
681
+ if (!this.shouldLogRequest(req.path)) {
682
+ return next();
683
+ }
684
+ const start = Date.now();
685
+ const method = req.method;
686
+ const path = req.path;
687
+ res.on("finish", () => {
688
+ const duration = Date.now() - start;
689
+ const status = res.statusCode;
690
+ const level = this.httpLoggingConfig?.level || "info";
691
+ const logData = {
692
+ method,
693
+ path,
694
+ status,
695
+ duration: `${duration}ms`
696
+ };
697
+ if (this.httpLoggingConfig?.includeQueryParams) {
698
+ logData.query = req.query;
699
+ }
700
+ if (this.httpLoggingConfig?.includeHeaders) {
701
+ const headers = { ...req.headers };
702
+ const redactHeaders = this.httpLoggingConfig.redactHeaders || [];
703
+ redactHeaders.forEach((h) => {
704
+ const key = h.toLowerCase();
705
+ if (headers[key] !== void 0) {
706
+ headers[key] = "[REDACTED]";
707
+ }
708
+ });
709
+ logData.headers = headers;
710
+ }
711
+ this.logger[level](`${method} ${path} ${status} ${duration}ms`, logData);
712
+ });
713
+ next();
714
+ });
719
715
  }
720
716
  };
721
717