@mastra/auth-neon 0.3.0-alpha.0 → 0.3.1-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/index.cjs CHANGED
@@ -1,373 +1,414 @@
1
- 'use strict';
2
-
3
- var server = require('@mastra/core/server');
4
- var jose = require('jose');
5
- var ee = require('@mastra/core/auth/ee');
6
-
7
- // src/index.ts
8
- var DEFAULT_CACHE_TTL_MS = 6e4;
9
- var DEFAULT_CACHE_MAX_SIZE = 1e3;
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _mastra_core_auth = require("@mastra/core/auth");
3
+ let _mastra_core_server = require("@mastra/core/server");
4
+ let jose = require("jose");
5
+ let _mastra_core_auth_ee = require("@mastra/core/auth/ee");
6
+ //#region src/rbac-provider.ts
7
+ const DEFAULT_CACHE_TTL_MS = 6e4;
8
+ const DEFAULT_CACHE_MAX_SIZE = 1e3;
9
+ /**
10
+ * RBAC provider for Neon Auth that maps organization roles to Mastra permissions.
11
+ *
12
+ * Neon Auth uses Better Auth's Organization plugin which provides `owner`,
13
+ * `admin`, and `member` roles. This provider maps those roles to Mastra
14
+ * permission patterns via a configurable `roleMapping`.
15
+ *
16
+ * @example Static mapping
17
+ * ```typescript
18
+ * import { MastraRBACNeon } from '@mastra/auth-neon';
19
+ *
20
+ * const rbac = new MastraRBACNeon({
21
+ * roleMapping: {
22
+ * owner: ['*'],
23
+ * admin: ['*'],
24
+ * member: ['agents:read', 'workflows:*'],
25
+ * _default: [],
26
+ * },
27
+ * });
28
+ * ```
29
+ *
30
+ * @example With custom role extraction from JWT claims
31
+ * ```typescript
32
+ * const rbac = new MastraRBACNeon({
33
+ * roleMapping: {
34
+ * admin: ['*'],
35
+ * member: ['agents:read'],
36
+ * },
37
+ * getUserRoles: (user) => {
38
+ * const role = user.jwt?.role as string;
39
+ * return role ? [role] : [];
40
+ * },
41
+ * });
42
+ * ```
43
+ */
10
44
  var MastraRBACNeon = class {
11
- options;
12
- baseUrl;
13
- rolesCache = /* @__PURE__ */ new Map();
14
- cacheTtlMs;
15
- cacheMaxSize;
16
- get roleMapping() {
17
- return this.options.roleMapping;
18
- }
19
- constructor(options) {
20
- this.options = options;
21
- const rawUrl = options.baseUrl ?? process.env.NEON_AUTH_BASE_URL ?? "";
22
- let end = rawUrl.length;
23
- while (end > 0 && rawUrl[end - 1] === "/") {
24
- end--;
25
- }
26
- this.baseUrl = rawUrl.slice(0, end);
27
- this.cacheTtlMs = options.cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
28
- this.cacheMaxSize = options.cache?.maxSize ?? DEFAULT_CACHE_MAX_SIZE;
29
- }
30
- async getRoles(user) {
31
- if (this.options.getUserRoles) {
32
- return this.options.getUserRoles(user);
33
- }
34
- if (user.jwt) {
35
- const role = user.jwt.role;
36
- if (role) return [role];
37
- }
38
- const userId = user.user?.id;
39
- if (!userId) return [];
40
- const cached = this.rolesCache.get(userId);
41
- if (cached && cached.expiresAt > Date.now()) {
42
- this.rolesCache.delete(userId);
43
- this.rolesCache.set(userId, cached);
44
- return cached.roles;
45
- }
46
- const roles = await this.fetchRolesFromNeonAuth(userId);
47
- if (this.rolesCache.size >= this.cacheMaxSize) {
48
- const firstKey = this.rolesCache.keys().next().value;
49
- if (firstKey) this.rolesCache.delete(firstKey);
50
- }
51
- this.rolesCache.set(userId, { roles, expiresAt: Date.now() + this.cacheTtlMs });
52
- return roles;
53
- }
54
- async hasRole(user, role) {
55
- const roles = await this.getRoles(user);
56
- return roles.includes(role);
57
- }
58
- async getPermissions(user) {
59
- const roles = await this.getRoles(user);
60
- return ee.resolvePermissionsFromMapping(roles, this.options.roleMapping);
61
- }
62
- async hasPermission(user, permission) {
63
- const permissions = await this.getPermissions(user);
64
- return permissions.some((p) => ee.matchesPermission(p, permission));
65
- }
66
- async hasAllPermissions(user, permissions) {
67
- const userPermissions = await this.getPermissions(user);
68
- return permissions.every((required) => userPermissions.some((p) => ee.matchesPermission(p, required)));
69
- }
70
- async hasAnyPermission(user, permissions) {
71
- const userPermissions = await this.getPermissions(user);
72
- return permissions.some((required) => userPermissions.some((p) => ee.matchesPermission(p, required)));
73
- }
74
- async getAvailableRoles() {
75
- return Object.keys(this.options.roleMapping).filter((k) => k !== "_default").map((k) => ({ id: k, name: k.charAt(0).toUpperCase() + k.slice(1) }));
76
- }
77
- async getRolePermissions(roleId) {
78
- return ee.resolvePermissionsFromMapping([roleId], this.options.roleMapping);
79
- }
80
- /**
81
- * Fetch organization membership roles from Neon Auth.
82
- *
83
- * Uses Better Auth's `organization/list-memberships` admin API.
84
- * Falls back to empty roles on error (default permissions will apply).
85
- */
86
- async fetchRolesFromNeonAuth(userId) {
87
- if (!this.baseUrl) return [];
88
- try {
89
- const response = await fetch(`${this.baseUrl}/auth/api/organization/list-memberships`, {
90
- method: "POST",
91
- headers: { "Content-Type": "application/json" },
92
- body: JSON.stringify({ userId })
93
- });
94
- if (!response.ok) return [];
95
- const data = await response.json();
96
- const memberships = Array.isArray(data) ? data : data.data ?? [];
97
- const relevant = this.options.organizationId ? memberships.filter((m) => m.organizationId === this.options.organizationId) : memberships;
98
- return relevant.map((m) => m.role);
99
- } catch {
100
- return [];
101
- }
102
- }
45
+ options;
46
+ baseUrl;
47
+ rolesCache = /* @__PURE__ */ new Map();
48
+ cacheTtlMs;
49
+ cacheMaxSize;
50
+ get roleMapping() {
51
+ return this.options.roleMapping;
52
+ }
53
+ constructor(options) {
54
+ this.options = options;
55
+ const rawUrl = options.baseUrl ?? process.env.NEON_AUTH_BASE_URL ?? "";
56
+ let end = rawUrl.length;
57
+ while (end > 0 && rawUrl[end - 1] === "/") end--;
58
+ this.baseUrl = rawUrl.slice(0, end);
59
+ this.cacheTtlMs = options.cache?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
60
+ this.cacheMaxSize = options.cache?.maxSize ?? DEFAULT_CACHE_MAX_SIZE;
61
+ }
62
+ async getRoles(user) {
63
+ if (this.options.getUserRoles) return this.options.getUserRoles(user);
64
+ if (user.jwt) {
65
+ const role = user.jwt.role;
66
+ if (role) return [role];
67
+ }
68
+ const userId = user.user?.id;
69
+ if (!userId) return [];
70
+ const cached = this.rolesCache.get(userId);
71
+ if (cached && cached.expiresAt > Date.now()) {
72
+ this.rolesCache.delete(userId);
73
+ this.rolesCache.set(userId, cached);
74
+ return cached.roles;
75
+ }
76
+ const roles = await this.fetchRolesFromNeonAuth(userId);
77
+ if (this.rolesCache.size >= this.cacheMaxSize) {
78
+ const firstKey = this.rolesCache.keys().next().value;
79
+ if (firstKey) this.rolesCache.delete(firstKey);
80
+ }
81
+ this.rolesCache.set(userId, {
82
+ roles,
83
+ expiresAt: Date.now() + this.cacheTtlMs
84
+ });
85
+ return roles;
86
+ }
87
+ async hasRole(user, role) {
88
+ return (await this.getRoles(user)).includes(role);
89
+ }
90
+ async getPermissions(user) {
91
+ return (0, _mastra_core_auth_ee.resolvePermissionsFromMapping)(await this.getRoles(user), this.options.roleMapping);
92
+ }
93
+ async hasPermission(user, permission) {
94
+ return (await this.getPermissions(user)).some((p) => (0, _mastra_core_auth_ee.matchesPermission)(p, permission));
95
+ }
96
+ async hasAllPermissions(user, permissions) {
97
+ const userPermissions = await this.getPermissions(user);
98
+ return permissions.every((required) => userPermissions.some((p) => (0, _mastra_core_auth_ee.matchesPermission)(p, required)));
99
+ }
100
+ async hasAnyPermission(user, permissions) {
101
+ const userPermissions = await this.getPermissions(user);
102
+ return permissions.some((required) => userPermissions.some((p) => (0, _mastra_core_auth_ee.matchesPermission)(p, required)));
103
+ }
104
+ async getAvailableRoles() {
105
+ return Object.keys(this.options.roleMapping).filter((k) => k !== "_default").map((k) => ({
106
+ id: k,
107
+ name: k.charAt(0).toUpperCase() + k.slice(1)
108
+ }));
109
+ }
110
+ async getRolePermissions(roleId) {
111
+ return (0, _mastra_core_auth_ee.resolvePermissionsFromMapping)([roleId], this.options.roleMapping);
112
+ }
113
+ /**
114
+ * Fetch organization membership roles from Neon Auth.
115
+ *
116
+ * Uses Better Auth's `organization/list-memberships` admin API.
117
+ * Falls back to empty roles on error (default permissions will apply).
118
+ */
119
+ async fetchRolesFromNeonAuth(userId) {
120
+ if (!this.baseUrl) return [];
121
+ try {
122
+ const response = await fetch(`${this.baseUrl}/auth/api/organization/list-memberships`, {
123
+ method: "POST",
124
+ headers: { "Content-Type": "application/json" },
125
+ body: JSON.stringify({ userId })
126
+ });
127
+ if (!response.ok) return [];
128
+ const data = await response.json();
129
+ const memberships = Array.isArray(data) ? data : data.data ?? [];
130
+ return (this.options.organizationId ? memberships.filter((m) => m.organizationId === this.options.organizationId) : memberships).map((m) => m.role);
131
+ } catch {
132
+ return [];
133
+ }
134
+ }
103
135
  };
