@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
498 lines (492 loc) • 16.2 kB
JavaScript
import { MastraBase } from '../chunk-WENZPAHS.js';
import { RegisteredLogger } from '../chunk-DBBWTK24.js';
import { MastraError } from '../chunk-FJEVLHJT.js';
// src/server/auth.ts
var MastraAuthProvider = class extends MastraBase {
protected;
public;
constructor(options) {
super({ component: "AUTH", name: options?.name });
if (options?.authorizeUser) {
this.authorizeUser = options.authorizeUser.bind(this);
}
this.protected = options?.protected;
this.public = options?.public;
this.mapUserToResourceId = options?.mapUserToResourceId;
}
registerOptions(opts) {
if (opts?.authorizeUser) {
this.authorizeUser = opts.authorizeUser.bind(this);
}
if (opts?.mapUserToResourceId) {
this.mapUserToResourceId = opts.mapUserToResourceId;
}
if (opts?.protected) {
this.protected = opts.protected;
}
if (opts?.public) {
this.public = opts.public;
}
}
};
// src/server/composite-auth.ts
function isSSOProvider(p) {
return p !== null && typeof p === "object" && typeof p.getLoginUrl === "function" && typeof p.handleCallback === "function";
}
function isSessionProvider(p) {
return p !== null && typeof p === "object" && typeof p.validateSession === "function" && typeof p.createSession === "function";
}
function isUserProvider(p) {
return p !== null && typeof p === "object" && typeof p.getCurrentUser === "function";
}
function isObjectLike(value) {
return typeof value === "object" && value !== null || typeof value === "function";
}
var CompositeAuth = class extends MastraAuthProvider {
providers;
authenticatedProviderByObject = /* @__PURE__ */ new WeakMap();
authenticatedProviderByPrimitive = /* @__PURE__ */ new Map();
constructor(providers) {
const combinedPublic = providers.flatMap((provider) => provider.public ?? []);
const combinedProtected = providers.flatMap((provider) => provider.protected ?? []);
super({
public: combinedPublic,
protected: combinedProtected
});
this.providers = providers;
if (providers.some((provider) => typeof provider.mapUserToResourceId === "function")) {
this.mapUserToResourceId = (user) => this.mapAuthenticatedUserToResourceId(user);
}
if (!providers.some(isSSOProvider)) {
this.getLoginUrl = void 0;
this.handleCallback = void 0;
this.getLoginButtonConfig = void 0;
}
if (!providers.some(isSessionProvider)) {
this.createSession = void 0;
this.validateSession = void 0;
this.getSessionIdFromRequest = void 0;
}
if (!providers.some(isUserProvider)) {
this.getCurrentUser = void 0;
this.getUser = void 0;
}
}
// Find first provider implementing an interface
findProvider(check) {
return this.providers.find(check);
}
rememberAuthenticatedProvider(user, provider) {
if (isObjectLike(user)) {
this.authenticatedProviderByObject.set(user, provider);
return;
}
this.authenticatedProviderByPrimitive.set(user, provider);
}
takeAuthenticatedProvider(user) {
if (isObjectLike(user)) {
const provider2 = this.authenticatedProviderByObject.get(user);
this.authenticatedProviderByObject.delete(user);
return provider2;
}
const primitiveUser = user;
const provider = this.authenticatedProviderByPrimitive.get(primitiveUser);
this.authenticatedProviderByPrimitive.delete(primitiveUser);
return provider;
}
mapAuthenticatedUserToResourceId(user) {
const provider = this.takeAuthenticatedProvider(user);
return provider?.mapUserToResourceId?.(user);
}
// ============================================================================
// License Exemption Markers
// Expose these if any underlying provider has them
// ============================================================================
/**
* True if any provider is MastraCloudAuth (exempt from license requirement).
*/
get isMastraCloudAuth() {
return this.providers.some(
(p) => "isMastraCloudAuth" in p && p.isMastraCloudAuth === true
);
}
/**
* True if any provider is SimpleAuth (exempt from license requirement).
*/
get isSimpleAuth() {
return this.providers.some((p) => "isSimpleAuth" in p && p.isSimpleAuth === true);
}
// ============================================================================
// MastraAuthProvider Implementation
// ============================================================================
async authenticateToken(token, request) {
for (const provider of this.providers) {
try {
const user = await provider.authenticateToken(token, request);
if (user) {
this.rememberAuthenticatedProvider(user, provider);
return user;
}
} catch {
}
}
return null;
}
async authorizeUser(user, request) {
for (const provider of this.providers) {
const authorized = await provider.authorizeUser(user, request);
if (authorized) {
return true;
}
}
return false;
}
// ============================================================================
// ISSOProvider Implementation
// ============================================================================
/**
* Forward cookie header to SSO provider for PKCE validation.
* Called by auth handler before handleCallback().
*/
setCallbackCookieHeader(cookieHeader) {
const sso = this.findProvider(isSSOProvider);
if (sso && typeof sso.setCallbackCookieHeader === "function") {
sso.setCallbackCookieHeader(cookieHeader);
}
}
getLoginUrl(redirectUri, state) {
const sso = this.findProvider(isSSOProvider);
if (!sso) throw new Error("No SSO provider configured in CompositeAuth");
return sso.getLoginUrl(redirectUri, state);
}
getLoginCookies(redirectUri, state) {
const sso = this.findProvider(isSSOProvider);
return sso?.getLoginCookies?.(redirectUri, state);
}
async handleCallback(code, state) {
const sso = this.findProvider(isSSOProvider);
if (!sso) throw new Error("No SSO provider configured in CompositeAuth");
return sso.handleCallback(code, state);
}
getLoginButtonConfig() {
const sso = this.findProvider(isSSOProvider);
if (!sso) return { provider: "unknown", text: "Sign in" };
return sso.getLoginButtonConfig();
}
async getLogoutUrl(redirectUri, request) {
for (const provider of this.providers) {
if (isSSOProvider(provider) && provider.getLogoutUrl) {
try {
const url = await provider.getLogoutUrl(redirectUri, request);
if (url) return url;
} catch {
}
}
}
return null;
}
// ============================================================================
// ISessionProvider Implementation
// ============================================================================
async createSession(userId, metadata) {
const session = this.findProvider(isSessionProvider);
if (!session) throw new Error("No session provider configured in CompositeAuth");
return session.createSession(userId, metadata);
}
async validateSession(sessionId) {
for (const provider of this.providers) {
if (isSessionProvider(provider)) {
try {
const session = await provider.validateSession(sessionId);
if (session) return session;
} catch {
}
}
}
return null;
}
async destroySession(sessionId) {
const destroyPromises = [];
for (const provider of this.providers) {
if (isSessionProvider(provider)) {
destroyPromises.push(
provider.destroySession(sessionId).catch(() => {
})
);
}
}
await Promise.all(destroyPromises);
}
async refreshSession(sessionId) {
for (const provider of this.providers) {
if (isSessionProvider(provider)) {
try {
const session = await provider.refreshSession(sessionId);
if (session) return session;
} catch {
}
}
}
return null;
}
getSessionIdFromRequest(request) {
for (const provider of this.providers) {
if (isSessionProvider(provider)) {
try {
const sessionId = provider.getSessionIdFromRequest(request);
if (sessionId) return sessionId;
} catch {
}
}
}
return null;
}
getSessionHeaders(session) {
const sessionProvider = this.findProvider(isSessionProvider);
return sessionProvider?.getSessionHeaders(session) ?? {};
}
getClearSessionHeaders() {
const headers = {};
for (const provider of this.providers) {
if (isSessionProvider(provider)) {
try {
const providerHeaders = provider.getClearSessionHeaders();
Object.assign(headers, providerHeaders);
} catch {
}
}
}
return headers;
}
// ============================================================================
// IUserProvider Implementation
// Try each provider until one returns a user (like authenticateToken)
// ============================================================================
async getCurrentUser(request) {
for (const provider of this.providers) {
if (isUserProvider(provider)) {
try {
const user = await provider.getCurrentUser(request);
if (user) return user;
} catch {
}
}
}
return null;
}
async getUser(userId) {
for (const provider of this.providers) {
if (isUserProvider(provider)) {
try {
const user = await provider.getUser(userId);
if (user) return user;
} catch {
}
}
}
return null;
}
};
// src/server/base.ts
var MastraServerBase = class extends MastraBase {
#app;
constructor({ app, name }) {
super({ component: RegisteredLogger.SERVER, name: name ?? "Server" });
this.#app = app;
}
/**
* Get the app instance.
*
* Returns the server app that was passed to the constructor. This allows users
* to access the underlying server framework's app for direct operations
* like calling routes via app.fetch() (Hono) or using the app for testing.
*
* @template T - The expected type of the app (defaults to TApp)
* @returns The app instance cast to T. Callers are responsible for ensuring T matches the actual app type.
*
* @example
* ```typescript
* const app = adapter.getApp<Hono>();
* const response = await app.fetch(new Request('http://localhost/api/agents'));
* ```
*/
getApp() {
return this.#app;
}
/**
* Protected getter for subclasses to access the app.
* This allows subclasses to use `this.app` naturally.
*/
get app() {
return this.#app;
}
};
// src/server/simple-auth.ts
var DEFAULT_HEADERS = ["Authorization", "X-Playground-Access"];
var SimpleAuth = class extends MastraAuthProvider {
/**
* Marker to exempt SimpleAuth from EE license requirement.
* SimpleAuth is for development/testing and should work without a license.
*/
isSimpleAuth = true;
tokens;
headers;
users;
userById;
constructor(options) {
super(options);
this.tokens = options.tokens;
this.users = Object.values(this.tokens);
this.headers = [...DEFAULT_HEADERS].concat(options.headers || []);
this.userById = new Map(this.users.map((u) => [String(u?.id), u]));
}
async authenticateToken(token, request) {
const requestTokens = this.getTokensFromHeaders(token, request);
for (const requestToken of requestTokens) {
const tokenToUser = this.tokens[requestToken];
if (tokenToUser) {
return tokenToUser;
}
}
return this.getUserFromCookie(this.getRequestHeader(request, "Cookie"));
}
async authorizeUser(user, _request) {
return this.users.includes(user);
}
/** Get current user from request headers or cookie. */
async getCurrentUser(request) {
for (const headerName of this.headers) {
const headerValue = request.headers.get(headerName);
if (headerValue) {
const token = this.stripBearerPrefix(headerValue);
const user = this.tokens[token];
if (user) {
return user;
}
}
}
return this.getUserFromCookie(request.headers.get("Cookie"));
}
getUserFromCookie(cookieHeader) {
if (!cookieHeader) return null;
const cookies = cookieHeader.split(";").map((c) => c.trim());
for (const cookie of cookies) {
if (cookie.startsWith("mastra-token=")) {
const token = cookie.slice("mastra-token=".length);
const user = this.tokens[token];
if (user) {
return user;
}
}
}
return null;
}
/** Get user by ID. */
async getUser(userId) {
return this.userById.get(userId) ?? null;
}
/**
* Sign in with token (passed as password field).
* The email field is ignored - only the token matters.
*/
async signIn(_email, password, _request) {
const token = password;
const user = this.tokens[token];
if (!user) {
throw new Error("Invalid token");
}
const cookie = `mastra-token=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400`;
return {
user,
token,
cookies: [cookie]
};
}
async signUp() {
throw new Error("Sign up is not supported with SimpleAuth. Use pre-configured tokens.");
}
isSignUpEnabled() {
return false;
}
/**
* Get headers to clear the session cookie on logout.
* Partial ISessionProvider implementation for logout support.
*/
getClearSessionHeaders() {
return {
"Set-Cookie": "mastra-token=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0"
};
}
stripBearerPrefix(token) {
return token.startsWith("Bearer ") ? token.slice(7) : token;
}
/**
* Get a header value from either a HonoRequest or standard Request.
* The auth middleware passes a raw Request (c.req.raw), not a HonoRequest,
* so we need to handle both APIs.
*/
getRequestHeader(request, name) {
if (typeof request.header === "function") {
return request.header(name);
}
return request.headers?.get(name) ?? void 0;
}
getTokensFromHeaders(token, request) {
const tokens = [token];
for (const headerName of this.headers) {
const headerValue = this.getRequestHeader(request, headerName);
if (headerValue) {
tokens.push(this.stripBearerPrefix(headerValue));
}
}
return tokens;
}
};
// src/server/index.ts
function validateOptions(path, options) {
if (options.method === void 0) {
throw new MastraError({
id: "MASTRA_SERVER_API_INVALID_ROUTE_OPTIONS",
text: `Invalid options for route "${path}", missing "method" property`,
domain: "MASTRA_SERVER" /* MASTRA_SERVER */,
category: "USER" /* USER */
});
}
if (options.handler === void 0 && options.createHandler === void 0) {
throw new MastraError({
id: "MASTRA_SERVER_API_INVALID_ROUTE_OPTIONS",
text: `Invalid options for route "${path}", you must define a "handler" or "createHandler" property`,
domain: "MASTRA_SERVER" /* MASTRA_SERVER */,
category: "USER" /* USER */
});
}
if (options.handler !== void 0 && options.createHandler !== void 0) {
throw new MastraError({
id: "MASTRA_SERVER_API_INVALID_ROUTE_OPTIONS",
text: `Invalid options for route "${path}", you can only define one of the following properties: "handler" or "createHandler"`,
domain: "MASTRA_SERVER" /* MASTRA_SERVER */,
category: "USER" /* USER */
});
}
}
function registerApiRoute(path, options) {
validateOptions(path, options);
return {
path,
method: options.method,
handler: options.handler,
createHandler: options.createHandler,
openapi: options.openapi,
middleware: options.middleware,
cors: options.cors,
requiresAuth: options.requiresAuth,
requiresPermission: options.requiresPermission,
fga: options.fga
};
}
function defineAuth(config) {
return config;
}
export { CompositeAuth, MastraAuthProvider, MastraServerBase, SimpleAuth, defineAuth, registerApiRoute };
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map