@mastra/auth-studio 0.0.0 → 1.1.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 +27 -0
- package/LICENSE.md +30 -0
- package/dist/index.cjs +292 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +289 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -3
- package/README.md +0 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @mastra/auth-studio
|
|
2
|
+
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added `@mastra/auth-studio` — an auth provider for deployed Mastra Studio instances that proxies authentication through the Mastra shared API. ([#13163](https://github.com/mastra-ai/mastra/pull/13163))
|
|
8
|
+
|
|
9
|
+
Deployed instances need no secrets — all WorkOS authentication is handled by the shared API. The package provides SSO login/callback flows, session management via sealed cookies, RBAC with organization-scoped permissions, and automatic forced account picker on deploy logins.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`504fc8b`](https://github.com/mastra-ai/mastra/commit/504fc8b9d0ddab717577ad3bf9c95ea4bd5377bd), [`f9c150b`](https://github.com/mastra-ai/mastra/commit/f9c150b7595ad05ad9cc9a11098e2944361e8c22), [`88de7e8`](https://github.com/mastra-ai/mastra/commit/88de7e8dfe4b7e1951a9e441bb33136e705ce24e), [`edee4b3`](https://github.com/mastra-ai/mastra/commit/edee4b37dff0af515fc7cc0e8d71ee39e6a762f0), [`3790c75`](https://github.com/mastra-ai/mastra/commit/3790c7578cc6a47d854eb12d89e6b1912867fe29), [`e7a235b`](https://github.com/mastra-ai/mastra/commit/e7a235be6472e0c870ed6c791ddb17c492dc188b), [`d51d298`](https://github.com/mastra-ai/mastra/commit/d51d298953967aab1f58ec965b644d109214f085), [`6dbeeb9`](https://github.com/mastra-ai/mastra/commit/6dbeeb94a8b1eebb727300d1a98961f882180794), [`d5f0d8d`](https://github.com/mastra-ai/mastra/commit/d5f0d8d6a03e515ddaa9b5da19b7e44b8357b07b), [`09c3b18`](https://github.com/mastra-ai/mastra/commit/09c3b1802ff14e243a8a8baea327440bc8cc2e32), [`b896379`](https://github.com/mastra-ai/mastra/commit/b8963791c6afa79484645fcec596a201f936b9a2), [`85c84eb`](https://github.com/mastra-ai/mastra/commit/85c84ebb78aebfcba9d209c8e152b16d7a00cb71), [`a89272a`](https://github.com/mastra-ai/mastra/commit/a89272a5d71939b9fcd284e6a6dc1dd091a6bdcf), [`ee9c8df`](https://github.com/mastra-ai/mastra/commit/ee9c8df644f19d055af5f496bf4942705f5a47b7), [`77b4a25`](https://github.com/mastra-ai/mastra/commit/77b4a254e51907f8ff3a3ba95596a18e93ae4b35), [`276246e`](https://github.com/mastra-ai/mastra/commit/276246e0b9066a1ea48bbc70df84dbe528daaf99), [`08ecfdb`](https://github.com/mastra-ai/mastra/commit/08ecfdbdad6fb8285deef86a034bdf4a6047cfca), [`d5f628c`](https://github.com/mastra-ai/mastra/commit/d5f628ca86c6f6f3ff1035d52f635df32dd81cab), [`524c0f3`](https://github.com/mastra-ai/mastra/commit/524c0f3c434c3d9d18f66338dcef383d6161b59c), [`c18a0e9`](https://github.com/mastra-ai/mastra/commit/c18a0e9cef1e4ca004b2963d35e4cfc031971eac), [`4bd21ea`](https://github.com/mastra-ai/mastra/commit/4bd21ea43d44d0a0427414fc047577f9f0aa3bec), [`115a7a4`](https://github.com/mastra-ai/mastra/commit/115a7a47db5e9896fec12ae6507501adb9ec89bf), [`22a48ae`](https://github.com/mastra-ai/mastra/commit/22a48ae2513eb54d8d79dad361fddbca97a155e8), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9311c17`](https://github.com/mastra-ai/mastra/commit/9311c17d7a0640d9c4da2e71b814dc67c57c6369), [`7edf78f`](https://github.com/mastra-ai/mastra/commit/7edf78f80422c43e84585f08ba11df0d4d0b73c5), [`1c4221c`](https://github.com/mastra-ai/mastra/commit/1c4221cf6032ec98d0e094d4ee11da3e48490d96), [`d25b9ea`](https://github.com/mastra-ai/mastra/commit/d25b9eabd400167255a97b690ffbc4ee4097ded5), [`fe1ce5c`](https://github.com/mastra-ai/mastra/commit/fe1ce5c9211c03d561606fda95cbfe7df1d9a9b5), [`b03c0e0`](https://github.com/mastra-ai/mastra/commit/b03c0e0389a799523929a458b0509c9e4244d562), [`0a8366b`](https://github.com/mastra-ai/mastra/commit/0a8366b0a692fcdde56c4d526e4cf03c502ae4ac), [`85664e9`](https://github.com/mastra-ai/mastra/commit/85664e9fd857320fbc245e301f764f45f66f32a3), [`bc79650`](https://github.com/mastra-ai/mastra/commit/bc796500c6e0334faa158a96077e3fb332274869), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`3a3a59e`](https://github.com/mastra-ai/mastra/commit/3a3a59e8ffaa6a985fe3d9a126a3f5ade11a6724), [`3108d4e`](https://github.com/mastra-ai/mastra/commit/3108d4e649c9fddbf03253a6feeb388a5fa9fa5a), [`0c33b2c`](https://github.com/mastra-ai/mastra/commit/0c33b2c9db537f815e1c59e2c898ffce2e395a79), [`191e5bd`](https://github.com/mastra-ai/mastra/commit/191e5bd29b82f5bda35243945790da7bc7b695c2), [`f77cd94`](https://github.com/mastra-ai/mastra/commit/f77cd94c44eabed490384e7d19232a865e13214c), [`e8135c7`](https://github.com/mastra-ai/mastra/commit/e8135c7e300dac5040670eec7eab896ac6092e30), [`daca48f`](https://github.com/mastra-ai/mastra/commit/daca48f0fb17b7ae0b62a2ac40cf0e491b2fd0b7), [`257d14f`](https://github.com/mastra-ai/mastra/commit/257d14faca5931f2e4186fc165b6f0b1f915deee), [`352f25d`](https://github.com/mastra-ai/mastra/commit/352f25da316b24cdd5b410fd8dddf6a8b763da2a), [`93477d0`](https://github.com/mastra-ai/mastra/commit/93477d0769b8a13ea5ed73d508d967fb23eaeed9), [`31c78b3`](https://github.com/mastra-ai/mastra/commit/31c78b3eb28f58a8017f1dcc795c33214d87feac), [`0bc0720`](https://github.com/mastra-ai/mastra/commit/0bc07201095791858087cc56f353fcd65e87ab54), [`36516ac`](https://github.com/mastra-ai/mastra/commit/36516aca1021cbeb42e74751b46a2614101f37c8), [`e947652`](https://github.com/mastra-ai/mastra/commit/e9476527fdecb4449e54570e80dfaf8466901254), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`ec248f6`](https://github.com/mastra-ai/mastra/commit/ec248f6b56e8a037c066c49b2178e2507471d988)]:
|
|
14
|
+
- @mastra/core@1.9.0
|
|
15
|
+
|
|
16
|
+
## 1.1.0-alpha.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- Added `@mastra/auth-studio` — an auth provider for deployed Mastra Studio instances that proxies authentication through the Mastra shared API. ([#13163](https://github.com/mastra-ai/mastra/pull/13163))
|
|
21
|
+
|
|
22
|
+
Deployed instances need no secrets — all WorkOS authentication is handled by the shared API. The package provides SSO login/callback flows, session management via sealed cookies, RBAC with organization-scoped permissions, and automatic forced account picker on deploy logins.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [[`504fc8b`](https://github.com/mastra-ai/mastra/commit/504fc8b9d0ddab717577ad3bf9c95ea4bd5377bd), [`f9c150b`](https://github.com/mastra-ai/mastra/commit/f9c150b7595ad05ad9cc9a11098e2944361e8c22), [`88de7e8`](https://github.com/mastra-ai/mastra/commit/88de7e8dfe4b7e1951a9e441bb33136e705ce24e), [`edee4b3`](https://github.com/mastra-ai/mastra/commit/edee4b37dff0af515fc7cc0e8d71ee39e6a762f0), [`3790c75`](https://github.com/mastra-ai/mastra/commit/3790c7578cc6a47d854eb12d89e6b1912867fe29), [`e7a235b`](https://github.com/mastra-ai/mastra/commit/e7a235be6472e0c870ed6c791ddb17c492dc188b), [`d51d298`](https://github.com/mastra-ai/mastra/commit/d51d298953967aab1f58ec965b644d109214f085), [`6dbeeb9`](https://github.com/mastra-ai/mastra/commit/6dbeeb94a8b1eebb727300d1a98961f882180794), [`d5f0d8d`](https://github.com/mastra-ai/mastra/commit/d5f0d8d6a03e515ddaa9b5da19b7e44b8357b07b), [`09c3b18`](https://github.com/mastra-ai/mastra/commit/09c3b1802ff14e243a8a8baea327440bc8cc2e32), [`b896379`](https://github.com/mastra-ai/mastra/commit/b8963791c6afa79484645fcec596a201f936b9a2), [`85c84eb`](https://github.com/mastra-ai/mastra/commit/85c84ebb78aebfcba9d209c8e152b16d7a00cb71), [`a89272a`](https://github.com/mastra-ai/mastra/commit/a89272a5d71939b9fcd284e6a6dc1dd091a6bdcf), [`ee9c8df`](https://github.com/mastra-ai/mastra/commit/ee9c8df644f19d055af5f496bf4942705f5a47b7), [`77b4a25`](https://github.com/mastra-ai/mastra/commit/77b4a254e51907f8ff3a3ba95596a18e93ae4b35), [`276246e`](https://github.com/mastra-ai/mastra/commit/276246e0b9066a1ea48bbc70df84dbe528daaf99), [`08ecfdb`](https://github.com/mastra-ai/mastra/commit/08ecfdbdad6fb8285deef86a034bdf4a6047cfca), [`d5f628c`](https://github.com/mastra-ai/mastra/commit/d5f628ca86c6f6f3ff1035d52f635df32dd81cab), [`524c0f3`](https://github.com/mastra-ai/mastra/commit/524c0f3c434c3d9d18f66338dcef383d6161b59c), [`c18a0e9`](https://github.com/mastra-ai/mastra/commit/c18a0e9cef1e4ca004b2963d35e4cfc031971eac), [`4bd21ea`](https://github.com/mastra-ai/mastra/commit/4bd21ea43d44d0a0427414fc047577f9f0aa3bec), [`115a7a4`](https://github.com/mastra-ai/mastra/commit/115a7a47db5e9896fec12ae6507501adb9ec89bf), [`22a48ae`](https://github.com/mastra-ai/mastra/commit/22a48ae2513eb54d8d79dad361fddbca97a155e8), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9311c17`](https://github.com/mastra-ai/mastra/commit/9311c17d7a0640d9c4da2e71b814dc67c57c6369), [`7edf78f`](https://github.com/mastra-ai/mastra/commit/7edf78f80422c43e84585f08ba11df0d4d0b73c5), [`1c4221c`](https://github.com/mastra-ai/mastra/commit/1c4221cf6032ec98d0e094d4ee11da3e48490d96), [`d25b9ea`](https://github.com/mastra-ai/mastra/commit/d25b9eabd400167255a97b690ffbc4ee4097ded5), [`fe1ce5c`](https://github.com/mastra-ai/mastra/commit/fe1ce5c9211c03d561606fda95cbfe7df1d9a9b5), [`b03c0e0`](https://github.com/mastra-ai/mastra/commit/b03c0e0389a799523929a458b0509c9e4244d562), [`0a8366b`](https://github.com/mastra-ai/mastra/commit/0a8366b0a692fcdde56c4d526e4cf03c502ae4ac), [`85664e9`](https://github.com/mastra-ai/mastra/commit/85664e9fd857320fbc245e301f764f45f66f32a3), [`bc79650`](https://github.com/mastra-ai/mastra/commit/bc796500c6e0334faa158a96077e3fb332274869), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`3a3a59e`](https://github.com/mastra-ai/mastra/commit/3a3a59e8ffaa6a985fe3d9a126a3f5ade11a6724), [`3108d4e`](https://github.com/mastra-ai/mastra/commit/3108d4e649c9fddbf03253a6feeb388a5fa9fa5a), [`0c33b2c`](https://github.com/mastra-ai/mastra/commit/0c33b2c9db537f815e1c59e2c898ffce2e395a79), [`191e5bd`](https://github.com/mastra-ai/mastra/commit/191e5bd29b82f5bda35243945790da7bc7b695c2), [`f77cd94`](https://github.com/mastra-ai/mastra/commit/f77cd94c44eabed490384e7d19232a865e13214c), [`e8135c7`](https://github.com/mastra-ai/mastra/commit/e8135c7e300dac5040670eec7eab896ac6092e30), [`daca48f`](https://github.com/mastra-ai/mastra/commit/daca48f0fb17b7ae0b62a2ac40cf0e491b2fd0b7), [`257d14f`](https://github.com/mastra-ai/mastra/commit/257d14faca5931f2e4186fc165b6f0b1f915deee), [`352f25d`](https://github.com/mastra-ai/mastra/commit/352f25da316b24cdd5b410fd8dddf6a8b763da2a), [`93477d0`](https://github.com/mastra-ai/mastra/commit/93477d0769b8a13ea5ed73d508d967fb23eaeed9), [`31c78b3`](https://github.com/mastra-ai/mastra/commit/31c78b3eb28f58a8017f1dcc795c33214d87feac), [`0bc0720`](https://github.com/mastra-ai/mastra/commit/0bc07201095791858087cc56f353fcd65e87ab54), [`36516ac`](https://github.com/mastra-ai/mastra/commit/36516aca1021cbeb42e74751b46a2614101f37c8), [`e947652`](https://github.com/mastra-ai/mastra/commit/e9476527fdecb4449e54570e80dfaf8466901254), [`3c6ef79`](https://github.com/mastra-ai/mastra/commit/3c6ef798481e00d6d22563be2de98818fd4dd5e0), [`9257d01`](https://github.com/mastra-ai/mastra/commit/9257d01d1366d81f84c582fe02b5e200cf9621f4), [`ec248f6`](https://github.com/mastra-ai/mastra/commit/ec248f6b56e8a037c066c49b2178e2507471d988)]:
|
|
27
|
+
- @mastra/core@1.9.0-alpha.0
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Portions of this software are licensed as follows:
|
|
2
|
+
|
|
3
|
+
- All content that resides under any directory named "ee/" within this
|
|
4
|
+
repository, including but not limited to:
|
|
5
|
+
- `packages/core/src/auth/ee/`
|
|
6
|
+
- `packages/server/src/server/auth/ee/`
|
|
7
|
+
is licensed under the license defined in `ee/LICENSE`.
|
|
8
|
+
|
|
9
|
+
- All third-party components incorporated into the Mastra Software are
|
|
10
|
+
licensed under the original license provided by the owner of the
|
|
11
|
+
applicable component.
|
|
12
|
+
|
|
13
|
+
- Content outside of the above-mentioned directories or restrictions is
|
|
14
|
+
available under the "Apache License 2.0" as defined below.
|
|
15
|
+
|
|
16
|
+
# Apache License 2.0
|
|
17
|
+
|
|
18
|
+
Copyright (c) 2025 Kepler Software, Inc.
|
|
19
|
+
|
|
20
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
21
|
+
you may not use this file except in compliance with the License.
|
|
22
|
+
You may obtain a copy of the License at
|
|
23
|
+
|
|
24
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
25
|
+
|
|
26
|
+
Unless required by applicable law or agreed to in writing, software
|
|
27
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
28
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
29
|
+
See the License for the specific language governing permissions and
|
|
30
|
+
limitations under the License.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ee = require('@mastra/core/auth/ee');
|
|
4
|
+
var server = require('@mastra/core/server');
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var COOKIE_NAME = "wos-session";
|
|
8
|
+
var MastraAuthStudio = class extends server.MastraAuthProvider {
|
|
9
|
+
isMastraCloudAuth = true;
|
|
10
|
+
sharedApiUrl;
|
|
11
|
+
organizationId;
|
|
12
|
+
useProductionCookies;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super({ name: "mastra-studio", ...options });
|
|
15
|
+
this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || "http://localhost:3010/v1";
|
|
16
|
+
this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;
|
|
17
|
+
if (this.sharedApiUrl.endsWith("/")) {
|
|
18
|
+
this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);
|
|
19
|
+
}
|
|
20
|
+
this.useProductionCookies = this.sharedApiUrl.includes(".mastra.ai");
|
|
21
|
+
if (options) {
|
|
22
|
+
this.registerOptions(options);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// MastraAuthProvider abstract methods
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Authenticate an incoming request by forwarding the sealed session cookie
|
|
30
|
+
* to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.
|
|
31
|
+
*/
|
|
32
|
+
async authenticateToken(token, request) {
|
|
33
|
+
let user = null;
|
|
34
|
+
const cookieHeader = request?.headers?.get("Cookie");
|
|
35
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
36
|
+
if (sessionCookie) {
|
|
37
|
+
user = await this.verifySessionCookie(sessionCookie);
|
|
38
|
+
}
|
|
39
|
+
if (!user && token) {
|
|
40
|
+
user = await this.verifyBearerToken(token);
|
|
41
|
+
}
|
|
42
|
+
if (!user) return null;
|
|
43
|
+
if (this.organizationId && user.organizationId !== this.organizationId) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return user;
|
|
47
|
+
}
|
|
48
|
+
authorizeUser(user) {
|
|
49
|
+
return !!user?.id;
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// ISSOProvider
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
getLoginUrl(redirectUri, state) {
|
|
55
|
+
let postLoginRedirect = "/";
|
|
56
|
+
if (state) {
|
|
57
|
+
const pipeIndex = state.indexOf("|");
|
|
58
|
+
if (pipeIndex !== -1) {
|
|
59
|
+
try {
|
|
60
|
+
postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const params = new URLSearchParams({
|
|
66
|
+
product: "deploy",
|
|
67
|
+
redirect_uri: redirectUri,
|
|
68
|
+
post_login_redirect: postLoginRedirect,
|
|
69
|
+
// Force re-authentication so AuthKit always shows the account picker
|
|
70
|
+
prompt: "login",
|
|
71
|
+
...this.organizationId ? { organization_id: this.organizationId } : {}
|
|
72
|
+
});
|
|
73
|
+
return `${this.sharedApiUrl}/auth/login?${params.toString()}`;
|
|
74
|
+
}
|
|
75
|
+
async handleCallback(code, _state) {
|
|
76
|
+
const user = await this.verifySessionCookie(code);
|
|
77
|
+
if (!user) {
|
|
78
|
+
throw new Error("Session validation failed");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
user,
|
|
82
|
+
tokens: {
|
|
83
|
+
accessToken: code
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
setCallbackCookieHeader(_cookieHeader) {
|
|
88
|
+
}
|
|
89
|
+
getLoginCookies() {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
getLoginButtonConfig() {
|
|
93
|
+
return {
|
|
94
|
+
provider: "mastra-studio",
|
|
95
|
+
text: "Sign in with Mastra"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
async getLogoutUrl(_redirectUri, request) {
|
|
99
|
+
const cookieHeader = request?.headers.get("Cookie");
|
|
100
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
101
|
+
if (!sessionCookie) return null;
|
|
102
|
+
try {
|
|
103
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
headers: {
|
|
106
|
+
"Content-Type": "application/json",
|
|
107
|
+
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (res.ok) {
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
return data.logoutUrl ?? null;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// ISessionProvider
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
async createSession(userId, metadata) {
|
|
122
|
+
const now = /* @__PURE__ */ new Date();
|
|
123
|
+
return {
|
|
124
|
+
id: metadata?.accessToken || crypto.randomUUID(),
|
|
125
|
+
userId,
|
|
126
|
+
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
127
|
+
// 24 hours
|
|
128
|
+
createdAt: now,
|
|
129
|
+
metadata
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async validateSession(sessionId) {
|
|
133
|
+
const user = await this.verifySessionCookie(sessionId);
|
|
134
|
+
if (!user) return null;
|
|
135
|
+
const now = /* @__PURE__ */ new Date();
|
|
136
|
+
return {
|
|
137
|
+
id: sessionId,
|
|
138
|
+
userId: user.id,
|
|
139
|
+
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
140
|
+
createdAt: now
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async destroySession(sessionId) {
|
|
144
|
+
try {
|
|
145
|
+
await fetch(`${this.sharedApiUrl}/auth/logout`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: {
|
|
148
|
+
Cookie: `${COOKIE_NAME}=${sessionId}`
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async refreshSession(sessionId) {
|
|
155
|
+
return this.validateSession(sessionId);
|
|
156
|
+
}
|
|
157
|
+
getSessionIdFromRequest(request) {
|
|
158
|
+
const cookieHeader = request.headers.get("Cookie");
|
|
159
|
+
return parseCookie(cookieHeader, COOKIE_NAME);
|
|
160
|
+
}
|
|
161
|
+
getSessionHeaders(session) {
|
|
162
|
+
const parts = [`${COOKIE_NAME}=${session.id}`, "HttpOnly", "SameSite=Lax", "Path=/", "Max-Age=86400"];
|
|
163
|
+
if (this.useProductionCookies) {
|
|
164
|
+
parts.push("Secure");
|
|
165
|
+
parts.push("Domain=.mastra.ai");
|
|
166
|
+
}
|
|
167
|
+
return { "Set-Cookie": parts.join("; ") };
|
|
168
|
+
}
|
|
169
|
+
getClearSessionHeaders() {
|
|
170
|
+
const parts = [`${COOKIE_NAME}=`, "HttpOnly", "SameSite=Lax", "Path=/", "Max-Age=0"];
|
|
171
|
+
if (this.useProductionCookies) {
|
|
172
|
+
parts.push("Secure");
|
|
173
|
+
parts.push("Domain=.mastra.ai");
|
|
174
|
+
}
|
|
175
|
+
return { "Set-Cookie": parts.join("; ") };
|
|
176
|
+
}
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// IUserProvider
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
async getCurrentUser(request) {
|
|
181
|
+
const cookieHeader = request.headers.get("Cookie");
|
|
182
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
183
|
+
if (sessionCookie) {
|
|
184
|
+
return this.verifySessionCookie(sessionCookie);
|
|
185
|
+
}
|
|
186
|
+
const authHeader = request.headers.get("Authorization");
|
|
187
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
188
|
+
return this.verifyBearerToken(authHeader.slice(7));
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
async getUser(_userId) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Internal helpers
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
/**
|
|
199
|
+
* Forward a sealed session cookie to the shared API's /auth/me endpoint
|
|
200
|
+
* to validate it and get user info.
|
|
201
|
+
*/
|
|
202
|
+
async verifySessionCookie(sessionCookie) {
|
|
203
|
+
try {
|
|
204
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/me`, {
|
|
205
|
+
headers: {
|
|
206
|
+
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
if (!res.ok) return null;
|
|
210
|
+
const data = await res.json();
|
|
211
|
+
return {
|
|
212
|
+
id: data.user.id,
|
|
213
|
+
email: data.user.email,
|
|
214
|
+
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
215
|
+
avatarUrl: data.user.profilePictureUrl,
|
|
216
|
+
organizationId: data.organizationId,
|
|
217
|
+
role: data.role,
|
|
218
|
+
permissions: data.permissions
|
|
219
|
+
};
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Forward a Bearer token to the shared API's /auth/verify endpoint
|
|
226
|
+
* to validate it and get user info (used for CLI tokens).
|
|
227
|
+
*/
|
|
228
|
+
async verifyBearerToken(token) {
|
|
229
|
+
try {
|
|
230
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {
|
|
231
|
+
headers: {
|
|
232
|
+
Authorization: `Bearer ${token}`
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
if (!res.ok) return null;
|
|
236
|
+
const data = await res.json();
|
|
237
|
+
return {
|
|
238
|
+
id: data.user.id,
|
|
239
|
+
email: data.user.email,
|
|
240
|
+
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
241
|
+
organizationId: data.organizationId
|
|
242
|
+
};
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
function parseCookie(cookieHeader, name) {
|
|
249
|
+
if (!cookieHeader) return null;
|
|
250
|
+
const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));
|
|
251
|
+
return match?.[1] ?? null;
|
|
252
|
+
}
|
|
253
|
+
var MastraRBACStudio = class {
|
|
254
|
+
options;
|
|
255
|
+
get roleMapping() {
|
|
256
|
+
return this.options.roleMapping;
|
|
257
|
+
}
|
|
258
|
+
constructor(options) {
|
|
259
|
+
this.options = options;
|
|
260
|
+
}
|
|
261
|
+
async getRoles(user) {
|
|
262
|
+
return user.role ? [user.role] : [];
|
|
263
|
+
}
|
|
264
|
+
async hasRole(user, role) {
|
|
265
|
+
const roles = await this.getRoles(user);
|
|
266
|
+
return roles.includes(role);
|
|
267
|
+
}
|
|
268
|
+
async getPermissions(user) {
|
|
269
|
+
const roles = await this.getRoles(user);
|
|
270
|
+
if (roles.length === 0) {
|
|
271
|
+
return this.options.roleMapping["_default"] ?? [];
|
|
272
|
+
}
|
|
273
|
+
return ee.resolvePermissionsFromMapping(roles, this.options.roleMapping);
|
|
274
|
+
}
|
|
275
|
+
async hasPermission(user, permission) {
|
|
276
|
+
const permissions = await this.getPermissions(user);
|
|
277
|
+
return permissions.some((p) => ee.matchesPermission(p, permission));
|
|
278
|
+
}
|
|
279
|
+
async hasAllPermissions(user, permissions) {
|
|
280
|
+
const userPermissions = await this.getPermissions(user);
|
|
281
|
+
return permissions.every((required) => userPermissions.some((p) => ee.matchesPermission(p, required)));
|
|
282
|
+
}
|
|
283
|
+
async hasAnyPermission(user, permissions) {
|
|
284
|
+
const userPermissions = await this.getPermissions(user);
|
|
285
|
+
return permissions.some((required) => userPermissions.some((p) => ee.matchesPermission(p, required)));
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
exports.MastraAuthStudio = MastraAuthStudio;
|
|
290
|
+
exports.MastraRBACStudio = MastraRBACStudio;
|
|
291
|
+
//# sourceMappingURL=index.cjs.map
|
|
292
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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\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\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 // Use production cookie settings (Secure + Domain=.mastra.ai) only when\n // the shared API is actually on .mastra.ai — NOT based on NODE_ENV which\n // may be 'production' even in local dev (e.g. mastra dev sets it).\n this.useProductionCookies = this.sharedApiUrl.includes('.mastra.ai');\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 return this.validateSession(sessionId);\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) {\n parts.push('Secure');\n parts.push('Domain=.mastra.ai');\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) {\n parts.push('Secure');\n parts.push('Domain=.mastra.ai');\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 };\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 };\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// 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
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ISSOProvider, ISessionProvider, IUserProvider, Session, SSOCallbackResult, SSOLoginConfig } from '@mastra/core/auth';
|
|
2
|
+
import type { EEUser, IRBACProvider, RoleMapping } from '@mastra/core/auth/ee';
|
|
3
|
+
import { MastraAuthProvider } from '@mastra/core/server';
|
|
4
|
+
import type { MastraAuthProviderOptions } from '@mastra/core/server';
|
|
5
|
+
export interface StudioUser extends EEUser {
|
|
6
|
+
id: string;
|
|
7
|
+
email?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
avatarUrl?: string;
|
|
10
|
+
organizationId?: string;
|
|
11
|
+
role?: string;
|
|
12
|
+
permissions?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface MastraAuthStudioOptions extends MastraAuthProviderOptions<StudioUser> {
|
|
15
|
+
/** Base URL of the Mastra shared API (e.g., https://api.mastra.ai/v1) */
|
|
16
|
+
sharedApiUrl?: string;
|
|
17
|
+
/** Organization ID that owns this deployed instance. Users not in this org are rejected. */
|
|
18
|
+
organizationId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Auth provider for Mastra Studio deployed instances.
|
|
22
|
+
*
|
|
23
|
+
* Proxies all authentication through the shared API, keeping the
|
|
24
|
+
* WorkOS API key safely in the shared API. Deployed instances only
|
|
25
|
+
* need the shared API URL — no secrets required.
|
|
26
|
+
*
|
|
27
|
+
* The shared API's sealed session cookie (`wos-session`) is set with
|
|
28
|
+
* `Domain=.mastra.ai` in production, so it's included in requests
|
|
29
|
+
* to deployed instances and can be forwarded for validation.
|
|
30
|
+
*/
|
|
31
|
+
export declare class MastraAuthStudio extends MastraAuthProvider<StudioUser> implements ISSOProvider<StudioUser>, ISessionProvider<Session>, IUserProvider<StudioUser> {
|
|
32
|
+
readonly isMastraCloudAuth = true;
|
|
33
|
+
private sharedApiUrl;
|
|
34
|
+
private organizationId;
|
|
35
|
+
private useProductionCookies;
|
|
36
|
+
constructor(options?: MastraAuthStudioOptions);
|
|
37
|
+
/**
|
|
38
|
+
* Authenticate an incoming request by forwarding the sealed session cookie
|
|
39
|
+
* to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.
|
|
40
|
+
*/
|
|
41
|
+
authenticateToken(token: string, request: any): Promise<StudioUser | null>;
|
|
42
|
+
authorizeUser(user: StudioUser): boolean;
|
|
43
|
+
getLoginUrl(redirectUri: string, state: string): string;
|
|
44
|
+
handleCallback(code: string, _state: string): Promise<SSOCallbackResult<StudioUser>>;
|
|
45
|
+
setCallbackCookieHeader(_cookieHeader: string | null): void;
|
|
46
|
+
getLoginCookies(): string[] | undefined;
|
|
47
|
+
getLoginButtonConfig(): SSOLoginConfig;
|
|
48
|
+
getLogoutUrl(_redirectUri: string, request?: Request): Promise<string | null>;
|
|
49
|
+
createSession(userId: string, metadata?: Record<string, unknown>): Promise<Session>;
|
|
50
|
+
validateSession(sessionId: string): Promise<Session | null>;
|
|
51
|
+
destroySession(sessionId: string): Promise<void>;
|
|
52
|
+
refreshSession(sessionId: string): Promise<Session | null>;
|
|
53
|
+
getSessionIdFromRequest(request: Request): string | null;
|
|
54
|
+
getSessionHeaders(session: Session): Record<string, string>;
|
|
55
|
+
getClearSessionHeaders(): Record<string, string>;
|
|
56
|
+
getCurrentUser(request: Request): Promise<StudioUser | null>;
|
|
57
|
+
getUser(_userId: string): Promise<StudioUser | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Forward a sealed session cookie to the shared API's /auth/me endpoint
|
|
60
|
+
* to validate it and get user info.
|
|
61
|
+
*/
|
|
62
|
+
private verifySessionCookie;
|
|
63
|
+
/**
|
|
64
|
+
* Forward a Bearer token to the shared API's /auth/verify endpoint
|
|
65
|
+
* to validate it and get user info (used for CLI tokens).
|
|
66
|
+
*/
|
|
67
|
+
private verifyBearerToken;
|
|
68
|
+
}
|
|
69
|
+
export interface MastraRBACStudioOptions {
|
|
70
|
+
/**
|
|
71
|
+
* Mapping from role names to permission arrays.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* {
|
|
76
|
+
* admin: ['*'],
|
|
77
|
+
* member: ['agents:read', 'workflows:*'],
|
|
78
|
+
* viewer: ['agents:read', 'workflows:read'],
|
|
79
|
+
* _default: [],
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
roleMapping: RoleMapping;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* RBAC provider for Mastra Studio authentication.
|
|
87
|
+
*
|
|
88
|
+
* Maps user roles (from the shared API's /auth/me endpoint) to Mastra permissions
|
|
89
|
+
* using a configurable role mapping.
|
|
90
|
+
*/
|
|
91
|
+
export declare class MastraRBACStudio implements IRBACProvider<StudioUser> {
|
|
92
|
+
private options;
|
|
93
|
+
get roleMapping(): RoleMapping;
|
|
94
|
+
constructor(options: MastraRBACStudioOptions);
|
|
95
|
+
getRoles(user: StudioUser): Promise<string[]>;
|
|
96
|
+
hasRole(user: StudioUser, role: string): Promise<boolean>;
|
|
97
|
+
getPermissions(user: StudioUser): Promise<string[]>;
|
|
98
|
+
hasPermission(user: StudioUser, permission: string): Promise<boolean>;
|
|
99
|
+
hasAllPermissions(user: StudioUser, permissions: string[]): Promise<boolean>;
|
|
100
|
+
hasAnyPermission(user: StudioUser, permissions: string[]): Promise<boolean>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { resolvePermissionsFromMapping, matchesPermission } from '@mastra/core/auth/ee';
|
|
2
|
+
import { MastraAuthProvider } from '@mastra/core/server';
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var COOKIE_NAME = "wos-session";
|
|
6
|
+
var MastraAuthStudio = class extends MastraAuthProvider {
|
|
7
|
+
isMastraCloudAuth = true;
|
|
8
|
+
sharedApiUrl;
|
|
9
|
+
organizationId;
|
|
10
|
+
useProductionCookies;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
super({ name: "mastra-studio", ...options });
|
|
13
|
+
this.sharedApiUrl = options?.sharedApiUrl || process.env.MASTRA_SHARED_API_URL || "http://localhost:3010/v1";
|
|
14
|
+
this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID;
|
|
15
|
+
if (this.sharedApiUrl.endsWith("/")) {
|
|
16
|
+
this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
this.useProductionCookies = this.sharedApiUrl.includes(".mastra.ai");
|
|
19
|
+
if (options) {
|
|
20
|
+
this.registerOptions(options);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// MastraAuthProvider abstract methods
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Authenticate an incoming request by forwarding the sealed session cookie
|
|
28
|
+
* to the shared API's /auth/me endpoint, or a Bearer token to /auth/verify.
|
|
29
|
+
*/
|
|
30
|
+
async authenticateToken(token, request) {
|
|
31
|
+
let user = null;
|
|
32
|
+
const cookieHeader = request?.headers?.get("Cookie");
|
|
33
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
34
|
+
if (sessionCookie) {
|
|
35
|
+
user = await this.verifySessionCookie(sessionCookie);
|
|
36
|
+
}
|
|
37
|
+
if (!user && token) {
|
|
38
|
+
user = await this.verifyBearerToken(token);
|
|
39
|
+
}
|
|
40
|
+
if (!user) return null;
|
|
41
|
+
if (this.organizationId && user.organizationId !== this.organizationId) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return user;
|
|
45
|
+
}
|
|
46
|
+
authorizeUser(user) {
|
|
47
|
+
return !!user?.id;
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// ISSOProvider
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
getLoginUrl(redirectUri, state) {
|
|
53
|
+
let postLoginRedirect = "/";
|
|
54
|
+
if (state) {
|
|
55
|
+
const pipeIndex = state.indexOf("|");
|
|
56
|
+
if (pipeIndex !== -1) {
|
|
57
|
+
try {
|
|
58
|
+
postLoginRedirect = decodeURIComponent(state.slice(pipeIndex + 1));
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const params = new URLSearchParams({
|
|
64
|
+
product: "deploy",
|
|
65
|
+
redirect_uri: redirectUri,
|
|
66
|
+
post_login_redirect: postLoginRedirect,
|
|
67
|
+
// Force re-authentication so AuthKit always shows the account picker
|
|
68
|
+
prompt: "login",
|
|
69
|
+
...this.organizationId ? { organization_id: this.organizationId } : {}
|
|
70
|
+
});
|
|
71
|
+
return `${this.sharedApiUrl}/auth/login?${params.toString()}`;
|
|
72
|
+
}
|
|
73
|
+
async handleCallback(code, _state) {
|
|
74
|
+
const user = await this.verifySessionCookie(code);
|
|
75
|
+
if (!user) {
|
|
76
|
+
throw new Error("Session validation failed");
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
user,
|
|
80
|
+
tokens: {
|
|
81
|
+
accessToken: code
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
setCallbackCookieHeader(_cookieHeader) {
|
|
86
|
+
}
|
|
87
|
+
getLoginCookies() {
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
getLoginButtonConfig() {
|
|
91
|
+
return {
|
|
92
|
+
provider: "mastra-studio",
|
|
93
|
+
text: "Sign in with Mastra"
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async getLogoutUrl(_redirectUri, request) {
|
|
97
|
+
const cookieHeader = request?.headers.get("Cookie");
|
|
98
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
99
|
+
if (!sessionCookie) return null;
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/logout`, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: {
|
|
104
|
+
"Content-Type": "application/json",
|
|
105
|
+
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (res.ok) {
|
|
109
|
+
const data = await res.json();
|
|
110
|
+
return data.logoutUrl ?? null;
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// ISessionProvider
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
async createSession(userId, metadata) {
|
|
120
|
+
const now = /* @__PURE__ */ new Date();
|
|
121
|
+
return {
|
|
122
|
+
id: metadata?.accessToken || crypto.randomUUID(),
|
|
123
|
+
userId,
|
|
124
|
+
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
125
|
+
// 24 hours
|
|
126
|
+
createdAt: now,
|
|
127
|
+
metadata
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async validateSession(sessionId) {
|
|
131
|
+
const user = await this.verifySessionCookie(sessionId);
|
|
132
|
+
if (!user) return null;
|
|
133
|
+
const now = /* @__PURE__ */ new Date();
|
|
134
|
+
return {
|
|
135
|
+
id: sessionId,
|
|
136
|
+
userId: user.id,
|
|
137
|
+
expiresAt: new Date(now.getTime() + 24 * 60 * 60 * 1e3),
|
|
138
|
+
createdAt: now
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async destroySession(sessionId) {
|
|
142
|
+
try {
|
|
143
|
+
await fetch(`${this.sharedApiUrl}/auth/logout`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: {
|
|
146
|
+
Cookie: `${COOKIE_NAME}=${sessionId}`
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async refreshSession(sessionId) {
|
|
153
|
+
return this.validateSession(sessionId);
|
|
154
|
+
}
|
|
155
|
+
getSessionIdFromRequest(request) {
|
|
156
|
+
const cookieHeader = request.headers.get("Cookie");
|
|
157
|
+
return parseCookie(cookieHeader, COOKIE_NAME);
|
|
158
|
+
}
|
|
159
|
+
getSessionHeaders(session) {
|
|
160
|
+
const parts = [`${COOKIE_NAME}=${session.id}`, "HttpOnly", "SameSite=Lax", "Path=/", "Max-Age=86400"];
|
|
161
|
+
if (this.useProductionCookies) {
|
|
162
|
+
parts.push("Secure");
|
|
163
|
+
parts.push("Domain=.mastra.ai");
|
|
164
|
+
}
|
|
165
|
+
return { "Set-Cookie": parts.join("; ") };
|
|
166
|
+
}
|
|
167
|
+
getClearSessionHeaders() {
|
|
168
|
+
const parts = [`${COOKIE_NAME}=`, "HttpOnly", "SameSite=Lax", "Path=/", "Max-Age=0"];
|
|
169
|
+
if (this.useProductionCookies) {
|
|
170
|
+
parts.push("Secure");
|
|
171
|
+
parts.push("Domain=.mastra.ai");
|
|
172
|
+
}
|
|
173
|
+
return { "Set-Cookie": parts.join("; ") };
|
|
174
|
+
}
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// IUserProvider
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
async getCurrentUser(request) {
|
|
179
|
+
const cookieHeader = request.headers.get("Cookie");
|
|
180
|
+
const sessionCookie = parseCookie(cookieHeader, COOKIE_NAME);
|
|
181
|
+
if (sessionCookie) {
|
|
182
|
+
return this.verifySessionCookie(sessionCookie);
|
|
183
|
+
}
|
|
184
|
+
const authHeader = request.headers.get("Authorization");
|
|
185
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
186
|
+
return this.verifyBearerToken(authHeader.slice(7));
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
async getUser(_userId) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Internal helpers
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* Forward a sealed session cookie to the shared API's /auth/me endpoint
|
|
198
|
+
* to validate it and get user info.
|
|
199
|
+
*/
|
|
200
|
+
async verifySessionCookie(sessionCookie) {
|
|
201
|
+
try {
|
|
202
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/me`, {
|
|
203
|
+
headers: {
|
|
204
|
+
Cookie: `${COOKIE_NAME}=${sessionCookie}`
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
if (!res.ok) return null;
|
|
208
|
+
const data = await res.json();
|
|
209
|
+
return {
|
|
210
|
+
id: data.user.id,
|
|
211
|
+
email: data.user.email,
|
|
212
|
+
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
213
|
+
avatarUrl: data.user.profilePictureUrl,
|
|
214
|
+
organizationId: data.organizationId,
|
|
215
|
+
role: data.role,
|
|
216
|
+
permissions: data.permissions
|
|
217
|
+
};
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Forward a Bearer token to the shared API's /auth/verify endpoint
|
|
224
|
+
* to validate it and get user info (used for CLI tokens).
|
|
225
|
+
*/
|
|
226
|
+
async verifyBearerToken(token) {
|
|
227
|
+
try {
|
|
228
|
+
const res = await fetch(`${this.sharedApiUrl}/auth/verify`, {
|
|
229
|
+
headers: {
|
|
230
|
+
Authorization: `Bearer ${token}`
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
if (!res.ok) return null;
|
|
234
|
+
const data = await res.json();
|
|
235
|
+
return {
|
|
236
|
+
id: data.user.id,
|
|
237
|
+
email: data.user.email,
|
|
238
|
+
name: [data.user.firstName, data.user.lastName].filter(Boolean).join(" ") || void 0,
|
|
239
|
+
organizationId: data.organizationId
|
|
240
|
+
};
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
function parseCookie(cookieHeader, name) {
|
|
247
|
+
if (!cookieHeader) return null;
|
|
248
|
+
const match = cookieHeader.match(new RegExp(`${name}=([^;]+)`));
|
|
249
|
+
return match?.[1] ?? null;
|
|
250
|
+
}
|
|
251
|
+
var MastraRBACStudio = class {
|
|
252
|
+
options;
|
|
253
|
+
get roleMapping() {
|
|
254
|
+
return this.options.roleMapping;
|
|
255
|
+
}
|
|
256
|
+
constructor(options) {
|
|
257
|
+
this.options = options;
|
|
258
|
+
}
|
|
259
|
+
async getRoles(user) {
|
|
260
|
+
return user.role ? [user.role] : [];
|
|
261
|
+
}
|
|
262
|
+
async hasRole(user, role) {
|
|
263
|
+
const roles = await this.getRoles(user);
|
|
264
|
+
return roles.includes(role);
|
|
265
|
+
}
|
|
266
|
+
async getPermissions(user) {
|
|
267
|
+
const roles = await this.getRoles(user);
|
|
268
|
+
if (roles.length === 0) {
|
|
269
|
+
return this.options.roleMapping["_default"] ?? [];
|
|
270
|
+
}
|
|
271
|
+
return resolvePermissionsFromMapping(roles, this.options.roleMapping);
|
|
272
|
+
}
|
|
273
|
+
async hasPermission(user, permission) {
|
|
274
|
+
const permissions = await this.getPermissions(user);
|
|
275
|
+
return permissions.some((p) => matchesPermission(p, permission));
|
|
276
|
+
}
|
|
277
|
+
async hasAllPermissions(user, permissions) {
|
|
278
|
+
const userPermissions = await this.getPermissions(user);
|
|
279
|
+
return permissions.every((required) => userPermissions.some((p) => matchesPermission(p, required)));
|
|
280
|
+
}
|
|
281
|
+
async hasAnyPermission(user, permissions) {
|
|
282
|
+
const userPermissions = await this.getPermissions(user);
|
|
283
|
+
return permissions.some((required) => userPermissions.some((p) => matchesPermission(p, required)));
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export { MastraAuthStudio, MastraRBACStudio };
|
|
288
|
+
//# sourceMappingURL=index.js.map
|
|
289
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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\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\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 // Use production cookie settings (Secure + Domain=.mastra.ai) only when\n // the shared API is actually on .mastra.ai — NOT based on NODE_ENV which\n // may be 'production' even in local dev (e.g. mastra dev sets it).\n this.useProductionCookies = this.sharedApiUrl.includes('.mastra.ai');\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 return this.validateSession(sessionId);\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) {\n parts.push('Secure');\n parts.push('Domain=.mastra.ai');\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) {\n parts.push('Secure');\n parts.push('Domain=.mastra.ai');\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 };\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 };\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// 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,12 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/auth-studio",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Mastra Studio Auth integration",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Mastra Studio Auth integration — proxies authentication through Mastra shared API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
5
21
|
"license": "Apache-2.0",
|
|
22
|
+
"dependencies": {},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "22.19.7",
|
|
25
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
26
|
+
"@vitest/ui": "4.0.18",
|
|
27
|
+
"eslint": "^9.37.0",
|
|
28
|
+
"tsup": "^8.5.1",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "4.0.18",
|
|
31
|
+
"@internal/lint": "0.0.64",
|
|
32
|
+
"@internal/types-builder": "0.0.39",
|
|
33
|
+
"@mastra/core": "1.9.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@mastra/core": ">=1.0.0-0 <2.0.0-0"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
6
42
|
"homepage": "https://mastra.ai",
|
|
7
43
|
"repository": {
|
|
8
44
|
"type": "git",
|
|
9
45
|
"url": "git+https://github.com/mastra-ai/mastra.git",
|
|
10
46
|
"directory": "auth/studio"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/mastra-ai/mastra/issues"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22.13.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup --silent --config tsup.config.ts",
|
|
56
|
+
"build:watch": "tsup --watch --silent --config tsup.config.ts",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"lint": "eslint ."
|
|
11
59
|
}
|
|
12
|
-
}
|
|
60
|
+
}
|
package/README.md
DELETED