104
-
105
- // src/index.ts
106
- function getRequestHeader(request, name) {
107
- if (request instanceof Request) {
108
- return request.headers.get(name);
109
- }
110
- return request.raw?.headers.get(name) ?? request.headers?.get(name) ?? request.header(name) ?? null;
111
- }
136
+ //#endregion
137
+ //#region src/index.ts
112
138
  function stripTrailingSlashes(url) {
113
- let end = url.length;
114
- while (end > 0 && url[end - 1] === "/") {
115
- end--;
116
- }
117
- return url.slice(0, end);
139
+ let end = url.length;
140
+ while (end > 0 && url[end - 1] === "/") end--;
141
+ return url.slice(0, end);
118
142
  }
119
143
  function parseCookies(response) {
120
- if (typeof response.headers.getSetCookie === "function") {
121
- return response.headers.getSetCookie();
122
- }
123
- const raw = response.headers.get("set-cookie");
124
- if (!raw) return [];
125
- return raw.split(/,(?=\s*\w+=)/);
144
+ if (typeof response.headers.getSetCookie === "function") return response.headers.getSetCookie();
145
+ const raw = response.headers.get("set-cookie");
146
+ if (!raw) return [];
147
+ return raw.split(/,(?=\s*\w+=)/);
126
148
  }
