@mastra/auth-studio 1.2.1-alpha.1 → 1.2.2-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 +22 -0
- package/dist/index.cjs +48 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -8
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @mastra/auth-studio
|
|
2
2
|
|
|
3
|
+
## 1.2.2-alpha.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. ([#15819](https://github.com/mastra-ai/mastra/pull/15819))
|
|
8
|
+
|
|
9
|
+
Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`28caa5b`](https://github.com/mastra-ai/mastra/commit/28caa5b032358545af2589ed90636eccb4dd9d2f), [`7d056b6`](https://github.com/mastra-ai/mastra/commit/7d056b6ecf603cacaa0f663ff1df025ed885b6c1), [`26f1f94`](https://github.com/mastra-ai/mastra/commit/26f1f9490574b864ba1ecedf2c9632e0767a23bd)]:
|
|
12
|
+
- @mastra/core@1.29.0-alpha.5
|
|
13
|
+
|
|
14
|
+
## 1.2.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Fix session refresh for studio-deployed instances. Sessions now properly refresh when expired, preventing users from being logged out every 5 minutes. ([#15024](https://github.com/mastra-ai/mastra/pull/15024))
|
|
19
|
+
|
|
20
|
+
- Fix Bearer token authentication to extract role from /auth/verify response. Previously, CLI tokens created via `mastra auth token create` would fail permission checks because the role was not being extracted, causing MastraRBACStudio to fall back to empty default permissions. ([#15075](https://github.com/mastra-ai/mastra/pull/15075))
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`f32b9e1`](https://github.com/mastra-ai/mastra/commit/f32b9e115a3c754d1c8cfa3f4256fba87b09cfb7), [`7d6f521`](https://github.com/mastra-ai/mastra/commit/7d6f52164d0cca099f0b07cb2bba334360f1c8ab), [`a50d220`](https://github.com/mastra-ai/mastra/commit/a50d220b01ecbc5644d489a3d446c3bd4ab30245), [`665477b`](https://github.com/mastra-ai/mastra/commit/665477bc104fd52cfef8e7610d7664781a70c220), [`4cc2755`](https://github.com/mastra-ai/mastra/commit/4cc2755a7194cb08720ff2ab4dffb4b4a5103dfd), [`ac7baf6`](https://github.com/mastra-ai/mastra/commit/ac7baf66ef1db15e03975ef4ebb02724f015a391), [`ed425d7`](https://github.com/mastra-ai/mastra/commit/ed425d78e7c66cbda8209fee910856f98c6c6b82), [`1371703`](https://github.com/mastra-ai/mastra/commit/1371703835080450ef3f9aea58059a95d0da2e5a), [`0df8321`](https://github.com/mastra-ai/mastra/commit/0df832196eeb2450ab77ce887e8553abdd44c5a6), [`98f8a8b`](https://github.com/mastra-ai/mastra/commit/98f8a8bdf5761b9982f3ad3acbe7f1cc3efa71f3), [`ba6f7e9`](https://github.com/mastra-ai/mastra/commit/ba6f7e9086d8281393f2acae60fda61de3bff1f9), [`7eb2596`](https://github.com/mastra-ai/mastra/commit/7eb25960d607e07468c9a10c5437abd2deaf1e9a), [`1805ddc`](https://github.com/mastra-ai/mastra/commit/1805ddc9c9b3b14b63749735a13c05a45af43a80), [`fff91cf`](https://github.com/mastra-ai/mastra/commit/fff91cf914de0e731578aacebffdeebef82f0440), [`61109b3`](https://github.com/mastra-ai/mastra/commit/61109b34feb0e38d54bee4b8ca83eb7345b1d557), [`33f1ead`](https://github.com/mastra-ai/mastra/commit/33f1eadfa19c86953f593478e5fa371093b33779)]:
|
|
23
|
+
- @mastra/core@1.23.0
|
|
24
|
+
|
|
3
25
|
## 1.2.1-alpha.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -52,7 +52,7 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
52
52
|
user = await this.verifyBearerToken(token);
|
|
53
53
|
}
|
|
54
54
|
if (!user) return null;
|
|
55
|
-
if (this.organizationId && user.
|
|
55
|
+
if (this.organizationId && !user.memberOrgIds?.includes(this.organizationId)) {
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
return user;
|
|
@@ -85,8 +85,16 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
85
85
|
return `${this.sharedApiUrl}/auth/login?${params.toString()}`;
|
|
86
86
|
}
|
|
87
87
|
async handleCallback(code, _state) {
|
|
88
|
+
this.logger.debug("SSO callback: validating sealed session via shared API", {
|
|
89
|
+
sharedApiUrl: this.sharedApiUrl,
|
|
90
|
+
codeLength: code?.length
|
|
91
|
+
});
|
|
88
92
|
const user = await this.verifySessionCookie(code);
|
|
89
93
|
if (!user) {
|
|
94
|
+
this.logger.error("SSO callback: session validation failed \u2014 verifySessionCookie returned null", {
|
|
95
|
+
sharedApiUrl: this.sharedApiUrl,
|
|
96
|
+
codeLength: code?.length
|
|
97
|
+
});
|
|
90
98
|
throw new Error("Session validation failed");
|
|
91
99
|
}
|
|
92
100
|
return {
|
|
@@ -172,11 +180,16 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
172
180
|
}
|
|
173
181
|
});
|
|
174
182
|
if (!res.ok) {
|
|
183
|
+
this.logger.warn("refreshSession: shared API refresh returned non-OK status", {
|
|
184
|
+
status: res.status,
|
|
185
|
+
url: `${this.sharedApiUrl}/auth/refresh`
|
|
186
|
+
});
|
|
175
187
|
return this.validateSession(sessionId);
|
|
176
188
|
}
|
|
177
189
|
const setCookie = res.headers.get("Set-Cookie");
|
|
178
190
|
const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;
|
|
179
191
|
if (!newSessionId) {
|
|
192
|
+
this.logger.warn("refreshSession: no Set-Cookie header in refresh response");
|
|
180
193
|
return this.validateSession(sessionId);
|
|
181
194
|
}
|
|
182
195
|
const user = await this.verifySessionCookie(newSessionId);
|
|
@@ -188,7 +201,11 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
188
201
|
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
189
202
|
createdAt: now
|
|
190
203
|
};
|
|
191
|
-
} catch {
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this.logger.error("refreshSession: fetch to shared API failed", {
|
|
206
|
+
url: `${this.sharedApiUrl}/auth/refresh`,
|
|
207
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
208
|
+
});
|
|
192
209
|
return this.validateSession(sessionId);
|
|
193
210
|
}
|
|
194
211
|
}
|
|
@@ -244,7 +261,14 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
244
261
|
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
245
262
|
}
|
|
246
263
|
});
|
|
247
|
-
if (!res.ok)
|
|
264
|
+
if (!res.ok) {
|
|
265
|
+
this.logger.warn("verifySessionCookie: shared API returned non-OK status", {
|
|
266
|
+
status: res.status,
|
|
267
|
+
statusText: res.statusText,
|
|
268
|
+
url: `${this.sharedApiUrl}/auth/me`
|
|
269
|
+
});
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
248
272
|
const data = await res.json();
|
|
249
273
|
return {
|
|
250
274
|
id: data.user.id,
|
|
@@ -253,9 +277,14 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
253
277
|
avatarUrl: data.user.profilePictureUrl,
|
|
254
278
|
organizationId: data.organizationId,
|
|
255
279
|
role: data.role,
|
|
256
|
-
permissions: data.permissions
|
|
280
|
+
permissions: data.permissions,
|
|
281
|
+
memberOrgIds: data.memberOrgIds
|
|
257
282
|
};
|
|
258
|
-
} catch {
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.logger.error("verifySessionCookie: fetch to shared API failed", {
|
|
285
|
+
url: `${this.sharedApiUrl}/auth/me`,
|
|
286
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
287
|
+
});
|
|
259
288
|
return null;
|
|
260
289
|
}
|
|
261
290
|
}
|
|
@@ -270,16 +299,27 @@ var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
|
270
299
|
Authorization: `Bearer ${token}`
|
|
271
300
|
}
|
|
272
301
|
});
|
|
273
|
-
if (!res.ok)
|
|
302
|
+
if (!res.ok) {
|
|
303
|
+
this.logger.warn("verifyBearerToken: shared API returned non-OK status", {
|
|
304
|
+
status: res.status,
|
|
305
|
+
url: `${this.sharedApiUrl}/auth/verify`
|
|
306
|
+
});
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
274
309
|
const data = await res.json();
|
|
275
310
|
return {
|
|
276
311
|
id: data.user.id,
|
|
277
312
|
email: data.user.email,
|
|
278
313
|
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
279
314
|
organizationId: data.organizationId,
|
|
280
|
-
role: data.role
|
|
315
|
+
role: data.role,
|
|
316
|
+
memberOrgIds: data.memberOrgIds
|
|
281
317
|
};
|
|
282
|
-
} catch {
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.logger.error("verifyBearerToken: fetch to shared API failed", {
|
|
320
|
+
url: `${this.sharedApiUrl}/auth/verify`,
|
|
321
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
322
|
+
});
|
|
283
323
|
return null;
|
|
284
324
|
}
|
|
285
325
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["MastraAuthProvider","resolvePermissionsFromMapping","matchesPermission"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ISSOProvider,\n ISessionProvider,\n IUserProvider,\n Session,\n SSOCallbackResult,\n SSOLoginConfig,\n} from '@mastra/core/auth';\nimport type { EEUser, IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\nimport { MastraAuthProvider } from '@mastra/core/server';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\n\nexport interface StudioUser extends EEUser {\n id: string;\n email?: string;\n name?: string;\n avatarUrl?: string;\n organizationId?: string;\n role?: string;\n permissions?: string[];\n}\n\nexport interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {\n /** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */\n sharedApiUrl?: string;\n /** Organization ID that owns this deployed instance. Users not in this org are rejected. */\n organizationId?: string;\n /**\n * Cookie domain for session cookies (e.g., '.example.com').\n * When set, cookies will include Secure and Domain attributes.\n * Defaults to auto-detecting from sharedApiUrl (uses '.mastra.ai' when sharedApiUrl contains '.mastra.ai').\n * Can also be set via MASTRA_COOKIE_DOMAIN environment variable.\n */\n cookieDomain?: string;\n}\n\nconst COOKIE_NAME = 'wos-session';\n\n/**\n * Auth provider for Mastra Studio deployed instances.\n *\n * Proxies all authentication through the shared API, keeping the\n * WorkOS API key safely in the shared API. Deployed instances only\n * need the shared API URL — no secrets required.\n *\n * The shared API's sealed session cookie (`wos-session`) is set with\n * `Domain=.mastra.ai` in production, so it's included in requests\n * to deployed instances and can be forwarded for validation.\n */\nexport class MastraAuthStudio\n extends MastraAuthProvider<StudioUser>\n implements ISSOProvider<StudioUser>, ISessionProvider<Session>, IUserProvider<StudioUser>\n{\n readonly isMastraCloudAuth = true;\n\n private sharedApiUrl: string;\n private organizationId: string | undefined;\n private useProductionCookies: boolean;\n private cookieDomain: string | undefined;\n\n constructor(options?: MastraAuthStudioOptions) {\n super({ name: 'mastra-studio', ...options });\n this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || 'http://localhost:3010/v1';\n this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;\n\n // Strip trailing slash\n if (this.sharedApiUrl.endsWith('/')) {\n this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);\n }\n\n // Cookie domain can be explicitly configured, read from env, or auto-detected from sharedApiUrl\n this.cookieDomain = options?.cookieDomain || process.env.MASTRA_COOKIE_DOMAIN;\n\n // Use production cookie settings (Secure + Domain) when:\n // 1. An explicit cookieDomain is configured, OR\n // 2. The shared API is on .mastra.ai (auto-detect default domain)\n // Use hostname-based detection to avoid false positives (e.g., api.mastra.ai.evil.com)\n let autoDetectMastraAi = false;\n try {\n const hostname = new URL(this.sharedApiUrl).hostname.toLowerCase();\n autoDetectMastraAi = hostname === 'mastra.ai' || hostname.endsWith('.mastra.ai');\n } catch {\n autoDetectMastraAi = false;\n }\n this.useProductionCookies = !!this.cookieDomain || autoDetectMastraAi;\n\n // If no explicit domain but we're on .mastra.ai, use the default domain\n if (!this.cookieDomain && autoDetectMastraAi) {\n this.cookieDomain = '.mastra.ai';\n }\n\n if (options) {\n this.registerOptions(options);\n }\n }\n\n // ---------------------------------------------------------------------------\n // MastraAuthProvider abstract methods\n // ---------------------------------------------------------------------------\n\n /**\n * Authenticate an incoming request by forwarding the sealed session cookie\n * to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.\n */\n async authenticateToken(token: string, request: any): Promise<StudioUser | null> {\n let user: StudioUser | null = null;\n\n // Try sealed session cookie first (browser flow)\n const cookieHeader = request?.headers?.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n user = await this.verifySessionCookie(sessionCookie);\n }\n\n // Fall back to Bearer token (CLI / API token flow)\n if (!user && token) {\n user = await this.verifyBearerToken(token);\n }\n\n if (!user) return null;\n\n // Org-scoping: if this instance belongs to a specific org, reject users not in that org\n if (this.organizationId && user.organizationId !== this.organizationId) {\n return null;\n }\n\n return user;\n }\n\n authorizeUser(user: StudioUser): boolean {\n return !!user?.id;\n }\n\n // ---------------------------------------------------------------------------\n // ISSOProvider\n // ---------------------------------------------------------------------------\n\n getLoginUrl(redirectUri: string, state: string): string {\n // Extract the post-login redirect from state (format: uuid|encodedPostLoginRedirect)\n let postLoginRedirect = '/';\n if (state) {\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));\n } catch {\n // ignore decode errors\n }\n }\n }\n\n const params = new URLSearchParams({\n product: 'deploy',\n redirect_uri: redirectUri,\n post_login_redirect: postLoginRedirect,\n // Force re-authentication so AuthKit always shows the account picker\n prompt: 'login',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n\n return `${this.sharedApiUrl}/auth/login?${params.toString()}`;\n }\n\n async handleCallback(code: string, _state: string): Promise<SSOCallbackResult<StudioUser>> {\n // The shared API already consumed the OAuth code and passes the sealed\n // session directly as the `code` parameter in the redirect to this callback.\n // Validate it to get user info.\n const user = await this.verifySessionCookie(code);\n if (!user) {\n throw new Error('Session validation failed');\n }\n\n // Omit `cookies` so the Mastra server fallback path calls\n // createSession() + getSessionHeaders() to build a cookie scoped to\n // the deployed instance's domain.\n return {\n user,\n tokens: {\n accessToken: code,\n },\n };\n }\n\n setCallbackCookieHeader(_cookieHeader: string | null): void {\n // No-op: we don't use PKCE cookies — the shared API handles the full OAuth flow\n }\n\n getLoginCookies(): string[] | undefined {\n // No PKCE cookies needed — shared API manages the OAuth state\n return undefined;\n }\n\n getLoginButtonConfig(): SSOLoginConfig {\n return {\n provider: 'mastra-studio',\n text: 'Sign in with Mastra',\n };\n }\n\n async getLogoutUrl(_redirectUri: string, request?: Request): Promise<string | null> {\n const cookieHeader = request?.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (!sessionCookie) return null;\n\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; logoutUrl?: string };\n return data.logoutUrl ?? null;\n }\n } catch {\n // Failed to get logout URL — return null\n }\n\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // ISessionProvider\n // ---------------------------------------------------------------------------\n\n async createSession(userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n return {\n id: (metadata?.accessToken as string) || crypto.randomUUID(),\n userId,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000), // 24 hours\n createdAt: now,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n const user = await this.verifySessionCookie(sessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: sessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n }\n\n async destroySession(sessionId: string): Promise<void> {\n try {\n await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n } catch {\n // Best effort\n }\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n try {\n // Call the shared API's /auth/refresh endpoint to get a fresh access token\n const res = await fetch(`${this.sharedApiUrl}/auth/refresh`, {\n method: 'GET',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n\n if (!res.ok) {\n // Refresh failed, fall back to validation (will likely also fail)\n return this.validateSession(sessionId);\n }\n\n // Parse the new sealed session from Set-Cookie header\n const setCookie = res.headers.get('Set-Cookie');\n const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;\n\n if (!newSessionId) {\n // No new cookie returned, fall back to validation with original\n return this.validateSession(sessionId);\n }\n\n // Verify the new session works and return it\n const user = await this.verifySessionCookie(newSessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: newSessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n } catch {\n // On error, fall back to validation\n return this.validateSession(sessionId);\n }\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n return parseCookie(cookieHeader, COOKIE_NAME);\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const parts = [`${COOKIE_NAME}=${session.id}`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=86400'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const parts = [`${COOKIE_NAME}=`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=0'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n // ---------------------------------------------------------------------------\n // IUserProvider\n // ---------------------------------------------------------------------------\n\n async getCurrentUser(request: Request): Promise<StudioUser | null> {\n const cookieHeader = request.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n return this.verifySessionCookie(sessionCookie);\n }\n\n // Try bearer token\n const authHeader = request.headers.get('Authorization');\n if (authHeader?.startsWith('Bearer ')) {\n return this.verifyBearerToken(authHeader.slice(7));\n }\n\n return null;\n }\n\n async getUser(_userId: string): Promise<StudioUser | null> {\n // Cannot look up users by ID — only validate sessions\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Forward a sealed session cookie to the shared API's /auth/me endpoint\n * to validate it and get user info.\n */\n private async verifySessionCookie(sessionCookie: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/me`, {\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n profilePictureUrl?: string;\n };\n organizationId: string;\n role?: string;\n permissions?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n avatarUrl: data.user.profilePictureUrl,\n organizationId: data.organizationId,\n role: data.role,\n permissions: data.permissions,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Forward a Bearer token to the shared API's /auth/verify endpoint\n * to validate it and get user info (used for CLI tokens).\n */\n private async verifyBearerToken(token: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n };\n organizationId: string;\n role?: string;\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n organizationId: data.organizationId,\n role: data.role,\n };\n } catch {\n return null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cookie helpers\n// ---------------------------------------------------------------------------\n\nfunction parseCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));\n return match?.[1] ?? null;\n}\n\n/**\n * Parse a cookie value from a Set-Cookie header.\n * Set-Cookie format: \"name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400\"\n */\nfunction parseCookieFromHeader(setCookieHeader: string, name: string): string | null {\n // Set-Cookie header starts with \"name=value\" followed by optional attributes\n const parts = setCookieHeader.split(';');\n if (parts.length === 0) return null;\n\n const [cookieName, ...valueParts] = parts[0]!.split('=');\n if (cookieName?.trim() !== name) return null;\n\n // Value could contain = characters, so rejoin\n return valueParts.join('=') || null;\n}\n\n// ---------------------------------------------------------------------------\n// MastraRBACStudio — role-based permission provider for Studio auth\n// ---------------------------------------------------------------------------\n\nexport interface MastraRBACStudioOptions {\n /**\n * Mapping from role names to permission arrays.\n *\n * @example\n * ```typescript\n * {\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * viewer: ['agents:read', 'workflows:read'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n}\n\n/**\n * RBAC provider for Mastra Studio authentication.\n *\n * Maps user roles (from the shared API's /auth/me endpoint) to Mastra permissions\n * using a configurable role mapping.\n */\nexport class MastraRBACStudio implements IRBACProvider<StudioUser> {\n private options: MastraRBACStudioOptions;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACStudioOptions) {\n this.options = options;\n }\n\n async getRoles(user: StudioUser): Promise<string[]> {\n return user.role ? [user.role] : [];\n }\n\n async hasRole(user: StudioUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: StudioUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n if (roles.length === 0) {\n return this.options.roleMapping['_default'] ?? [];\n }\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: StudioUser, permission: string): Promise<boolean> {\n const permissions = await this.getPermissions(user);\n return permissions.some(p => matchesPermission(p, permission));\n }\n\n async hasAllPermissions(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.every(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n\n async hasAnyPermission(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.some(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["MastraAuthProvider","resolvePermissionsFromMapping","matchesPermission"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n ISSOProvider,\n ISessionProvider,\n IUserProvider,\n Session,\n SSOCallbackResult,\n SSOLoginConfig,\n} from '@mastra/core/auth';\nimport type { EEUser, IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\nimport { MastraAuthProvider } from '@mastra/core/server';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\n\nexport interface StudioUser extends EEUser {\n id: string;\n email?: string;\n name?: string;\n avatarUrl?: string;\n organizationId?: string;\n role?: string;\n permissions?: string[];\n /** All organization IDs the user is a member of (for cross-org access checks) */\n memberOrgIds?: string[];\n}\n\nexport interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {\n /** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */\n sharedApiUrl?: string;\n /** Organization ID that owns this deployed instance. Users not in this org are rejected. */\n organizationId?: string;\n /**\n * Cookie domain for session cookies (e.g., '.example.com').\n * When set, cookies will include Secure and Domain attributes.\n * Defaults to auto-detecting from sharedApiUrl (uses '.mastra.ai' when sharedApiUrl contains '.mastra.ai').\n * Can also be set via MASTRA_COOKIE_DOMAIN environment variable.\n */\n cookieDomain?: string;\n}\n\nconst COOKIE_NAME = 'wos-session';\n\n/**\n * Auth provider for Mastra Studio deployed instances.\n *\n * Proxies all authentication through the shared API, keeping the\n * WorkOS API key safely in the shared API. Deployed instances only\n * need the shared API URL — no secrets required.\n *\n * The shared API's sealed session cookie (`wos-session`) is set with\n * `Domain=.mastra.ai` in production, so it's included in requests\n * to deployed instances and can be forwarded for validation.\n */\nexport class MastraAuthStudio\n extends MastraAuthProvider<StudioUser>\n implements ISSOProvider<StudioUser>, ISessionProvider<Session>, IUserProvider<StudioUser>\n{\n readonly isMastraCloudAuth = true;\n\n private sharedApiUrl: string;\n private organizationId: string | undefined;\n private useProductionCookies: boolean;\n private cookieDomain: string | undefined;\n\n constructor(options?: MastraAuthStudioOptions) {\n super({ name: 'mastra-studio', ...options });\n this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || 'http://localhost:3010/v1';\n this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;\n\n // Strip trailing slash\n if (this.sharedApiUrl.endsWith('/')) {\n this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);\n }\n\n // Cookie domain can be explicitly configured, read from env, or auto-detected from sharedApiUrl\n this.cookieDomain = options?.cookieDomain || process.env.MASTRA_COOKIE_DOMAIN;\n\n // Use production cookie settings (Secure + Domain) when:\n // 1. An explicit cookieDomain is configured, OR\n // 2. The shared API is on .mastra.ai (auto-detect default domain)\n // Use hostname-based detection to avoid false positives (e.g., api.mastra.ai.evil.com)\n let autoDetectMastraAi = false;\n try {\n const hostname = new URL(this.sharedApiUrl).hostname.toLowerCase();\n autoDetectMastraAi = hostname === 'mastra.ai' || hostname.endsWith('.mastra.ai');\n } catch {\n autoDetectMastraAi = false;\n }\n this.useProductionCookies = !!this.cookieDomain || autoDetectMastraAi;\n\n // If no explicit domain but we're on .mastra.ai, use the default domain\n if (!this.cookieDomain && autoDetectMastraAi) {\n this.cookieDomain = '.mastra.ai';\n }\n\n if (options) {\n this.registerOptions(options);\n }\n }\n\n // ---------------------------------------------------------------------------\n // MastraAuthProvider abstract methods\n // ---------------------------------------------------------------------------\n\n /**\n * Authenticate an incoming request by forwarding the sealed session cookie\n * to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.\n */\n async authenticateToken(token: string, request: any): Promise<StudioUser | null> {\n let user: StudioUser | null = null;\n\n // Try sealed session cookie first (browser flow)\n const cookieHeader = request?.headers?.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n user = await this.verifySessionCookie(sessionCookie);\n }\n\n // Fall back to Bearer token (CLI / API token flow)\n if (!user && token) {\n user = await this.verifyBearerToken(token);\n }\n\n if (!user) return null;\n\n // Org-scoping: if this instance belongs to a specific org, reject users not a member of that org\n // Check memberOrgIds (all orgs user belongs to) rather than organizationId (current org)\n if (this.organizationId && !user.memberOrgIds?.includes(this.organizationId)) {\n return null;\n }\n\n return user;\n }\n\n authorizeUser(user: StudioUser): boolean {\n return !!user?.id;\n }\n\n // ---------------------------------------------------------------------------\n // ISSOProvider\n // ---------------------------------------------------------------------------\n\n getLoginUrl(redirectUri: string, state: string): string {\n // Extract the post-login redirect from state (format: uuid|encodedPostLoginRedirect)\n let postLoginRedirect = '/';\n if (state) {\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));\n } catch {\n // ignore decode errors\n }\n }\n }\n\n const params = new URLSearchParams({\n product: 'deploy',\n redirect_uri: redirectUri,\n post_login_redirect: postLoginRedirect,\n // Force re-authentication so AuthKit always shows the account picker\n prompt: 'login',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n\n return `${this.sharedApiUrl}/auth/login?${params.toString()}`;\n }\n\n async handleCallback(code: string, _state: string): Promise<SSOCallbackResult<StudioUser>> {\n // The shared API already consumed the OAuth code and passes the sealed\n // session directly as the `code` parameter in the redirect to this callback.\n // Validate it to get user info.\n this.logger.debug('SSO callback: validating sealed session via shared API', {\n sharedApiUrl: this.sharedApiUrl,\n codeLength: code?.length,\n });\n const user = await this.verifySessionCookie(code);\n if (!user) {\n this.logger.error('SSO callback: session validation failed — verifySessionCookie returned null', {\n sharedApiUrl: this.sharedApiUrl,\n codeLength: code?.length,\n });\n throw new Error('Session validation failed');\n }\n\n // Omit `cookies` so the Mastra server fallback path calls\n // createSession() + getSessionHeaders() to build a cookie scoped to\n // the deployed instance's domain.\n return {\n user,\n tokens: {\n accessToken: code,\n },\n };\n }\n\n setCallbackCookieHeader(_cookieHeader: string | null): void {\n // No-op: we don't use PKCE cookies — the shared API handles the full OAuth flow\n }\n\n getLoginCookies(): string[] | undefined {\n // No PKCE cookies needed — shared API manages the OAuth state\n return undefined;\n }\n\n getLoginButtonConfig(): SSOLoginConfig {\n return {\n provider: 'mastra-studio',\n text: 'Sign in with Mastra',\n };\n }\n\n async getLogoutUrl(_redirectUri: string, request?: Request): Promise<string | null> {\n const cookieHeader = request?.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (!sessionCookie) return null;\n\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; logoutUrl?: string };\n return data.logoutUrl ?? null;\n }\n } catch {\n // Failed to get logout URL — return null\n }\n\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // ISessionProvider\n // ---------------------------------------------------------------------------\n\n async createSession(userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n return {\n id: (metadata?.accessToken as string) || crypto.randomUUID(),\n userId,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000), // 24 hours\n createdAt: now,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n const user = await this.verifySessionCookie(sessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: sessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n }\n\n async destroySession(sessionId: string): Promise<void> {\n try {\n await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n } catch {\n // Best effort\n }\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n try {\n // Call the shared API's /auth/refresh endpoint to get a fresh access token\n const res = await fetch(`${this.sharedApiUrl}/auth/refresh`, {\n method: 'GET',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('refreshSession: shared API refresh returned non-OK status', {\n status: res.status,\n url: `${this.sharedApiUrl}/auth/refresh`,\n });\n // Refresh failed, fall back to validation (will likely also fail)\n return this.validateSession(sessionId);\n }\n\n // Parse the new sealed session from Set-Cookie header\n const setCookie = res.headers.get('Set-Cookie');\n const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;\n\n if (!newSessionId) {\n this.logger.warn('refreshSession: no Set-Cookie header in refresh response');\n // No new cookie returned, fall back to validation with original\n return this.validateSession(sessionId);\n }\n\n // Verify the new session works and return it\n const user = await this.verifySessionCookie(newSessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: newSessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n } catch (error) {\n this.logger.error('refreshSession: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/refresh`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n // On error, fall back to validation\n return this.validateSession(sessionId);\n }\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n return parseCookie(cookieHeader, COOKIE_NAME);\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const parts = [`${COOKIE_NAME}=${session.id}`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=86400'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const parts = [`${COOKIE_NAME}=`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=0'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n // ---------------------------------------------------------------------------\n // IUserProvider\n // ---------------------------------------------------------------------------\n\n async getCurrentUser(request: Request): Promise<StudioUser | null> {\n const cookieHeader = request.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n return this.verifySessionCookie(sessionCookie);\n }\n\n // Try bearer token\n const authHeader = request.headers.get('Authorization');\n if (authHeader?.startsWith('Bearer ')) {\n return this.verifyBearerToken(authHeader.slice(7));\n }\n\n return null;\n }\n\n async getUser(_userId: string): Promise<StudioUser | null> {\n // Cannot look up users by ID — only validate sessions\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Forward a sealed session cookie to the shared API's /auth/me endpoint\n * to validate it and get user info.\n */\n private async verifySessionCookie(sessionCookie: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/me`, {\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('verifySessionCookie: shared API returned non-OK status', {\n status: res.status,\n statusText: res.statusText,\n url: `${this.sharedApiUrl}/auth/me`,\n });\n return null;\n }\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n profilePictureUrl?: string;\n };\n organizationId: string;\n role?: string;\n permissions?: string[];\n memberOrgIds?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n avatarUrl: data.user.profilePictureUrl,\n organizationId: data.organizationId,\n role: data.role,\n permissions: data.permissions,\n memberOrgIds: data.memberOrgIds,\n };\n } catch (error) {\n this.logger.error('verifySessionCookie: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/me`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n return null;\n }\n }\n\n /**\n * Forward a Bearer token to the shared API's /auth/verify endpoint\n * to validate it and get user info (used for CLI tokens).\n */\n private async verifyBearerToken(token: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('verifyBearerToken: shared API returned non-OK status', {\n status: res.status,\n url: `${this.sharedApiUrl}/auth/verify`,\n });\n return null;\n }\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n };\n organizationId: string;\n role?: string;\n memberOrgIds?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n organizationId: data.organizationId,\n role: data.role,\n memberOrgIds: data.memberOrgIds,\n };\n } catch (error) {\n this.logger.error('verifyBearerToken: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/verify`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n return null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cookie helpers\n// ---------------------------------------------------------------------------\n\nfunction parseCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));\n return match?.[1] ?? null;\n}\n\n/**\n * Parse a cookie value from a Set-Cookie header.\n * Set-Cookie format: \"name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400\"\n */\nfunction parseCookieFromHeader(setCookieHeader: string, name: string): string | null {\n // Set-Cookie header starts with \"name=value\" followed by optional attributes\n const parts = setCookieHeader.split(';');\n if (parts.length === 0) return null;\n\n const [cookieName, ...valueParts] = parts[0]!.split('=');\n if (cookieName?.trim() !== name) return null;\n\n // Value could contain = characters, so rejoin\n return valueParts.join('=') || null;\n}\n\n// ---------------------------------------------------------------------------\n// MastraRBACStudio — role-based permission provider for Studio auth\n// ---------------------------------------------------------------------------\n\nexport interface MastraRBACStudioOptions {\n /**\n * Mapping from role names to permission arrays.\n *\n * @example\n * ```typescript\n * {\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * viewer: ['agents:read', 'workflows:read'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n}\n\n/**\n * RBAC provider for Mastra Studio authentication.\n *\n * Maps user roles (from the shared API's /auth/me endpoint) to Mastra permissions\n * using a configurable role mapping.\n */\nexport class MastraRBACStudio implements IRBACProvider<StudioUser> {\n private options: MastraRBACStudioOptions;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACStudioOptions) {\n this.options = options;\n }\n\n async getRoles(user: StudioUser): Promise<string[]> {\n return user.role ? [user.role] : [];\n }\n\n async hasRole(user: StudioUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: StudioUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n if (roles.length === 0) {\n return this.options.roleMapping['_default'] ?? [];\n }\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: StudioUser, permission: string): Promise<boolean> {\n const permissions = await this.getPermissions(user);\n return permissions.some(p => matchesPermission(p, permission));\n }\n\n async hasAllPermissions(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.every(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n\n async hasAnyPermission(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.some(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface StudioUser extends EEUser {
|
|
|
10
10
|
organizationId?: string;
|
|
11
11
|
role?: string;
|
|
12
12
|
permissions?: string[];
|
|
13
|
+
/** All organization IDs the user is a member of (for cross-org access checks) */
|
|
14
|
+
memberOrgIds?: string[];
|
|
13
15
|
}
|
|
14
16
|
export interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {
|
|
15
17
|
/** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
50
50
|
user = await this.verifyBearerToken(token);
|
|
51
51
|
}
|
|
52
52
|
if (!user) return null;
|
|
53
|
-
if (this.organizationId && user.
|
|
53
|
+
if (this.organizationId && !user.memberOrgIds?.includes(this.organizationId)) {
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
return user;
|
|
@@ -83,8 +83,16 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
83
83
|
return `${this.sharedApiUrl}/auth/login?${params.toString()}`;
|
|
84
84
|
}
|
|
85
85
|
async handleCallback(code, _state) {
|
|
86
|
+
this.logger.debug("SSO callback: validating sealed session via shared API", {
|
|
87
|
+
sharedApiUrl: this.sharedApiUrl,
|
|
88
|
+
codeLength: code?.length
|
|
89
|
+
});
|
|
86
90
|
const user = await this.verifySessionCookie(code);
|
|
87
91
|
if (!user) {
|
|
92
|
+
this.logger.error("SSO callback: session validation failed \u2014 verifySessionCookie returned null", {
|
|
93
|
+
sharedApiUrl: this.sharedApiUrl,
|
|
94
|
+
codeLength: code?.length
|
|
95
|
+
});
|
|
88
96
|
throw new Error("Session validation failed");
|
|
89
97
|
}
|
|
90
98
|
return {
|
|
@@ -170,11 +178,16 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
170
178
|
}
|
|
171
179
|
});
|
|
172
180
|
if (!res.ok) {
|
|
181
|
+
this.logger.warn("refreshSession: shared API refresh returned non-OK status", {
|
|
182
|
+
status: res.status,
|
|
183
|
+
url: `${this.sharedApiUrl}/auth/refresh`
|
|
184
|
+
});
|
|
173
185
|
return this.validateSession(sessionId);
|
|
174
186
|
}
|
|
175
187
|
const setCookie = res.headers.get("Set-Cookie");
|
|
176
188
|
const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;
|
|
177
189
|
if (!newSessionId) {
|
|
190
|
+
this.logger.warn("refreshSession: no Set-Cookie header in refresh response");
|
|
178
191
|
return this.validateSession(sessionId);
|
|
179
192
|
}
|
|
180
193
|
const user = await this.verifySessionCookie(newSessionId);
|
|
@@ -186,7 +199,11 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
186
199
|
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
187
200
|
createdAt: now
|
|
188
201
|
};
|
|
189
|
-
} catch {
|
|
202
|
+
} catch (error) {
|
|
203
|
+
this.logger.error("refreshSession: fetch to shared API failed", {
|
|
204
|
+
url: `${this.sharedApiUrl}/auth/refresh`,
|
|
205
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
206
|
+
});
|
|
190
207
|
return this.validateSession(sessionId);
|
|
191
208
|
}
|
|
192
209
|
}
|
|
@@ -242,7 +259,14 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
242
259
|
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
243
260
|
}
|
|
244
261
|
});
|
|
245
|
-
if (!res.ok)
|
|
262
|
+
if (!res.ok) {
|
|
263
|
+
this.logger.warn("verifySessionCookie: shared API returned non-OK status", {
|
|
264
|
+
status: res.status,
|
|
265
|
+
statusText: res.statusText,
|
|
266
|
+
url: `${this.sharedApiUrl}/auth/me`
|
|
267
|
+
});
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
246
270
|
const data = await res.json();
|
|
247
271
|
return {
|
|
248
272
|
id: data.user.id,
|
|
@@ -251,9 +275,14 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
251
275
|
avatarUrl: data.user.profilePictureUrl,
|
|
252
276
|
organizationId: data.organizationId,
|
|
253
277
|
role: data.role,
|
|
254
|
-
permissions: data.permissions
|
|
278
|
+
permissions: data.permissions,
|
|
279
|
+
memberOrgIds: data.memberOrgIds
|
|
255
280
|
};
|
|
256
|
-
} catch {
|
|
281
|
+
} catch (error) {
|
|
282
|
+
this.logger.error("verifySessionCookie: fetch to shared API failed", {
|
|
283
|
+
url: `${this.sharedApiUrl}/auth/me`,
|
|
284
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
285
|
+
});
|
|
257
286
|
return null;
|
|
258
287
|
}
|
|
259
288
|
}
|
|
@@ -268,16 +297,27 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
268
297
|
Authorization: `Bearer ${token}`
|
|
269
298
|
}
|
|
270
299
|
});
|
|
271
|
-
if (!res.ok)
|
|
300
|
+
if (!res.ok) {
|
|
301
|
+
this.logger.warn("verifyBearerToken: shared API returned non-OK status", {
|
|
302
|
+
status: res.status,
|
|
303
|
+
url: `${this.sharedApiUrl}/auth/verify`
|
|
304
|
+
});
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
272
307
|
const data = await res.json();
|
|
273
308
|
return {
|
|
274
309
|
id: data.user.id,
|
|
275
310
|
email: data.user.email,
|
|
276
311
|
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
277
312
|
organizationId: data.organizationId,
|
|
278
|
-
role: data.role
|
|
313
|
+
role: data.role,
|
|
314
|
+
memberOrgIds: data.memberOrgIds
|
|
279
315
|
};
|
|
280
|
-
} catch {
|
|
316
|
+
} catch (error) {
|
|
317
|
+
this.logger.error("verifyBearerToken: fetch to shared API failed", {
|
|
318
|
+
url: `${this.sharedApiUrl}/auth/verify`,
|
|
319
|
+
error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error)
|
|
320
|
+
});
|
|
281
321
|
return null;
|
|
282
322
|
}
|
|
283
323
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ISSOProvider,\n ISessionProvider,\n IUserProvider,\n Session,\n SSOCallbackResult,\n SSOLoginConfig,\n} from '@mastra/core/auth';\nimport type { EEUser, IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\nimport { MastraAuthProvider } from '@mastra/core/server';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\n\nexport interface StudioUser extends EEUser {\n id: string;\n email?: string;\n name?: string;\n avatarUrl?: string;\n organizationId?: string;\n role?: string;\n permissions?: string[];\n}\n\nexport interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {\n /** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */\n sharedApiUrl?: string;\n /** Organization ID that owns this deployed instance. Users not in this org are rejected. */\n organizationId?: string;\n /**\n * Cookie domain for session cookies (e.g., '.example.com').\n * When set, cookies will include Secure and Domain attributes.\n * Defaults to auto-detecting from sharedApiUrl (uses '.mastra.ai' when sharedApiUrl contains '.mastra.ai').\n * Can also be set via MASTRA_COOKIE_DOMAIN environment variable.\n */\n cookieDomain?: string;\n}\n\nconst COOKIE_NAME = 'wos-session';\n\n/**\n * Auth provider for Mastra Studio deployed instances.\n *\n * Proxies all authentication through the shared API, keeping the\n * WorkOS API key safely in the shared API. Deployed instances only\n * need the shared API URL — no secrets required.\n *\n * The shared API's sealed session cookie (`wos-session`) is set with\n * `Domain=.mastra.ai` in production, so it's included in requests\n * to deployed instances and can be forwarded for validation.\n */\nexport class MastraAuthStudio\n extends MastraAuthProvider<StudioUser>\n implements ISSOProvider<StudioUser>, ISessionProvider<Session>, IUserProvider<StudioUser>\n{\n readonly isMastraCloudAuth = true;\n\n private sharedApiUrl: string;\n private organizationId: string | undefined;\n private useProductionCookies: boolean;\n private cookieDomain: string | undefined;\n\n constructor(options?: MastraAuthStudioOptions) {\n super({ name: 'mastra-studio', ...options });\n this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || 'http://localhost:3010/v1';\n this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;\n\n // Strip trailing slash\n if (this.sharedApiUrl.endsWith('/')) {\n this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);\n }\n\n // Cookie domain can be explicitly configured, read from env, or auto-detected from sharedApiUrl\n this.cookieDomain = options?.cookieDomain || process.env.MASTRA_COOKIE_DOMAIN;\n\n // Use production cookie settings (Secure + Domain) when:\n // 1. An explicit cookieDomain is configured, OR\n // 2. The shared API is on .mastra.ai (auto-detect default domain)\n // Use hostname-based detection to avoid false positives (e.g., api.mastra.ai.evil.com)\n let autoDetectMastraAi = false;\n try {\n const hostname = new URL(this.sharedApiUrl).hostname.toLowerCase();\n autoDetectMastraAi = hostname === 'mastra.ai' || hostname.endsWith('.mastra.ai');\n } catch {\n autoDetectMastraAi = false;\n }\n this.useProductionCookies = !!this.cookieDomain || autoDetectMastraAi;\n\n // If no explicit domain but we're on .mastra.ai, use the default domain\n if (!this.cookieDomain && autoDetectMastraAi) {\n this.cookieDomain = '.mastra.ai';\n }\n\n if (options) {\n this.registerOptions(options);\n }\n }\n\n // ---------------------------------------------------------------------------\n // MastraAuthProvider abstract methods\n // ---------------------------------------------------------------------------\n\n /**\n * Authenticate an incoming request by forwarding the sealed session cookie\n * to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.\n */\n async authenticateToken(token: string, request: any): Promise<StudioUser | null> {\n let user: StudioUser | null = null;\n\n // Try sealed session cookie first (browser flow)\n const cookieHeader = request?.headers?.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n user = await this.verifySessionCookie(sessionCookie);\n }\n\n // Fall back to Bearer token (CLI / API token flow)\n if (!user && token) {\n user = await this.verifyBearerToken(token);\n }\n\n if (!user) return null;\n\n // Org-scoping: if this instance belongs to a specific org, reject users not in that org\n if (this.organizationId && user.organizationId !== this.organizationId) {\n return null;\n }\n\n return user;\n }\n\n authorizeUser(user: StudioUser): boolean {\n return !!user?.id;\n }\n\n // ---------------------------------------------------------------------------\n // ISSOProvider\n // ---------------------------------------------------------------------------\n\n getLoginUrl(redirectUri: string, state: string): string {\n // Extract the post-login redirect from state (format: uuid|encodedPostLoginRedirect)\n let postLoginRedirect = '/';\n if (state) {\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));\n } catch {\n // ignore decode errors\n }\n }\n }\n\n const params = new URLSearchParams({\n product: 'deploy',\n redirect_uri: redirectUri,\n post_login_redirect: postLoginRedirect,\n // Force re-authentication so AuthKit always shows the account picker\n prompt: 'login',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n\n return `${this.sharedApiUrl}/auth/login?${params.toString()}`;\n }\n\n async handleCallback(code: string, _state: string): Promise<SSOCallbackResult<StudioUser>> {\n // The shared API already consumed the OAuth code and passes the sealed\n // session directly as the `code` parameter in the redirect to this callback.\n // Validate it to get user info.\n const user = await this.verifySessionCookie(code);\n if (!user) {\n throw new Error('Session validation failed');\n }\n\n // Omit `cookies` so the Mastra server fallback path calls\n // createSession() + getSessionHeaders() to build a cookie scoped to\n // the deployed instance's domain.\n return {\n user,\n tokens: {\n accessToken: code,\n },\n };\n }\n\n setCallbackCookieHeader(_cookieHeader: string | null): void {\n // No-op: we don't use PKCE cookies — the shared API handles the full OAuth flow\n }\n\n getLoginCookies(): string[] | undefined {\n // No PKCE cookies needed — shared API manages the OAuth state\n return undefined;\n }\n\n getLoginButtonConfig(): SSOLoginConfig {\n return {\n provider: 'mastra-studio',\n text: 'Sign in with Mastra',\n };\n }\n\n async getLogoutUrl(_redirectUri: string, request?: Request): Promise<string | null> {\n const cookieHeader = request?.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (!sessionCookie) return null;\n\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; logoutUrl?: string };\n return data.logoutUrl ?? null;\n }\n } catch {\n // Failed to get logout URL — return null\n }\n\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // ISessionProvider\n // ---------------------------------------------------------------------------\n\n async createSession(userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n return {\n id: (metadata?.accessToken as string) || crypto.randomUUID(),\n userId,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000), // 24 hours\n createdAt: now,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n const user = await this.verifySessionCookie(sessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: sessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n }\n\n async destroySession(sessionId: string): Promise<void> {\n try {\n await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n } catch {\n // Best effort\n }\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n try {\n // Call the shared API's /auth/refresh endpoint to get a fresh access token\n const res = await fetch(`${this.sharedApiUrl}/auth/refresh`, {\n method: 'GET',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n\n if (!res.ok) {\n // Refresh failed, fall back to validation (will likely also fail)\n return this.validateSession(sessionId);\n }\n\n // Parse the new sealed session from Set-Cookie header\n const setCookie = res.headers.get('Set-Cookie');\n const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;\n\n if (!newSessionId) {\n // No new cookie returned, fall back to validation with original\n return this.validateSession(sessionId);\n }\n\n // Verify the new session works and return it\n const user = await this.verifySessionCookie(newSessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: newSessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n } catch {\n // On error, fall back to validation\n return this.validateSession(sessionId);\n }\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n return parseCookie(cookieHeader, COOKIE_NAME);\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const parts = [`${COOKIE_NAME}=${session.id}`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=86400'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const parts = [`${COOKIE_NAME}=`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=0'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n // ---------------------------------------------------------------------------\n // IUserProvider\n // ---------------------------------------------------------------------------\n\n async getCurrentUser(request: Request): Promise<StudioUser | null> {\n const cookieHeader = request.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n return this.verifySessionCookie(sessionCookie);\n }\n\n // Try bearer token\n const authHeader = request.headers.get('Authorization');\n if (authHeader?.startsWith('Bearer ')) {\n return this.verifyBearerToken(authHeader.slice(7));\n }\n\n return null;\n }\n\n async getUser(_userId: string): Promise<StudioUser | null> {\n // Cannot look up users by ID — only validate sessions\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Forward a sealed session cookie to the shared API's /auth/me endpoint\n * to validate it and get user info.\n */\n private async verifySessionCookie(sessionCookie: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/me`, {\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n profilePictureUrl?: string;\n };\n organizationId: string;\n role?: string;\n permissions?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n avatarUrl: data.user.profilePictureUrl,\n organizationId: data.organizationId,\n role: data.role,\n permissions: data.permissions,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Forward a Bearer token to the shared API's /auth/verify endpoint\n * to validate it and get user info (used for CLI tokens).\n */\n private async verifyBearerToken(token: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n };\n organizationId: string;\n role?: string;\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n organizationId: data.organizationId,\n role: data.role,\n };\n } catch {\n return null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cookie helpers\n// ---------------------------------------------------------------------------\n\nfunction parseCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));\n return match?.[1] ?? null;\n}\n\n/**\n * Parse a cookie value from a Set-Cookie header.\n * Set-Cookie format: \"name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400\"\n */\nfunction parseCookieFromHeader(setCookieHeader: string, name: string): string | null {\n // Set-Cookie header starts with \"name=value\" followed by optional attributes\n const parts = setCookieHeader.split(';');\n if (parts.length === 0) return null;\n\n const [cookieName, ...valueParts] = parts[0]!.split('=');\n if (cookieName?.trim() !== name) return null;\n\n // Value could contain = characters, so rejoin\n return valueParts.join('=') || null;\n}\n\n// ---------------------------------------------------------------------------\n// MastraRBACStudio — role-based permission provider for Studio auth\n// ---------------------------------------------------------------------------\n\nexport interface MastraRBACStudioOptions {\n /**\n * Mapping from role names to permission arrays.\n *\n * @example\n * ```typescript\n * {\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * viewer: ['agents:read', 'workflows:read'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n}\n\n/**\n * RBAC provider for Mastra Studio authentication.\n *\n * Maps user roles (from the shared API's /auth/me endpoint) to Mastra permissions\n * using a configurable role mapping.\n */\nexport class MastraRBACStudio implements IRBACProvider<StudioUser> {\n private options: MastraRBACStudioOptions;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACStudioOptions) {\n this.options = options;\n }\n\n async getRoles(user: StudioUser): Promise<string[]> {\n return user.role ? [user.role] : [];\n }\n\n async hasRole(user: StudioUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: StudioUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n if (roles.length === 0) {\n return this.options.roleMapping['_default'] ?? [];\n }\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: StudioUser, permission: string): Promise<boolean> {\n const permissions = await this.getPermissions(user);\n return permissions.some(p => matchesPermission(p, permission));\n }\n\n async hasAllPermissions(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.every(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n\n async hasAnyPermission(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.some(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ISSOProvider,\n ISessionProvider,\n IUserProvider,\n Session,\n SSOCallbackResult,\n SSOLoginConfig,\n} from '@mastra/core/auth';\nimport type { EEUser, IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';\nimport { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';\nimport { MastraAuthProvider } from '@mastra/core/server';\nimport type { MastraAuthProviderOptions } from '@mastra/core/server';\n\nexport interface StudioUser extends EEUser {\n id: string;\n email?: string;\n name?: string;\n avatarUrl?: string;\n organizationId?: string;\n role?: string;\n permissions?: string[];\n /** All organization IDs the user is a member of (for cross-org access checks) */\n memberOrgIds?: string[];\n}\n\nexport interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {\n /** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */\n sharedApiUrl?: string;\n /** Organization ID that owns this deployed instance. Users not in this org are rejected. */\n organizationId?: string;\n /**\n * Cookie domain for session cookies (e.g., '.example.com').\n * When set, cookies will include Secure and Domain attributes.\n * Defaults to auto-detecting from sharedApiUrl (uses '.mastra.ai' when sharedApiUrl contains '.mastra.ai').\n * Can also be set via MASTRA_COOKIE_DOMAIN environment variable.\n */\n cookieDomain?: string;\n}\n\nconst COOKIE_NAME = 'wos-session';\n\n/**\n * Auth provider for Mastra Studio deployed instances.\n *\n * Proxies all authentication through the shared API, keeping the\n * WorkOS API key safely in the shared API. Deployed instances only\n * need the shared API URL — no secrets required.\n *\n * The shared API's sealed session cookie (`wos-session`) is set with\n * `Domain=.mastra.ai` in production, so it's included in requests\n * to deployed instances and can be forwarded for validation.\n */\nexport class MastraAuthStudio\n extends MastraAuthProvider<StudioUser>\n implements ISSOProvider<StudioUser>, ISessionProvider<Session>, IUserProvider<StudioUser>\n{\n readonly isMastraCloudAuth = true;\n\n private sharedApiUrl: string;\n private organizationId: string | undefined;\n private useProductionCookies: boolean;\n private cookieDomain: string | undefined;\n\n constructor(options?: MastraAuthStudioOptions) {\n super({ name: 'mastra-studio', ...options });\n this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || 'http://localhost:3010/v1';\n this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;\n\n // Strip trailing slash\n if (this.sharedApiUrl.endsWith('/')) {\n this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);\n }\n\n // Cookie domain can be explicitly configured, read from env, or auto-detected from sharedApiUrl\n this.cookieDomain = options?.cookieDomain || process.env.MASTRA_COOKIE_DOMAIN;\n\n // Use production cookie settings (Secure + Domain) when:\n // 1. An explicit cookieDomain is configured, OR\n // 2. The shared API is on .mastra.ai (auto-detect default domain)\n // Use hostname-based detection to avoid false positives (e.g., api.mastra.ai.evil.com)\n let autoDetectMastraAi = false;\n try {\n const hostname = new URL(this.sharedApiUrl).hostname.toLowerCase();\n autoDetectMastraAi = hostname === 'mastra.ai' || hostname.endsWith('.mastra.ai');\n } catch {\n autoDetectMastraAi = false;\n }\n this.useProductionCookies = !!this.cookieDomain || autoDetectMastraAi;\n\n // If no explicit domain but we're on .mastra.ai, use the default domain\n if (!this.cookieDomain && autoDetectMastraAi) {\n this.cookieDomain = '.mastra.ai';\n }\n\n if (options) {\n this.registerOptions(options);\n }\n }\n\n // ---------------------------------------------------------------------------\n // MastraAuthProvider abstract methods\n // ---------------------------------------------------------------------------\n\n /**\n * Authenticate an incoming request by forwarding the sealed session cookie\n * to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.\n */\n async authenticateToken(token: string, request: any): Promise<StudioUser | null> {\n let user: StudioUser | null = null;\n\n // Try sealed session cookie first (browser flow)\n const cookieHeader = request?.headers?.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n user = await this.verifySessionCookie(sessionCookie);\n }\n\n // Fall back to Bearer token (CLI / API token flow)\n if (!user && token) {\n user = await this.verifyBearerToken(token);\n }\n\n if (!user) return null;\n\n // Org-scoping: if this instance belongs to a specific org, reject users not a member of that org\n // Check memberOrgIds (all orgs user belongs to) rather than organizationId (current org)\n if (this.organizationId && !user.memberOrgIds?.includes(this.organizationId)) {\n return null;\n }\n\n return user;\n }\n\n authorizeUser(user: StudioUser): boolean {\n return !!user?.id;\n }\n\n // ---------------------------------------------------------------------------\n // ISSOProvider\n // ---------------------------------------------------------------------------\n\n getLoginUrl(redirectUri: string, state: string): string {\n // Extract the post-login redirect from state (format: uuid|encodedPostLoginRedirect)\n let postLoginRedirect = '/';\n if (state) {\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));\n } catch {\n // ignore decode errors\n }\n }\n }\n\n const params = new URLSearchParams({\n product: 'deploy',\n redirect_uri: redirectUri,\n post_login_redirect: postLoginRedirect,\n // Force re-authentication so AuthKit always shows the account picker\n prompt: 'login',\n ...(this.organizationId ? { organization_id: this.organizationId } : {}),\n });\n\n return `${this.sharedApiUrl}/auth/login?${params.toString()}`;\n }\n\n async handleCallback(code: string, _state: string): Promise<SSOCallbackResult<StudioUser>> {\n // The shared API already consumed the OAuth code and passes the sealed\n // session directly as the `code` parameter in the redirect to this callback.\n // Validate it to get user info.\n this.logger.debug('SSO callback: validating sealed session via shared API', {\n sharedApiUrl: this.sharedApiUrl,\n codeLength: code?.length,\n });\n const user = await this.verifySessionCookie(code);\n if (!user) {\n this.logger.error('SSO callback: session validation failed — verifySessionCookie returned null', {\n sharedApiUrl: this.sharedApiUrl,\n codeLength: code?.length,\n });\n throw new Error('Session validation failed');\n }\n\n // Omit `cookies` so the Mastra server fallback path calls\n // createSession() + getSessionHeaders() to build a cookie scoped to\n // the deployed instance's domain.\n return {\n user,\n tokens: {\n accessToken: code,\n },\n };\n }\n\n setCallbackCookieHeader(_cookieHeader: string | null): void {\n // No-op: we don't use PKCE cookies — the shared API handles the full OAuth flow\n }\n\n getLoginCookies(): string[] | undefined {\n // No PKCE cookies needed — shared API manages the OAuth state\n return undefined;\n }\n\n getLoginButtonConfig(): SSOLoginConfig {\n return {\n provider: 'mastra-studio',\n text: 'Sign in with Mastra',\n };\n }\n\n async getLogoutUrl(_redirectUri: string, request?: Request): Promise<string | null> {\n const cookieHeader = request?.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (!sessionCookie) return null;\n\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; logoutUrl?: string };\n return data.logoutUrl ?? null;\n }\n } catch {\n // Failed to get logout URL — return null\n }\n\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // ISessionProvider\n // ---------------------------------------------------------------------------\n\n async createSession(userId: string, metadata?: Record<string, unknown>): Promise<Session> {\n const now = new Date();\n return {\n id: (metadata?.accessToken as string) || crypto.randomUUID(),\n userId,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000), // 24 hours\n createdAt: now,\n metadata,\n };\n }\n\n async validateSession(sessionId: string): Promise<Session | null> {\n const user = await this.verifySessionCookie(sessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: sessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n }\n\n async destroySession(sessionId: string): Promise<void> {\n try {\n await fetch(`${this.sharedApiUrl}/auth/logout`, {\n method: 'POST',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n } catch {\n // Best effort\n }\n }\n\n async refreshSession(sessionId: string): Promise<Session | null> {\n try {\n // Call the shared API's /auth/refresh endpoint to get a fresh access token\n const res = await fetch(`${this.sharedApiUrl}/auth/refresh`, {\n method: 'GET',\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionId}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('refreshSession: shared API refresh returned non-OK status', {\n status: res.status,\n url: `${this.sharedApiUrl}/auth/refresh`,\n });\n // Refresh failed, fall back to validation (will likely also fail)\n return this.validateSession(sessionId);\n }\n\n // Parse the new sealed session from Set-Cookie header\n const setCookie = res.headers.get('Set-Cookie');\n const newSessionId = setCookie ? parseCookieFromHeader(setCookie, COOKIE_NAME) : null;\n\n if (!newSessionId) {\n this.logger.warn('refreshSession: no Set-Cookie header in refresh response');\n // No new cookie returned, fall back to validation with original\n return this.validateSession(sessionId);\n }\n\n // Verify the new session works and return it\n const user = await this.verifySessionCookie(newSessionId);\n if (!user) return null;\n\n const now = new Date();\n return {\n id: newSessionId,\n userId: user.id,\n expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1000),\n createdAt: now,\n };\n } catch (error) {\n this.logger.error('refreshSession: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/refresh`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n // On error, fall back to validation\n return this.validateSession(sessionId);\n }\n }\n\n getSessionIdFromRequest(request: Request): string | null {\n const cookieHeader = request.headers.get('Cookie');\n return parseCookie(cookieHeader, COOKIE_NAME);\n }\n\n getSessionHeaders(session: Session): Record<string, string> {\n const parts = [`${COOKIE_NAME}=${session.id}`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=86400'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n getClearSessionHeaders(): Record<string, string> {\n const parts = [`${COOKIE_NAME}=`, 'HttpOnly', 'SameSite=Lax', 'Path=/', 'Max-Age=0'];\n if (this.useProductionCookies && this.cookieDomain) {\n parts.push('Secure');\n parts.push(`Domain=${this.cookieDomain}`);\n }\n return { 'Set-Cookie': parts.join('; ') };\n }\n\n // ---------------------------------------------------------------------------\n // IUserProvider\n // ---------------------------------------------------------------------------\n\n async getCurrentUser(request: Request): Promise<StudioUser | null> {\n const cookieHeader = request.headers.get('Cookie');\n const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);\n\n if (sessionCookie) {\n return this.verifySessionCookie(sessionCookie);\n }\n\n // Try bearer token\n const authHeader = request.headers.get('Authorization');\n if (authHeader?.startsWith('Bearer ')) {\n return this.verifyBearerToken(authHeader.slice(7));\n }\n\n return null;\n }\n\n async getUser(_userId: string): Promise<StudioUser | null> {\n // Cannot look up users by ID — only validate sessions\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // Internal helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Forward a sealed session cookie to the shared API's /auth/me endpoint\n * to validate it and get user info.\n */\n private async verifySessionCookie(sessionCookie: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/me`, {\n headers: {\n Cookie: `${COOKIE_NAME}=${sessionCookie}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('verifySessionCookie: shared API returned non-OK status', {\n status: res.status,\n statusText: res.statusText,\n url: `${this.sharedApiUrl}/auth/me`,\n });\n return null;\n }\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n profilePictureUrl?: string;\n };\n organizationId: string;\n role?: string;\n permissions?: string[];\n memberOrgIds?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n avatarUrl: data.user.profilePictureUrl,\n organizationId: data.organizationId,\n role: data.role,\n permissions: data.permissions,\n memberOrgIds: data.memberOrgIds,\n };\n } catch (error) {\n this.logger.error('verifySessionCookie: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/me`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n return null;\n }\n }\n\n /**\n * Forward a Bearer token to the shared API's /auth/verify endpoint\n * to validate it and get user info (used for CLI tokens).\n */\n private async verifyBearerToken(token: string): Promise<StudioUser | null> {\n try {\n const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) {\n this.logger.warn('verifyBearerToken: shared API returned non-OK status', {\n status: res.status,\n url: `${this.sharedApiUrl}/auth/verify`,\n });\n return null;\n }\n\n const data = (await res.json()) as {\n user: {\n id: string;\n email: string;\n firstName: string;\n lastName: string;\n };\n organizationId: string;\n role?: string;\n memberOrgIds?: string[];\n };\n\n return {\n id: data.user.id,\n email: data.user.email,\n name: [data.user.firstName, data.user.lastName].filter(Boolean).join(' ') || undefined,\n organizationId: data.organizationId,\n role: data.role,\n memberOrgIds: data.memberOrgIds,\n };\n } catch (error) {\n this.logger.error('verifyBearerToken: fetch to shared API failed', {\n url: `${this.sharedApiUrl}/auth/verify`,\n error: error instanceof Error ? { message: error.message, stack: error.stack } : String(error),\n });\n return null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cookie helpers\n// ---------------------------------------------------------------------------\n\nfunction parseCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));\n return match?.[1] ?? null;\n}\n\n/**\n * Parse a cookie value from a Set-Cookie header.\n * Set-Cookie format: \"name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400\"\n */\nfunction parseCookieFromHeader(setCookieHeader: string, name: string): string | null {\n // Set-Cookie header starts with \"name=value\" followed by optional attributes\n const parts = setCookieHeader.split(';');\n if (parts.length === 0) return null;\n\n const [cookieName, ...valueParts] = parts[0]!.split('=');\n if (cookieName?.trim() !== name) return null;\n\n // Value could contain = characters, so rejoin\n return valueParts.join('=') || null;\n}\n\n// ---------------------------------------------------------------------------\n// MastraRBACStudio — role-based permission provider for Studio auth\n// ---------------------------------------------------------------------------\n\nexport interface MastraRBACStudioOptions {\n /**\n * Mapping from role names to permission arrays.\n *\n * @example\n * ```typescript\n * {\n * admin: ['*'],\n * member: ['agents:read', 'workflows:*'],\n * viewer: ['agents:read', 'workflows:read'],\n * _default: [],\n * }\n * ```\n */\n roleMapping: RoleMapping;\n}\n\n/**\n * RBAC provider for Mastra Studio authentication.\n *\n * Maps user roles (from the shared API's /auth/me endpoint) to Mastra permissions\n * using a configurable role mapping.\n */\nexport class MastraRBACStudio implements IRBACProvider<StudioUser> {\n private options: MastraRBACStudioOptions;\n\n get roleMapping(): RoleMapping {\n return this.options.roleMapping;\n }\n\n constructor(options: MastraRBACStudioOptions) {\n this.options = options;\n }\n\n async getRoles(user: StudioUser): Promise<string[]> {\n return user.role ? [user.role] : [];\n }\n\n async hasRole(user: StudioUser, role: string): Promise<boolean> {\n const roles = await this.getRoles(user);\n return roles.includes(role);\n }\n\n async getPermissions(user: StudioUser): Promise<string[]> {\n const roles = await this.getRoles(user);\n if (roles.length === 0) {\n return this.options.roleMapping['_default'] ?? [];\n }\n return resolvePermissionsFromMapping(roles, this.options.roleMapping);\n }\n\n async hasPermission(user: StudioUser, permission: string): Promise<boolean> {\n const permissions = await this.getPermissions(user);\n return permissions.some(p => matchesPermission(p, permission));\n }\n\n async hasAllPermissions(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.every(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n\n async hasAnyPermission(user: StudioUser, permissions: string[]): Promise<boolean> {\n const userPermissions = await this.getPermissions(user);\n return permissions.some(required => userPermissions.some(p => matchesPermission(p, required)));\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/auth-studio",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2-alpha.0",
|
|
4
4
|
"description": "Mastra Studio Auth integration — proxies authentication through Mastra shared API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "22.19.15",
|
|
24
|
-
"@vitest/coverage-v8": "4.
|
|
25
|
-
"@vitest/ui": "4.
|
|
26
|
-
"eslint": "^
|
|
24
|
+
"@vitest/coverage-v8": "4.1.5",
|
|
25
|
+
"@vitest/ui": "4.1.5",
|
|
26
|
+
"eslint": "^10.2.1",
|
|
27
27
|
"tsup": "^8.5.1",
|
|
28
28
|
"typescript": "^5.9.3",
|
|
29
|
-
"vitest": "4.
|
|
30
|
-
"@internal/
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
29
|
+
"vitest": "4.1.5",
|
|
30
|
+
"@internal/types-builder": "0.0.61",
|
|
31
|
+
"@mastra/core": "1.29.0-alpha.5",
|
|
32
|
+
"@internal/lint": "0.0.86"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@mastra/core": ">=1.0.0-0 <2.0.0-0"
|