@mastra/auth-studio 1.3.8-alpha.0 → 1.3.9-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/dist/_types/@internal_auth/dist/provider/index.d.ts +9 -0
- package/dist/index.cjs +84 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ export interface MastraAuthStudioOptions extends MastraAuthProviderOptions<Studi
|
|
|
32
32
|
* Can also be set via MASTRA_COOKIE_DOMAIN environment variable.
|
|
33
33
|
*/
|
|
34
34
|
cookieDomain?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Lifetime of the session cookie (and Session.expiresAt) in seconds.
|
|
37
|
+
* Should match the identity provider's maximum session length.
|
|
38
|
+
* Can also be set via MASTRA_SESSION_MAX_AGE environment variable.
|
|
39
|
+
* Defaults to 14 days.
|
|
40
|
+
*/
|
|
41
|
+
sessionMaxAgeSeconds?: number;
|
|
35
42
|
}
|
|
36
43
|
/**
|
|
37
44
|
* Auth provider for Mastra Studio deployed instances.
|
|
@@ -50,6 +57,7 @@ export declare class MastraAuthStudio extends MastraAuthProvider<StudioUser> imp
|
|
|
50
57
|
private organizationId;
|
|
51
58
|
private useProductionCookies;
|
|
52
59
|
private cookieDomain;
|
|
60
|
+
private sessionMaxAgeSeconds;
|
|
53
61
|
/**
|
|
54
62
|
* `userId → sealed session cookie` cache. The `IOrganizationsProvider`
|
|
55
63
|
* interface only hands us a `userId`, but the shared API's org endpoints are
|
|
@@ -76,6 +84,19 @@ export declare class MastraAuthStudio extends MastraAuthProvider<StudioUser> imp
|
|
|
76
84
|
* caller's promise is reused by every follower until it settles.
|
|
77
85
|
*/
|
|
78
86
|
private organizationBootstrapInFlight;
|
|
87
|
+
/**
|
|
88
|
+
* Per-request response headers to forward to the browser — specifically a
|
|
89
|
+
* rotated `wos-session` `Set-Cookie` returned by the shared API when the
|
|
90
|
+
* platform `sessionAuth` middleware transparently refreshes an expired
|
|
91
|
+
* access token. Without this, the browser's sealed cookie would never
|
|
92
|
+
* rotate: platform would succeed server-to-server by refreshing on every
|
|
93
|
+
* request, but the next refresh would hit `invalid_grant` once WorkOS
|
|
94
|
+
* invalidated the previous refresh token.
|
|
95
|
+
*
|
|
96
|
+
* Keyed by the underlying `Request` (via `getWebRequest`) so we don't retain
|
|
97
|
+
* requests after the response is written.
|
|
98
|
+
*/
|
|
99
|
+
private pendingResponseHeaders;
|
|
79
100
|
constructor(options?: MastraAuthStudioOptions);
|
|
80
101
|
/**
|
|
81
102
|
* Authenticate an incoming request by forwarding the sealed session cookie
|
|
@@ -137,6 +158,25 @@ export declare class MastraAuthStudio extends MastraAuthProvider<StudioUser> imp
|
|
|
137
158
|
private verificationKey;
|
|
138
159
|
private getCachedVerification;
|
|
139
160
|
private cacheVerification;
|
|
161
|
+
/**
|
|
162
|
+
* Read every `Set-Cookie` header from a response, falling back to the
|
|
163
|
+
* single-value accessor on runtimes without `Headers.getSetCookie()`.
|
|
164
|
+
*/
|
|
165
|
+
private readSetCookieHeaders;
|
|
166
|
+
/**
|
|
167
|
+
* If the shared API rotated the sealed `wos-session` cookie on this
|
|
168
|
+
* response, stash the `Set-Cookie` header against the inbound request so
|
|
169
|
+
* the server middleware can forward it to the browser, and invalidate the
|
|
170
|
+
* short-TTL verification cache entry for the old cookie value. Returns the
|
|
171
|
+
* rotated sealed value, or null when the cookie was not rotated.
|
|
172
|
+
*/
|
|
173
|
+
private captureRotatedCookie;
|
|
174
|
+
/**
|
|
175
|
+
* Server middleware hook: emit any response headers the provider
|
|
176
|
+
* accumulated for this request (currently a rotated `Set-Cookie`) and
|
|
177
|
+
* clear them from the provider's per-request state.
|
|
178
|
+
*/
|
|
179
|
+
consumePendingResponseHeaders(request: MastraAuthRequest): Record<string, string> | undefined;
|
|
140
180
|
/**
|
|
141
181
|
* Fetch the shared API's `/auth/me` and return the raw response body, or
|
|
142
182
|
* `null` on any non-OK / network error. Split out so `ensureOrganization`
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,OAAO,EACP,iBAAiB,EACjB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEzE,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;IACvB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,uBAAwB,SAAQ,yBAAyB,CAAC,UAAU,CAAC;IACpF,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,10 @@ function getRequestHeader(request, name) {
|
|
|
20
20
|
if (headers && typeof headers === "object") return headerFromPlainObject(headers, name);
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
+
function getWebRequest(request) {
|
|
24
|
+
if (request instanceof Request) return request;
|
|
25
|
+
return request.raw instanceof Request ? request.raw : void 0;
|
|
26
|
+
}
|
|
23
27
|
//#endregion
|
|
24
28
|
//#region ../../packages/_internal-core/dist/logger/index.js
|
|
25
29
|
/**
|
|
@@ -423,6 +427,7 @@ function resolvePermissionsFromMapping(roles, mapping) {
|
|
|
423
427
|
*/
|
|
424
428
|
//#endregion
|
|
425
429
|
//#region src/index.ts
|
|
430
|
+
const DEFAULT_SESSION_MAX_AGE_SECONDS = 336 * 60 * 60;
|
|
426
431
|
const COOKIE_NAME = "wos-session";
|
|
427
432
|
const PROJECT_CONFIG_FILE = ".mastra-project.json";
|
|
428
433
|
/**
|
|
@@ -473,6 +478,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
473
478
|
organizationId;
|
|
474
479
|
useProductionCookies;
|
|
475
480
|
cookieDomain;
|
|
481
|
+
sessionMaxAgeSeconds;
|
|
476
482
|
/**
|
|
477
483
|
* `userId → sealed session cookie` cache. The `IOrganizationsProvider`
|
|
478
484
|
* interface only hands us a `userId`, but the shared API's org endpoints are
|
|
@@ -499,6 +505,19 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
499
505
|
* caller's promise is reused by every follower until it settles.
|
|
500
506
|
*/
|
|
501
507
|
organizationBootstrapInFlight = /* @__PURE__ */ new Map();
|
|
508
|
+
/**
|
|
509
|
+
* Per-request response headers to forward to the browser — specifically a
|
|
510
|
+
* rotated `wos-session` `Set-Cookie` returned by the shared API when the
|
|
511
|
+
* platform `sessionAuth` middleware transparently refreshes an expired
|
|
512
|
+
* access token. Without this, the browser's sealed cookie would never
|
|
513
|
+
* rotate: platform would succeed server-to-server by refreshing on every
|
|
514
|
+
* request, but the next refresh would hit `invalid_grant` once WorkOS
|
|
515
|
+
* invalidated the previous refresh token.
|
|
516
|
+
*
|
|
517
|
+
* Keyed by the underlying `Request` (via `getWebRequest`) so we don't retain
|
|
518
|
+
* requests after the response is written.
|
|
519
|
+
*/
|
|
520
|
+
pendingResponseHeaders = /* @__PURE__ */ new WeakMap();
|
|
502
521
|
constructor(options) {
|
|
503
522
|
super({
|
|
504
523
|
name: "mastra-studio",
|
|
@@ -509,6 +528,9 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
509
528
|
this.organizationId = options?.organizationId || process.env.MASTRA_ORGANIZATION_ID || readOrganizationIdFromProjectConfig();
|
|
510
529
|
if (this.sharedApiUrl.endsWith("/")) this.sharedApiUrl = this.sharedApiUrl.slice(0, -1);
|
|
511
530
|
this.cookieDomain = options?.cookieDomain || process.env.MASTRA_COOKIE_DOMAIN;
|
|
531
|
+
const envMaxAge = Number(process.env.MASTRA_SESSION_MAX_AGE);
|
|
532
|
+
const maxAge = options?.sessionMaxAgeSeconds ?? (envMaxAge > 0 ? envMaxAge : void 0);
|
|
533
|
+
this.sessionMaxAgeSeconds = maxAge && Number.isFinite(maxAge) && maxAge > 0 ? Math.floor(maxAge) : DEFAULT_SESSION_MAX_AGE_SECONDS;
|
|
512
534
|
let autoDetectMastraAi = false;
|
|
513
535
|
if (explicitSharedApiUrl) try {
|
|
514
536
|
const hostname = new URL(this.sharedApiUrl).hostname.toLowerCase();
|
|
@@ -527,7 +549,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
527
549
|
async authenticateToken(token, request) {
|
|
528
550
|
let user = null;
|
|
529
551
|
const sessionCookie = parseCookie(getRequestHeader(request, "Cookie"), COOKIE_NAME);
|
|
530
|
-
if (sessionCookie) user = await this.verifySessionCookie(sessionCookie);
|
|
552
|
+
if (sessionCookie) user = await this.verifySessionCookie(sessionCookie, request);
|
|
531
553
|
if (!user && token) user = await this.verifyBearerToken(token);
|
|
532
554
|
if (!user) return null;
|
|
533
555
|
if (this.organizationId && !user.memberOrgIds?.includes(this.organizationId)) return null;
|
|
@@ -601,7 +623,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
601
623
|
return {
|
|
602
624
|
id: metadata?.accessToken || crypto.randomUUID(),
|
|
603
625
|
userId,
|
|
604
|
-
expiresAt: new Date(now.getTime() +
|
|
626
|
+
expiresAt: new Date(now.getTime() + this.sessionMaxAgeSeconds * 1e3),
|
|
605
627
|
createdAt: now,
|
|
606
628
|
metadata
|
|
607
629
|
};
|
|
@@ -613,7 +635,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
613
635
|
return {
|
|
614
636
|
id: sessionId,
|
|
615
637
|
userId: user.id,
|
|
616
|
-
expiresAt: new Date(now.getTime() +
|
|
638
|
+
expiresAt: new Date(now.getTime() + this.sessionMaxAgeSeconds * 1e3),
|
|
617
639
|
createdAt: now
|
|
618
640
|
};
|
|
619
641
|
}
|
|
@@ -650,7 +672,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
650
672
|
return {
|
|
651
673
|
id: newSessionId,
|
|
652
674
|
userId: user.id,
|
|
653
|
-
expiresAt: new Date(now.getTime() +
|
|
675
|
+
expiresAt: new Date(now.getTime() + this.sessionMaxAgeSeconds * 1e3),
|
|
654
676
|
createdAt: now
|
|
655
677
|
};
|
|
656
678
|
} catch (error) {
|
|
@@ -673,7 +695,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
673
695
|
"HttpOnly",
|
|
674
696
|
"SameSite=Lax",
|
|
675
697
|
"Path=/",
|
|
676
|
-
|
|
698
|
+
`Max-Age=${this.sessionMaxAgeSeconds}`
|
|
677
699
|
];
|
|
678
700
|
if (this.useProductionCookies && this.cookieDomain) {
|
|
679
701
|
parts.push("Secure");
|
|
@@ -697,7 +719,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
697
719
|
}
|
|
698
720
|
async getCurrentUser(request) {
|
|
699
721
|
const sessionCookie = parseCookie(request.headers.get("Cookie"), COOKIE_NAME);
|
|
700
|
-
if (sessionCookie) return this.verifySessionCookie(sessionCookie);
|
|
722
|
+
if (sessionCookie) return this.verifySessionCookie(sessionCookie, request);
|
|
701
723
|
const authHeader = request.headers.get("Authorization");
|
|
702
724
|
if (authHeader?.startsWith("Bearer ")) return this.verifyBearerToken(authHeader.slice(7));
|
|
703
725
|
return null;
|
|
@@ -850,6 +872,57 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
850
872
|
}
|
|
851
873
|
}
|
|
852
874
|
/**
|
|
875
|
+
* Read every `Set-Cookie` header from a response, falling back to the
|
|
876
|
+
* single-value accessor on runtimes without `Headers.getSetCookie()`.
|
|
877
|
+
*/
|
|
878
|
+
readSetCookieHeaders(res) {
|
|
879
|
+
const headers = res.headers;
|
|
880
|
+
if (typeof headers.getSetCookie === "function") return headers.getSetCookie();
|
|
881
|
+
const single = res.headers.get("Set-Cookie");
|
|
882
|
+
return single ? [single] : [];
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* If the shared API rotated the sealed `wos-session` cookie on this
|
|
886
|
+
* response, stash the `Set-Cookie` header against the inbound request so
|
|
887
|
+
* the server middleware can forward it to the browser, and invalidate the
|
|
888
|
+
* short-TTL verification cache entry for the old cookie value. Returns the
|
|
889
|
+
* rotated sealed value, or null when the cookie was not rotated.
|
|
890
|
+
*/
|
|
891
|
+
captureRotatedCookie(res, oldSessionCookie, oldCacheKey, request) {
|
|
892
|
+
const setCookies = this.readSetCookieHeaders(res);
|
|
893
|
+
if (setCookies.length === 0) return null;
|
|
894
|
+
const rotatedCookieHeader = setCookies.find((h) => parseCookieFromHeader(h, COOKIE_NAME) !== null);
|
|
895
|
+
if (!rotatedCookieHeader) return null;
|
|
896
|
+
const rotatedValue = parseCookieFromHeader(rotatedCookieHeader, COOKIE_NAME);
|
|
897
|
+
if (!rotatedValue || rotatedValue === oldSessionCookie) return null;
|
|
898
|
+
const reissued = this.getSessionHeaders({
|
|
899
|
+
id: rotatedValue,
|
|
900
|
+
userId: "",
|
|
901
|
+
expiresAt: /* @__PURE__ */ new Date(),
|
|
902
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
903
|
+
});
|
|
904
|
+
this.verifiedCredentials.delete(oldCacheKey);
|
|
905
|
+
if (request) {
|
|
906
|
+
const rawRequest = getWebRequest(request);
|
|
907
|
+
if (rawRequest) this.pendingResponseHeaders.set(rawRequest, reissued);
|
|
908
|
+
else this.logger.warn("captureRotatedCookie: renewed session cookie cannot be forwarded (no web Request)");
|
|
909
|
+
}
|
|
910
|
+
return rotatedValue;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Server middleware hook: emit any response headers the provider
|
|
914
|
+
* accumulated for this request (currently a rotated `Set-Cookie`) and
|
|
915
|
+
* clear them from the provider's per-request state.
|
|
916
|
+
*/
|
|
917
|
+
consumePendingResponseHeaders(request) {
|
|
918
|
+
const rawRequest = getWebRequest(request);
|
|
919
|
+
if (!rawRequest) return void 0;
|
|
920
|
+
const headers = this.pendingResponseHeaders.get(rawRequest);
|
|
921
|
+
if (!headers) return void 0;
|
|
922
|
+
this.pendingResponseHeaders.delete(rawRequest);
|
|
923
|
+
return headers;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
853
926
|
* Fetch the shared API's `/auth/me` and return the raw response body, or
|
|
854
927
|
* `null` on any non-OK / network error. Split out so `ensureOrganization`
|
|
855
928
|
* and `isOrganizationAdmin` can reuse it without duplicating the shape.
|
|
@@ -870,7 +943,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
870
943
|
* Forward a sealed session cookie to the shared API's /auth/me endpoint
|
|
871
944
|
* to validate it and get user info.
|
|
872
945
|
*/
|
|
873
|
-
async verifySessionCookie(sessionCookie) {
|
|
946
|
+
async verifySessionCookie(sessionCookie, request) {
|
|
874
947
|
const cacheKey = await this.verificationKey("cookie", sessionCookie);
|
|
875
948
|
const cached = this.getCachedVerification(cacheKey);
|
|
876
949
|
if (cached) {
|
|
@@ -882,6 +955,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
882
955
|
headers: { Cookie: `${COOKIE_NAME}=${sessionCookie}` },
|
|
883
956
|
signal: AbortSignal.timeout(VERIFY_FETCH_TIMEOUT_MS)
|
|
884
957
|
});
|
|
958
|
+
const rotated = this.captureRotatedCookie(res, sessionCookie, cacheKey, request);
|
|
885
959
|
if (!res.ok) {
|
|
886
960
|
this.logger.warn("verifySessionCookie: shared API returned non-OK status", {
|
|
887
961
|
status: res.status,
|
|
@@ -891,7 +965,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
891
965
|
return null;
|
|
892
966
|
}
|
|
893
967
|
const data = await res.json();
|
|
894
|
-
this.rememberUserSession(data.user.id, sessionCookie);
|
|
968
|
+
this.rememberUserSession(data.user.id, rotated ?? sessionCookie);
|
|
895
969
|
const sessionUser = {
|
|
896
970
|
id: data.user.id,
|
|
897
971
|
email: data.user.email,
|
|
@@ -903,7 +977,7 @@ var MastraAuthStudio = class extends MastraAuthProvider {
|
|
|
903
977
|
memberOrgIds: data.memberOrgIds
|
|
904
978
|
};
|
|
905
979
|
const user = await this.asPinnedOrganizationMember(sessionUser, sessionCookie);
|
|
906
|
-
if (user.organizationId) this.cacheVerification(cacheKey, user);
|
|
980
|
+
if (user.organizationId && !rotated) this.cacheVerification(cacheKey, user);
|
|
907
981
|
return user;
|
|
908
982
|
} catch (error) {
|
|
909
983
|
this.logger.error("verifySessionCookie: fetch to shared API failed", {
|
|
@@ -975,7 +1049,7 @@ function isAdminRole(role) {
|
|
|
975
1049
|
}
|
|
976
1050
|
/**
|
|
977
1051
|
* Parse a cookie value from a Set-Cookie header.
|
|
978
|
-
* Set-Cookie format: "name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=
|
|
1052
|
+
* Set-Cookie format: "name=value; HttpOnly; SameSite=Lax; Path=/; Max-Age=1209600"
|
|
979
1053
|
*/
|
|
980
1054
|
function parseCookieFromHeader(setCookieHeader, name) {
|
|
981
1055
|
const parts = setCookieHeader.split(";");
|