127
149
  function mapNeonUserToEEUser(user) {
128
- return {
129
- id: user.id,
130
- email: user.email,
131
- name: user.name,
132
- avatarUrl: user.image ?? void 0,
133
- metadata: {
134
- emailVerified: user.emailVerified,
135
- createdAt: user.createdAt,
136
- updatedAt: user.updatedAt
137
- }
138
- };
150
+ return {
151
+ id: user.id,
152
+ email: user.email,
153
+ name: user.name,
154
+ avatarUrl: user.image ?? void 0,
155
+ metadata: {
156
+ emailVerified: user.emailVerified,
157
+ createdAt: user.createdAt,
158
+ updatedAt: user.updatedAt
159
+ }
160
+ };
139
161
  }
140
- var MastraAuthNeon = class extends server.MastraAuthProvider {
141
- baseUrl;
142
- jwksUrl;
143
- sessionCookieName;
144
- signUpEnabledConfig;
145
- constructor(options) {
146
- super({ name: options?.name ?? "neon" });
147
- const baseUrl = options?.baseUrl ?? process.env.NEON_AUTH_BASE_URL;
148
- if (!baseUrl) {
149
- throw new Error(
150
- "Neon Auth base URL is required, please provide it in the options or set the NEON_AUTH_BASE_URL environment variable"
151
- );
152
- }
153
- this.baseUrl = stripTrailingSlashes(baseUrl);
154
- this.jwksUrl = options?.jwksUrl ?? process.env.NEON_AUTH_JWKS_URL ?? `${this.baseUrl}/auth/jwks`;
155
- this.sessionCookieName = options?.sessionCookieName ?? "neonauth.session_token";
156
- this.signUpEnabledConfig = options?.signUpEnabled ?? true;
157
- this.registerOptions(options);
158
- }
159
- /** Expose the base URL for RBAC or other consumers. */
160
- getBaseUrl() {
161
- return this.baseUrl;
162
- }
163
- isSignUpEnabled() {
164
- return this.signUpEnabledConfig;
165
- }
166
- // ── IUserProvider ──
167
- async getCurrentUser(request) {
168
- try {
169
- const result = await this.fetchSession(request.headers);
170
- if (!result?.user) return null;
171
- return mapNeonUserToEEUser(result.user);
172
- } catch {
173
- return null;
174
- }
175
- }
176
- async getUser(_userId) {
177
- return null;
178
- }
179
- getUserProfileUrl(user) {
180
- return `/profile/${user.id}`;
181
- }
182
- // ── MastraAuthProvider ──
183
- async authenticateToken(token, request) {
184
- if (!token || typeof token !== "string") {
185
- return null;
186
- }
187
- const jwtResult = await this.verifyJwt(token);
188
- if (jwtResult) {
189
- return {
190
- user: {
191
- id: jwtResult.sub ?? "",
192
- email: jwtResult.email ?? "",
193
- name: jwtResult.name ?? "",
194
- image: jwtResult.picture ?? null,
195
- emailVerified: jwtResult.email_verified ?? false,
196
- createdAt: jwtResult.iat ? new Date(jwtResult.iat * 1e3).toISOString() : "",
197
- updatedAt: ""
198
- },
199
- jwt: jwtResult
200
- };
201
- }
202
- try {
203
- const headers = new Headers();
204
- const cookieHeader = getRequestHeader(request, "Cookie");
205
- if (cookieHeader) {
206
- headers.set("Cookie", cookieHeader);
207
- }
208
- const hasSessionCookie = !!cookieHeader?.split(";").some((pair) => pair.trim().split("=")[0]?.trim() === this.sessionCookieName);
209
- if (token && !hasSessionCookie) {
210
- const existingCookies = cookieHeader ? `${cookieHeader}; ` : "";
211
- headers.set("Cookie", `${existingCookies}${this.sessionCookieName}=${token}`);
212
- }
213
- const result = await this.fetchSession(headers);
214
- if (!result?.session || !result?.user) {
215
- return null;
216
- }
217
- return {
218
- session: result.session,
219
- user: result.user
220
- };
221
- } catch {
222
- return null;
223
- }
224
- }
225
- async authorizeUser(user) {
226
- if (!user?.user?.id) return false;
227
- if (user.jwt?.exp && user.jwt.exp * 1e3 < Date.now()) {
228
- return false;
229
- }
230
- return true;
231
- }
232
- // ── ICredentialsProvider ──
233
- async signIn(email, password, request) {
234
- const response = await fetch(`${this.baseUrl}/auth/sign-in/email`, {
235
- method: "POST",
236
- headers: {
237
- "Content-Type": "application/json",
238
- ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
239
- },
240
- body: JSON.stringify({ email, password })
241
- });
242
- if (!response.ok) {
243
- const errorData = await response.json().catch(() => ({}));
244
- throw new Error(errorData.message || "Invalid email or password");
245
- }
246
- const result = await response.json();
247
- if (!result?.user) {
248
- throw new Error("Invalid email or password");
249
- }
250
- const cookies = parseCookies(response);
251
- return {
252
- user: mapNeonUserToEEUser(result.user),
253
- token: result.token ?? void 0,
254
- cookies
255
- };
256
- }
257
- async signUp(email, password, name, request) {
258
- const displayName = name ?? email.split("@")[0] ?? "User";
259
- const response = await fetch(`${this.baseUrl}/auth/sign-up/email`, {
260
- method: "POST",
261
- headers: {
262
- "Content-Type": "application/json",
263
- ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
264
- },
265
- body: JSON.stringify({ email, password, name: displayName })
266
- });
267
- if (!response.ok) {
268
- const errorData = await response.json().catch(() => ({}));
269
- throw new Error(errorData.message || "Failed to create account");
270
- }
271
- const result = await response.json();
272
- if (!result?.user) {
273
- throw new Error("Failed to create account");
274
- }
275
- const cookies = parseCookies(response);
276
- return {
277
- user: mapNeonUserToEEUser(result.user),
278
- token: result.token ?? void 0,
279
- cookies
280
- };
281
- }
282
- // ── ISessionProvider ──
283
- async createSession(_userId, metadata) {
284
- const now = /* @__PURE__ */ new Date();
285
- const expiresAt = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1e3);
286
- return {
287
- id: crypto.randomUUID(),
288
- userId: _userId,
289
- createdAt: now,
290
- expiresAt,
291
- metadata
292
- };
293
- }
294
- async validateSession(sessionId) {
295
- try {
296
- const headers = new Headers();
297
- headers.set("Cookie", `${this.sessionCookieName}=${sessionId}`);
298
- const result = await this.fetchSession(headers);
299
- if (!result?.session) return null;
300
- return {
301
- id: result.session.id,
302
- userId: result.session.userId,
303
- expiresAt: new Date(result.session.expiresAt),
304
- createdAt: new Date(result.session.createdAt)
305
- };
306
- } catch {
307
- return null;
308
- }
309
- }
310
- async destroySession(_sessionId) {
311
- }
312
- async refreshSession(sessionId) {
313
- return this.validateSession(sessionId);
314
- }
315
- getSessionIdFromRequest(request) {
316
- const cookieHeader = request.headers.get("Cookie");
317
- if (!cookieHeader) return null;
318
- for (const pair of cookieHeader.split(";")) {
319
- const [key, ...rest] = pair.trim().split("=");
320
- if (key?.trim() === this.sessionCookieName) {
321
- return rest.join("=") || null;
322
- }
323
- }
324
- return null;
325
- }
326
- getSessionHeaders(session) {
327
- const cookie = session._sessionCookie;
328
- if (typeof cookie === "string") {
329
- return { "Set-Cookie": cookie };
330
- }
331
- return {};
332
- }
333
- getClearSessionHeaders() {
334
- const cookies = [
335
- `${this.sessionCookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`,
336
- `${this.sessionCookieName}_sig=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`
337
- ];
338
- return {
339
- "Set-Cookie": cookies.join(", ")
340
- };
341
- }
342
- // ── Internal helpers ──
343
- async verifyJwt(token) {
344
- try {
345
- const JWKS = jose.createRemoteJWKSet(new URL(this.jwksUrl));
346
- const { payload } = await jose.jwtVerify(token, JWKS);
347
- return payload;
348
- } catch {
349
- return null;
350
- }
351
- }
352
- /** Fetch and validate a session from the Neon Auth REST API. */
353
- async fetchSession(headers) {
354
- const response = await fetch(`${this.baseUrl}/auth/get-session`, {
355
- method: "GET",
356
- headers
357
- });
358
- if (!response.ok) {
359
- return null;
360
- }
361
- const data = await response.json();
362
- if (!data?.session || !data?.user) {
363
- return null;
364
- }
365
- return data;
366
- }
162
+ /**
163
+ * Mastra authentication provider for Neon Auth.
164
+ *
165
+ * Neon Auth is a managed authentication service built on Better Auth
166
+ * that stores users, sessions, and auth configuration directly in your
167
+ * Neon Postgres database.
168
+ *
169
+ * This adapter supports:
170
+ * - JWT bearer token verification via JWKS (for API clients)
171
+ * - Session cookie verification via Neon Auth REST API (for Studio)
172
+ * - Email/password sign-in and sign-up (for Studio credentials flow)
173
+ * - Session management (validate, refresh, destroy)
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * import { MastraAuthNeon } from '@mastra/auth-neon';
178
+ *
179
+ * const auth = new MastraAuthNeon({
180
+ * baseUrl: process.env.NEON_AUTH_BASE_URL,
181
+ * });
182
+ *
183
+ * const mastra = new Mastra({
184
+ * server: {
185
+ * auth,
186
+ * },
187
+ * });
188
+ * ```
189
+ *
190
+ * @example With RBAC
191
+ * ```typescript
192
+ * import { MastraAuthNeon, MastraRBACNeon } from '@mastra/auth-neon';
193
+ *
194
+ * const mastra = new Mastra({
195
+ * server: {
196
+ * auth: new MastraAuthNeon({
197
+ * baseUrl: process.env.NEON_AUTH_BASE_URL,
198
+ * }),
199
+ * rbac: new MastraRBACNeon({
200
+ * roleMapping: {
201
+ * owner: ['*'],
202
+ * admin: ['*'],
203
+ * member: ['agents:read', 'workflows:*'],
204
+ * _default: [],
205
+ * },
206
+ * }),
207
+ * },
208
+ * });
209
+ * ```
210
+ *
211
+ * @see https://neon.com/docs/auth/overview
212
+ */
213
+ var MastraAuthNeon = class extends _mastra_core_server.MastraAuthProvider {
214
+ baseUrl;
215
+ jwksUrl;
216
+ sessionCookieName;
217
+ signUpEnabledConfig;
218
+ constructor(options) {
219
+ super({ name: options?.name ?? "neon" });
220
+ const baseUrl = options?.baseUrl ?? process.env.NEON_AUTH_BASE_URL;
221
+ if (!baseUrl) throw new Error("Neon Auth base URL is required, please provide it in the options or set the NEON_AUTH_BASE_URL environment variable");
222
+ this.baseUrl = stripTrailingSlashes(baseUrl);
223
+ this.jwksUrl = options?.jwksUrl ?? process.env.NEON_AUTH_JWKS_URL ?? `${this.baseUrl}/auth/jwks`;
224
+ this.sessionCookieName = options?.sessionCookieName ?? "neonauth.session_token";
225
+ this.signUpEnabledConfig = options?.signUpEnabled ?? true;
226
+ this.registerOptions(options);
227
+ }
228
+ /** Expose the base URL for RBAC or other consumers. */
229
+ getBaseUrl() {
230
+ return this.baseUrl;
231
+ }
232
+ isSignUpEnabled() {
233
+ return this.signUpEnabledConfig;
234
+ }
235
+ async getCurrentUser(request) {
236
+ try {
237
+ const result = await this.fetchSession(request.headers);
238
+ if (!result?.user) return null;
239
+ return mapNeonUserToEEUser(result.user);
240
+ } catch {
241
+ return null;
242
+ }
243
+ }
244
+ async getUser(_userId) {
245
+ return null;
246
+ }
247
+ getUserProfileUrl(user) {
248
+ return `/profile/${user.id}`;
249
+ }
250
+ async authenticateToken(token, request) {
251
+ if (!token || typeof token !== "string") return null;
252
+ const jwtResult = await this.verifyJwt(token);
253
+ if (jwtResult) return {
254
+ user: {
255
+ id: jwtResult.sub ?? "",
256
+ email: jwtResult.email ?? "",
257
+ name: jwtResult.name ?? "",
258
+ image: jwtResult.picture ?? null,
259
+ emailVerified: jwtResult.email_verified ?? false,
260
+ createdAt: jwtResult.iat ? (/* @__PURE__ */ new Date(jwtResult.iat * 1e3)).toISOString() : "",
261
+ updatedAt: ""
262
+ },
263
+ jwt: jwtResult
264
+ };
265
+ try {
266
+ const headers = new Headers();
267
+ const cookieHeader = (0, _mastra_core_auth.getRequestHeader)(request, "Cookie");
268
+ if (cookieHeader) headers.set("Cookie", cookieHeader);
269
+ const hasSessionCookie = !!cookieHeader?.split(";").some((pair) => pair.trim().split("=")[0]?.trim() === this.sessionCookieName);
270
+ if (token && !hasSessionCookie) {
271
+ const existingCookies = cookieHeader ? `${cookieHeader}; ` : "";
272
+ headers.set("Cookie", `${existingCookies}${this.sessionCookieName}=${token}`);
273
+ }
274
+ const result = await this.fetchSession(headers);
275
+ if (!result?.session || !result?.user) return null;
276
+ return {
277
+ session: result.session,
278
+ user: result.user
279
+ };
280
+ } catch {
281
+ return null;
282
+ }
283
+ }
284
+ async authorizeUser(user) {
285
+ if (!user?.user?.id) return false;
286
+ if (user.jwt?.exp && user.jwt.exp * 1e3 < Date.now()) return false;
287
+ return true;
288
+ }
289
+ async signIn(email, password, request) {
290
+ const response = await fetch(`${this.baseUrl}/auth/sign-in/email`, {
291
+ method: "POST",
292
+ headers: {
293
+ "Content-Type": "application/json",
294
+ ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
295
+ },
296
+ body: JSON.stringify({
297
+ email,
298
+ password
299
+ })
300
+ });
301
+ if (!response.ok) {
302
+ const errorData = await response.json().catch(() => ({}));
303
+ throw new Error(errorData.message || "Invalid email or password");
304
+ }
305
+ const result = await response.json();
306
+ if (!result?.user) throw new Error("Invalid email or password");
307
+ const cookies = parseCookies(response);
308
+ return {
309
+ user: mapNeonUserToEEUser(result.user),
310
+ token: result.token ?? void 0,
311
+ cookies
312
+ };
313
+ }
314
+ async signUp(email, password, name, request) {
315
+ const displayName = name ?? email.split("@")[0] ?? "User";
316
+ const response = await fetch(`${this.baseUrl}/auth/sign-up/email`, {
317
+ method: "POST",
318
+ headers: {
319
+ "Content-Type": "application/json",
320
+ ...request?.headers ? Object.fromEntries(request.headers.entries()) : {}
321
+ },
322
+ body: JSON.stringify({
323
+ email,
324
+ password,
325
+ name: displayName
326
+ })
327
+ });
328
+ if (!response.ok) {
329
+ const errorData = await response.json().catch(() => ({}));
330
+ throw new Error(errorData.message || "Failed to create account");
331
+ }
332
+ const result = await response.json();
333
+ if (!result?.user) throw new Error("Failed to create account");
334
+ const cookies = parseCookies(response);
335
+ return {
336
+ user: mapNeonUserToEEUser(result.user),
337
+ token: result.token ?? void 0,
338
+ cookies
339
+ };
340
+ }
341
+ async createSession(_userId, metadata) {
342
+ const now = /* @__PURE__ */ new Date();
343
+ const expiresAt = new Date(now.getTime() + 10080 * 60 * 1e3);
344
+ return {
345
+ id: crypto.randomUUID(),
346
+ userId: _userId,
347
+ createdAt: now,
348
+ expiresAt,
349
+ metadata
350
+ };
351
+ }
352
+ async validateSession(sessionId) {
353
+ try {
354
+ const headers = new Headers();
355
+ headers.set("Cookie", `${this.sessionCookieName}=${sessionId}`);
356
+ const result = await this.fetchSession(headers);
357
+ if (!result?.session) return null;
358
+ return {
359
+ id: result.session.id,
360
+ userId: result.session.userId,
361
+ expiresAt: new Date(result.session.expiresAt),
362
+ createdAt: new Date(result.session.createdAt)
363
+ };
364
+ } catch {
365
+ return null;
366
+ }
367
+ }
368
+ async destroySession(_sessionId) {}
369
+ async refreshSession(sessionId) {
370
+ return this.validateSession(sessionId);
371
+ }
372
+ getSessionIdFromRequest(request) {
373
+ const cookieHeader = request.headers.get("Cookie");
374
+ if (!cookieHeader) return null;
375
+ for (const pair of cookieHeader.split(";")) {
376
+ const [key, ...rest] = pair.trim().split("=");
377
+ if (key?.trim() === this.sessionCookieName) return rest.join("=") || null;
378
+ }
379
+ return null;
380
+ }
381
+ getSessionHeaders(session) {
382
+ const cookie = session._sessionCookie;
383
+ if (typeof cookie === "string") return { "Set-Cookie": cookie };
384
+ return {};
385
+ }
386
+ getClearSessionHeaders() {
387
+ return { "Set-Cookie": [`${this.sessionCookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`, `${this.sessionCookieName}_sig=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`].join(", ") };
388
+ }
389
+ async verifyJwt(token) {
390
+ try {
391
+ const { payload } = await (0, jose.jwtVerify)(token, (0, jose.createRemoteJWKSet)(new URL(this.jwksUrl)));
392
+ return payload;
393
+ } catch {
394
+ return null;
395
+ }
396
+ }
397
+ /** Fetch and validate a session from the Neon Auth REST API. */
398
+ async fetchSession(headers) {
399
+ const response = await fetch(`${this.baseUrl}/auth/get-session`, {
400
+ method: "GET",
401
+ headers
402
+ });
403
+ if (!response.ok) return null;
404
+ const data = await response.json();
405
+ if (!data?.session || !data?.user) return null;
406
+ return data;
407
+ }
367
408
  };
368
-
409
+ //#endregion
369
410
  exports.MastraAuthNeon = MastraAuthNeon;
370
411
  exports.MastraRBACNeon = MastraRBACNeon;
371
412
  exports.mapNeonUserToEEUser = mapNeonUserToEEUser;
372
- //# sourceMappingURL=index.cjs.map
413
+
373
414
  //# sourceMappingURL=index.cjs.map