Skip to content

[Feature] Add mapUserToResourceId callback to auth config for automatic resource ID scoping #13926

@steffendsommer

Description

@steffendsommer

Problem

When auth is configured, coreAuthMiddleware authenticates the user and sets user on requestContext (line 278 of helpers.ts). However, it does not set mastra__resourceId, which is required for memory/thread scoping per user.

Currently, there's no first-class way to set mastra__resourceId after auth succeeds because:

  1. checkRouteAuth runs inside the route handler callback, not as separate framework middleware
  2. By the time user-defined middleware runs, the user isn't on requestContext yet
  3. The only workaround is subclassing the server adapter and overriding checkRouteAuth — which works but is adapter-specific and feels like a hack

This affects all server adapters (Hono, Express, Next.js, etc.) and is especially painful for Mastra Studio users who want threads/memory scoped to the authenticated user.

Proposed Solution

Add a mapUserToResourceId callback to the server auth config:

const server = new MastraServer({
  auth: workosAuth,
  mapUserToResourceId: (user) => user.id, // simple case
  // or for org-scoped:
  // mapUserToResourceId: (user) => `${user.organizationId}:${user.id}`,
});

In coreAuthMiddleware, after the user is authenticated and set on requestContext:

requestContext.set('user', user);

// Auto-set resource ID if callback is provided
if (authConfig.mapUserToResourceId) {
  const resourceId = authConfig.mapUserToResourceId(user);
  if (resourceId) {
    requestContext.set(MASTRA_RESOURCE_ID_KEY, resourceId);
  }
}

Since this lives in the shared coreAuthMiddleware (not in any adapter), it would work across all server adapters automatically.

Why not just default to user.id?

The resource ID isn't always user.id. Common cases where it differs:

  • Org-scoped: ${user.organizationId}:${user.id}
  • Custom claims: Resource ID from a JWT claim or external mapping
  • Multi-tenant: Scoped to a tenant ID rather than user ID
  • Composite keys: Combination of user + workspace + project

A callback gives consumers full control while keeping the common case simple.

Current Workaround

Subclass the adapter-specific server class and override checkRouteAuth. For example, with the Hono adapter:

import { MastraServer } from '@mastra/hono';
import { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';

class MyMastraServer extends MastraServer {
  protected override async checkRouteAuth(route: any, context: any) {
    const result = await super.checkRouteAuth(route, context);
    if (!result && context.requestContext) {
      const user = context.requestContext.get('user');
      if (user?.id) {
        context.requestContext.set(MASTRA_RESOURCE_ID_KEY, user.id);
      }
    }
    return result;
  }
}

Similar overrides would be needed for Express, Next.js, or any other adapter. This works but requires adapter-specific subclassing for something that should be a shared config option.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions